Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Ostream log exporter and tests #430

Merged
merged 8 commits into from
Dec 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions exporters/ostream/BUILD
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
package(default_visibility = ["//visibility:public"])

cc_library(
name = "ostream_log_exporter",
srcs = [
"src/log_exporter.cc",
],
hdrs = [
"include/opentelemetry/exporters/ostream/log_exporter.h",
],
strip_include_prefix = "include",
deps = [
"//sdk/src/logs",
],
)

cc_test(
name = "ostream_log_test",
srcs = ["test/ostream_log_test.cc"],
deps = [
":ostream_log_exporter",
"@com_google_googletest//:gtest_main",
],
)

cc_library(
name = "ostream_metrics_exporter",
srcs = [
Expand Down
13 changes: 13 additions & 0 deletions exporters/ostream/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
include_directories(include)

add_library(opentelemetry_exporter_ostream_logs src/log_exporter.cc)
add_library(opentelemetry_exporter_ostream_metrics src/metrics_exporter.cc)
add_library(opentelemetry_exporter_ostream_span src/span_exporter.cc)

if(BUILD_TESTING)
add_executable(ostream_metrics_test test/ostream_metrics_test.cc)
add_executable(ostream_span_test test/ostream_span_test.cc)
add_executable(ostream_log_test test/ostream_log_test.cc)

target_link_libraries(
ostream_span_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
Expand All @@ -15,12 +17,23 @@ if(BUILD_TESTING)
ostream_metrics_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
opentelemetry_exporter_ostream_metrics)

target_link_libraries(
ostream_log_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
opentelemetry_exporter_ostream_logs opentelemetry_logs)

gtest_add_tests(
TARGET ostream_log_test
TEST_PREFIX exporter.
TEST_LIST ostream_log_test)

gtest_add_tests(
TARGET ostream_metrics_test
TEST_PREFIX exporter.
TEST_LIST ostream_metrics_test)

gtest_add_tests(
TARGET ostream_span_test
TEST_PREFIX exporter.
TEST_LIST ostream_span_test)

endif() # BUILD_TESTING
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include "opentelemetry/nostd/type_traits.h"
#include "opentelemetry/sdk/logs/exporter.h"
#include "opentelemetry/sdk/logs/log_record.h"
#include "opentelemetry/version.h"

#include <iostream>
#include <sstream>

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
{
namespace logs
{
/**
* The OStreamLogExporter exports logs through an ostream (default set to std::cout)
*/
class OStreamLogExporter final : public opentelemetry::sdk::logs::LogExporter
{
public:
/**
* Create an OStreamLogExporter. This constructor takes in a reference to an ostream that the
* Export() method will send log data into. The default ostream is set to stdout.
*/
explicit OStreamLogExporter(std::ostream &sout = std::cout) noexcept;

std::unique_ptr<sdk::logs::Recordable> MakeRecordable() noexcept override;

/**
* Exports a span of logs sent from the processor.
*/
opentelemetry::sdk::logs::ExportResult Export(
const opentelemetry::nostd::span<std::unique_ptr<sdk::logs::Recordable>>
&records) noexcept override;

/**
* Marks the OStream Log Exporter as shut down.
*/
bool Shutdown(
std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override;

private:
// The OStream to send the logs to
std::ostream &sout_;
// Whether this exporter has been shut down
bool is_shutdown_ = false;
};
} // namespace logs
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
189 changes: 189 additions & 0 deletions exporters/ostream/src/log_exporter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "opentelemetry/exporters/ostream/log_exporter.h"

#include <iostream>

namespace nostd = opentelemetry::nostd;
namespace sdklogs = opentelemetry::sdk::logs;

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
{
namespace logs
{
/*********************** Helper functions ************************/

/*
print_value is used to print out the value of an attribute within a vector.
These values are held in a variant which makes the process of printing them much more
complicated.
*/

template <typename T>
void print_value(const T &item, std::ostream &sout)
{
sout << item;
}

template <typename T>
void print_value(const std::vector<T> &vec, std::ostream &sout)
{
sout << '[';
size_t i = 1;
size_t sz = vec.size();
for (auto v : vec)
{
sout << v;
if (i != sz)
sout << ',' << ' ';
i++;
};
sout << ']';
}

// Prior to C++14, generic lambda is not available so fallback to functor.
#if __cplusplus < 201402L

class OwnedAttributeValueVisitor
{
public:
OwnedAttributeValueVisitor(std::ostream &sout) : sout_(sout) {}

template <typename T>
void operator()(T &&arg)
{
print_value(arg, sout_);
}

private:
// The OStream to send the logs to
std::ostream &sout_;
};

#endif

void print_value(sdk::common::OwnedAttributeValue &value, std::ostream &sout)
{
#if __cplusplus < 201402L
nostd::visit(OwnedAttributeValueVisitor(sout), value);
#else
nostd::visit([&sout](auto &&arg) { print_value(arg, sout); }, value);
#endif
}

void printMap(std::unordered_map<std::string, sdk::common::OwnedAttributeValue> map,
std::ostream &sout)
{
sout << "{";
size_t size = map.size();
size_t i = 1;
for (auto kv : map)
{
sout << "{" << kv.first << ": ";
print_value(kv.second, sout);
sout << "}";

if (i != size)
sout << ", ";
i++;
}
sout << "}";
}

/*********************** Constructor ***********************/

OStreamLogExporter::OStreamLogExporter(std::ostream &sout) noexcept : sout_(sout) {}

/*********************** Exporter methods ***********************/

std::unique_ptr<sdklogs::Recordable> OStreamLogExporter::MakeRecordable() noexcept
{
return std::unique_ptr<sdklogs::Recordable>(new sdklogs::LogRecord());
}

sdklogs::ExportResult OStreamLogExporter::Export(
const nostd::span<std::unique_ptr<sdklogs::Recordable>> &records) noexcept
{
if (is_shutdown_)
{
return sdklogs::ExportResult::kFailure;
}

for (auto &record : records)
{
// Convert recordable to a LogRecord so that the getters of the LogRecord can be used
auto log_record =
std::unique_ptr<sdklogs::LogRecord>(static_cast<sdklogs::LogRecord *>(record.release()));

if (log_record == nullptr)
{
// TODO: Log Internal SDK error "recordable data was lost"
continue;
}

// Convert trace, spanid, traceflags into exportable representation
constexpr int trace_id_len = 32;
constexpr int span_id__len = 16;
constexpr int trace_flags_len = 2;

char trace_id[trace_id_len] = {0};
char span_id[span_id__len] = {0};
char trace_flags[trace_flags_len] = {0};

log_record->GetTraceId().ToLowerBase16(trace_id);
log_record->GetSpanId().ToLowerBase16(span_id);
log_record->GetTraceFlags().ToLowerBase16(trace_flags);

// Print out each field of the log record, noting that severity is separated
// into severity_num and severity_text
sout_ << "{\n"
<< " timestamp : " << log_record->GetTimestamp().time_since_epoch().count() << "\n"
<< " severity_num : " << static_cast<int>(log_record->GetSeverity()) << "\n"
<< " severity_text : "
<< opentelemetry::logs::SeverityNumToText[static_cast<int>(log_record->GetSeverity())]
<< "\n"
<< " name : " << log_record->GetName() << "\n"
<< " body : " << log_record->GetBody() << "\n"
<< " resource : ";

printMap(log_record->GetResource(), sout_);

sout_ << "\n"
<< " attributes : ";

printMap(log_record->GetAttributes(), sout_);

sout_ << "\n"
<< " trace_id : " << std::string(trace_id, trace_id_len) << "\n"
<< " span_id : " << std::string(span_id, span_id__len) << "\n"
<< " trace_flags : " << std::string(trace_flags, trace_flags_len) << "\n"
<< "}\n";
}

return sdklogs::ExportResult::kSuccess;
}

bool OStreamLogExporter::Shutdown(std::chrono::microseconds timeout) noexcept
{
is_shutdown_ = true;
return true;
}

} // namespace logs
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
Loading