Skip to content

Commit

Permalink
Add Ostream log exporter and tests (open-telemetry#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
Karen Xu committed Dec 22, 2020
1 parent b4f6945 commit 3dab113
Show file tree
Hide file tree
Showing 5 changed files with 576 additions and 0 deletions.
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

0 comments on commit 3dab113

Please sign in to comment.