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

Logging API and SDK modifications #12

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a064d19
Moved log record from stack to heap, removed const
MarkSeufert Nov 24, 2020
cea306c
Changed log to Log (camelcase)
MarkSeufert Nov 24, 2020
2c1586f
Added GetName() to logger API, along with tests
MarkSeufert Nov 24, 2020
350097d
Processor accepts shared_ptr instead of unique_ptr
MarkSeufert Nov 24, 2020
7646ea2
Added default values to the required log record fields
MarkSeufert Nov 24, 2020
25a3b1e
Added timestamp injection and tests
MarkSeufert Nov 25, 2020
dc8f9b9
Return noop logger when logger limit reached, formatting
MarkSeufert Nov 25, 2020
0ee12c6
Rename trace_flag to trace_flags in LogRecord
Dec 1, 2020
595ab5b
Set default traceid, spanid, traceflags in LogRecord.h
Dec 1, 2020
6083373
Inject spancontext (traceid, spanid, traceflags)
Dec 1, 2020
4c50ef2
Simplified logger context injection, added unit tests
MarkSeufert Dec 1, 2020
18b921a
Changed log(LogRecord) back from log(shared_ptr<LogRecord>)
MarkSeufert Dec 1, 2020
ca60fe2
Remove test for default value injection, comments
Dec 2, 2020
a30826a
Added const to Log(const LogRecord) and const GetName()
MarkSeufert Dec 2, 2020
36f7ba8
Change test names for better conventions / consistency
Dec 2, 2020
e7c67ad
Merge branch 'logs-API-SDK-update' of github.com:open-o11y/openteleme…
Dec 2, 2020
d3d13b4
change processor signatures for tests
Dec 2, 2020
0c97eae
Remove inMem exporter from build files
Dec 2, 2020
e153de5
Made processor.h interface match the processor PR
MarkSeufert Dec 2, 2020
d2cbd0f
Changed processor back to using unique_ptr from shared_ptr
MarkSeufert Dec 2, 2020
206e8ca
Modified Log overloads, fixed LogRecord KViterable
MarkSeufert Dec 3, 2020
30affaa
Added logger.Trace(~) overloads and test
MarkSeufert Dec 3, 2020
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
24 changes: 11 additions & 13 deletions api/include/opentelemetry/logs/log_record.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ enum class Severity : uint8_t
kDefault = kInfo // default severity is set to kInfo level, similar to what is done in ILogger
};

/* _nullKV is defined as a private variable that allows "resource" and
"attributes" fields to be instantiated using it as the default value */
static common::KeyValueIterableView<std::map<nostd::string_view, nostd::string_view>> _nullKV =
common::KeyValueIterableView<std::map<nostd::string_view, nostd::string_view>>{{}};

/**
* A default Event object to be passed in log statements,
* matching the 10 fields of the Log Data Model.
Expand All @@ -82,25 +77,28 @@ struct LogRecord
core::SystemTimestamp timestamp; // uint64 nanoseconds since Unix epoch
trace::TraceId trace_id; // byte sequence
trace::SpanId span_id; // byte sequence
trace::TraceFlags trace_flag; // byte
Severity severity; // Severity enum that combines severity_text and severity_number in the
// LogDataModel (can separate in SDK)
trace::TraceFlags trace_flags; // byte
Severity severity; // Severity enum that combines severity_text and severity_number

// other fields that will not be set by default
nostd::string_view name; // string
nostd::string_view body; // currently a simple string, but should be changed "Any" type
common::KeyValueIterable &resource; // key/value pair list
common::KeyValueIterable &attributes; // key/value pair list
nostd::shared_ptr<common::KeyValueIterable> resource; // key/value pair list
nostd::shared_ptr<common::KeyValueIterable> attributes; // key/value pair list

/* Default log record if user does not overwrite this.
* TODO: find better data type to represent the <Any> type for "body"
* Future enhancement: Potentially add other constructors to take default arguments
* from the user
**/
LogRecord() : resource(_nullKV), attributes(_nullKV)
LogRecord()
{
// TODO: in SDK, assign a default timestamp if not specified
name = "";
// Assign default values
timestamp = core::SystemTimestamp(std::chrono::seconds(0));
severity = Severity::kDefault;
trace_id = trace::TraceId();
span_id = trace::SpanId();
trace_flags = trace::TraceFlags();
}

/* for ease of use; user can use this function to convert a map into a KeyValueIterable for the
Expand Down
143 changes: 103 additions & 40 deletions api/include/opentelemetry/logs/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ class Logger
virtual ~Logger() = default;

/* Returns the name of the logger */
// TODO: decide whether this is useful and/or should be kept, as this is not a method required in
// the specification. virtual nostd::string_view getName() = 0;
virtual const nostd::string_view GetName() noexcept = 0;

/**
* Each of the following overloaded log(...) methods
Expand All @@ -55,65 +54,129 @@ class Logger
* @throws No exceptions under any circumstances.
*/

/* The below method is a logging statement that takes in a LogRecord.
* A default LogRecord that will be assigned if no parameters are passed to Logger's .log() method
* which should at minimum assign the trace_id, span_id, and timestamp
/**
* Logs a LogRecord, which contains all the fields of the Log Data Model. Normally called indirectly from other Log() Methods, but can be called directly for high detail.
* @param record A log record filled with information from the user.
*/
virtual void log(const LogRecord &record) noexcept = 0;

/** Overloaded methods for unstructured logging **/
inline void log(nostd::string_view message) noexcept
virtual void Log(const LogRecord &record) noexcept = 0;

///Overloaded Log methods, which are simpler than calling a LogRecord directly

/**
* Writes a log.
* @param severity The log's severity
* @param message The message to log
*/
inline void Log(Severity severity, nostd::string_view message) noexcept
{
// Set severity to the default then call log(Severity, String message) method
log(Severity::kDefault, message);
}
//Create a LogRecord to hold this information
LogRecord r;
r.severity = severity;
r.body = message;

inline void log(Severity severity, nostd::string_view message) noexcept
{
// TODO: set default timestamp later (not in API)
log(severity, message, core::SystemTimestamp(std::chrono::system_clock::now()));
//Call the main Log(LogRecord) method
Log(r);
}

inline void log(Severity severity,
nostd::string_view message,
core::SystemTimestamp time) noexcept
/**
* Writes a log.
* @param severity The log's severity
* @param name The name of the log
* @param message The message to log
*/
inline void Log(Severity severity, nostd::string_view name, nostd::string_view message) noexcept
{
// creates a LogRecord object with given parameters, then calls log(LogRecord)
//Create a LogRecord to hold this information
LogRecord r;
r.severity = severity;
r.body = message;
r.timestamp = time;
r.severity = severity;
r.name = name;
r.body = message;

log(r);
//Call the main Log(LogRecord) method
Log(r);
}

/** Overloaded methods for structured logging**/
// TODO: separate this method into separate methods since it is not useful for user to create
// empty logs
/**
* Writes a log.
* @param severity The severity of the log, from 1 to 24
* @param attributes A key/value object
*/
template <class T,
nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr>
inline void log(Severity severity = Severity::kDefault,
nostd::string_view name = "",
const T &attributes = {}) noexcept
nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr>
inline void Log(Severity severity, const T &attributes) noexcept
{
log(severity, name, common::KeyValueIterableView<T>(attributes));
//Create a LogRecord to hold this information
LogRecord r;
r.severity = severity;
r.attributes = nostd::shared_ptr<common::KeyValueIterable>(new common::KeyValueIterableView<T>{attributes});

//Call the main Log(LogRecord) method
Log(r);
}

inline void log(Severity severity,
nostd::string_view name,
const common::KeyValueIterable &attributes) noexcept
/**
* Writes a log.
* @param severity The severity of the log, from 1 to 24
* @param name The name of the log
* @param attributes A key/value object
*/
template <class T,
nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr>
inline void Log(Severity severity, nostd::string_view name, const T &attributes) noexcept
{
// creates a LogRecord object with given parameters, then calls log(LogRecord)
//Create a LogRecord to hold this information
LogRecord r;
r.severity = severity;
r.name = name;
r.attributes = attributes;
r.attributes = nostd::shared_ptr<common::KeyValueIterable>(new common::KeyValueIterableView<T>{attributes});

//Call the main Log(LogRecord) method
Log(r);
}

///Trace overloads

log(r);
/**
* Writes a log with a severity of trace.
* @param message The message to log
*/
inline void Trace(nostd::string_view message) noexcept
{
Log(Severity::kTrace, message);
}

/**
* Writes a log with a severity of trace.
* @param name The name of the log
* @param message The message to log
*/
inline void Trace(nostd::string_view name, nostd::string_view message) noexcept
{
Log(Severity::kTrace, name, message);
}

// TODO: add function aliases such as void debug(), void trace(), void info(), etc. for each
// severity level
/**
* Writes a log with a severity of trace.
* @param attributes A key/value object
*/
template <class T,
nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr>
inline void Trace(const T &attributes) noexcept
{
Log(Severity::kTrace, attributes);
}

/**
* Writes a log with a severity of trace.
* @param name The name of the log
* @param attributes A key/value object
*/
template <class T,
nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr>
inline void Trace(nostd::string_view name, const T &attributes) noexcept
{
Log(Severity::kTrace, name, attributes);
}

/** Future enhancement: templated method for objects / custom types (e.g. JSON, XML, custom
* classes, etc) **/
Expand Down
3 changes: 2 additions & 1 deletion api/include/opentelemetry/logs/noop.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ class NoopLogger final : public Logger
public:
NoopLogger() = default;

void log(const LogRecord &record) noexcept override {}
void Log(const LogRecord& record) noexcept override {}
const nostd::string_view GetName() noexcept override { return "noop logger"; };
};

/**
Expand Down
4 changes: 2 additions & 2 deletions api/test/logs/BUILD
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark")

cc_test(
name = "logger_provider_test",
name = "provider_test",
srcs = [
"logger_provider_test.cc",
"provider_test.cc",
],
deps = [
"//api",
Expand Down
8 changes: 4 additions & 4 deletions api/test/logs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
foreach(testname logger_provider_test logger_test)
add_executable(${testname} "${testname}.cc")
target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES}
foreach(testname provider_test logger_test)
add_executable(logs_api_${testname} "${testname}.cc")
target_link_libraries(logs_api_${testname} ${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
gtest_add_tests(TARGET ${testname} TEST_PREFIX logs. TEST_LIST ${testname})
gtest_add_tests(TARGET logs_api_${testname} TEST_PREFIX logs. TEST_LIST logs_api_${testname})
endforeach()
100 changes: 73 additions & 27 deletions api/test/logs/logger_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,41 @@ using opentelemetry::nostd::shared_ptr;
using opentelemetry::nostd::span;
using opentelemetry::nostd::string_view;

TEST(Logger, GetLoggerDefault)
TEST(LoggerTest, GetLoggerDefaultNoop)
{
auto lp = Provider::GetLoggerProvider();
auto logger = lp->GetLogger("TestLogger");
EXPECT_NE(nullptr, logger);
EXPECT_EQ(logger->GetName(), "noop logger");
}

TEST(Logger, GetNoopLoggerName)
{
auto lp = Provider::GetLoggerProvider();
auto logger = lp->GetLogger("TestLogger");
}

TEST(Logger, GetNoopLoggerNameWithArgs)
TEST(LoggerTest, GetLogger)
{
auto lp = Provider::GetLoggerProvider();

// Get a logger with no arguments
auto logger1 = lp->GetLogger("TestLogger1");

// Get a logger with options passed
auto logger2 = lp->GetLogger("TestLogger2", "Options");

// Get a logger with arguments
std::array<string_view, 1> sv{"string"};
span<string_view> args{sv};
auto logger = lp->GetLogger("NoopLoggerWithArgs", args);
// should probably also test that arguments were set properly too
// by adding a getArgs() method in NoopLogger
auto logger3 = lp->GetLogger("TestLogger3", args);
}

TEST(Logger, NoopLog)
{
auto lp = Provider::GetLoggerProvider();
auto logger = lp->GetLogger("TestLogger");
LogRecord r;
r.name = "Noop log name";
logger->log(r);
}
// Define a global log record that will be modified when the Log() method is called
static opentelemetry::nostd::shared_ptr<LogRecord> record_;

// Define a basic Logger class
class TestLogger : public Logger
{
void log(const LogRecord &record) noexcept override {}
void Log(const LogRecord &record) noexcept override
{
record_ = opentelemetry::nostd::shared_ptr<LogRecord>(new LogRecord(record));
}
const opentelemetry::nostd::string_view GetName() noexcept override { return "test logger"; };
};

// Define a basic LoggerProvider class that returns an instance of the logger class defined above
Expand All @@ -68,19 +66,67 @@ class TestProvider : public LoggerProvider
}
};

TEST(Logger, PushLoggerImplementation)
TEST(LoggerTest, PushLoggerImplementation)
{
// Push the new loggerprovider class into the global singleton
// Push the new loggerprovider class into the API
auto test_provider = shared_ptr<LoggerProvider>(new TestProvider());
Provider::SetLoggerProvider(test_provider);

auto lp = Provider::GetLoggerProvider();

// GetLogger(name, options) function
// Get a logger instance and check whether it's GetName() method returns
// "test logger" as defined in the custom implementation
auto logger = lp->GetLogger("TestLogger");
ASSERT_EQ("test logger", logger->GetName());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add ASSERT_EQ(logger2->GetName(), "TestLogger2")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API implementation returns a noop instance, so the name would always be "noop logger" which is what the first test checks for

}

// GetLogger(name, args) function
std::array<string_view, 1> sv{"string"};
Copy link

@kxyr kxyr Dec 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no harm in leaving this in?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup it's still in but the GitHub diff tool said that it was taken out and readded somewhere else... confusing

span<string_view> args{sv};
auto logger2 = lp->GetLogger("TestLogger2", args);
TEST(Logger, LogMethodOverloads)
{
// Use the same TestProvider and TestLogger from the previous test
auto test_provider = shared_ptr<LoggerProvider>(new TestProvider());
Provider::SetLoggerProvider(test_provider);

auto lp = Provider::GetLoggerProvider();
auto logger = lp->GetLogger("TestLogger");

// Check that calling the Log() overloads correctly constructs a log record which is automatically put into the static logger_ for testing

// Test Log(severity, name, message) method
logger->Log(Severity::kError, "Log Name", "This is the log message");
ASSERT_EQ(record_->severity, Severity::kError);
ASSERT_EQ(record_->name, "Log Name");
ASSERT_EQ(record_->body, "This is the log message");

// Test Trace(name, KVIterable) method
std::map<std::string, std::string> m = {{"key1", "val1"}, {"key2", "val2"}};
logger->Trace("Logging a map", m);
ASSERT_EQ(record_->severity, Severity::kTrace);
ASSERT_EQ(record_->name, "Logging a map");
ASSERT_EQ(record_->attributes->size(), 2);
}

TEST(LogRecord, SetDefault)
{
LogRecord r;

// Check that the timestamp is set to 0 by default
ASSERT_EQ(r.timestamp, opentelemetry::core::SystemTimestamp(std::chrono::seconds(0)));

// Check that the severity is set to kDefault by default
ASSERT_EQ(r.severity, Severity::kDefault);

// Check that trace_id is set to all zeros by default
char trace_buf[32];
r.trace_id.ToLowerBase16(trace_buf);
ASSERT_EQ(std::string(trace_buf, sizeof(trace_buf)), "00000000000000000000000000000000");

// Check that span_id is set to all zeros by default
char span_buf[16];
r.span_id.ToLowerBase16(span_buf);
ASSERT_EQ(std::string(span_buf, sizeof(span_buf)), "0000000000000000");

// Check that trace_flags is set to all zeros by default
char flags_buf[2];
r.trace_flags.ToLowerBase16(flags_buf);
ASSERT_EQ(std::string(flags_buf, sizeof(flags_buf)), "00");
}
File renamed without changes.
Loading