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

Update logs sdk and api to follow specification #1884

Merged
merged 16 commits into from
Jan 28, 2023

Conversation

owent
Copy link
Member

@owent owent commented Dec 22, 2022

Fixes #1692
Fixes #1693
Fixes #1687
Fixes #1688

Changes

  • Add include_trace_context and scope attribute for logs API and SDK.
  • Add type trait to set field of LogRecord.
  • Add EventLoggerProvider and LoggerProvider

For significant contributions please make sure you have completed the following items:

  • CHANGELOG.md updated for non-trivial changes
  • Unit tests have been added
  • Changes in public API reviewed

@owent owent requested a review from a team December 22, 2022 11:58
@codecov
Copy link

codecov bot commented Dec 22, 2022

Codecov Report

Merging #1884 (d336df3) into main (7e7184d) will not change coverage.
The diff coverage is n/a.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #1884   +/-   ##
=======================================
  Coverage   87.12%   87.12%           
=======================================
  Files         165      165           
  Lines        4596     4596           
=======================================
  Hits         4004     4004           
  Misses        592      592           
Impacted Files Coverage Δ
.../include/opentelemetry/common/key_value_iterable.h 100.00% <ø> (ø)

@owent owent changed the title [WIP] Update logs sdk and api to follow specification Update logs sdk and api to follow specification Dec 22, 2022
Signed-off-by: WenTao Ou <admin@owent.net>
Signed-off-by: WenTao Ou <admin@owent.net>
Signed-off-by: WenTao Ou <admin@owent.net>
Signed-off-by: owent <admin@owent.net>
@lalitb
Copy link
Member

lalitb commented Jan 4, 2023

Sorry for delay on this. Will provide my comments in this week.

@owent
Copy link
Member Author

owent commented Jan 5, 2023

Sorry for delay on this. Will provide my comments in this week.

Thanks, should we mark the old Logger::Log() APIs as deprecated?The new Logger::EmitLogRecord() and EventLogger::EmitEvent() accept the SpanID, TraceID, Attributes and etc in any order.

@lalitb
Copy link
Member

lalitb commented Jan 10, 2023

@owent - Do you think this issue - open-telemetry/opentelemetry-specification#3086 - will bring any changes in the Event API/SDK implementation ?

@lalitb
Copy link
Member

lalitb commented Jan 10, 2023

should we mark the old Logger::Log() APIs as deprecated?The new Logger::EmitLogRecord() and EventLogger::EmitEvent() accept the SpanID, TraceID, Attributes and etc in any order.

I thought Logger::Log() API provides single line convenience API to instrumentation libraries for emitting logs, and we should keep them as it is.

@owent
Copy link
Member Author

owent commented Jan 10, 2023

@owent - Do you think this issue - open-telemetry/opentelemetry-specification#3086 - will bring any changes in the Event API/SDK implementation ?

Not yet. This PR implement the main branch of https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/event-api.md by now.And the EventLogger is a standalone component.
Maybe some APIs of EventLogger may change in the future, I can continue modify them then.

@owent
Copy link
Member Author

owent commented Jan 10, 2023

should we mark the old Logger::Log() APIs as deprecated?The new Logger::EmitLogRecord() and EventLogger::EmitEvent() accept the SpanID, TraceID, Attributes and etc in any order.

I thought Logger::Log() API provides single line convenience API to instrumentation libraries for emitting logs, and we should keep them as it is.

OK, we can keep Logger::Log(), just wondering should we also modify the parameters so SpanID, TraceID, Attributes and etc and be passed in any order, just like Logger::EmitLogRecord()?

* @return nostd::span<const std::pair<nostd::string_view, common::AttributeValue>>
*/
inline static nostd::span<const std::pair<nostd::string_view, common::AttributeValue>>
MakeAttributes(std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>
Copy link
Member

Choose a reason for hiding this comment

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

Won't this will have usage inconsistency across Trace and Logs. In Trace API, we can pass initializer_list directly as argument (due to use of overloaded methods), and in logs we have to use MakeAttributes() to convert and pass ?

Copy link
Member Author

Choose a reason for hiding this comment

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

initializer_list can not be detected by universial reference(ArgumentType &&).Is there any better suggestion here?

Copy link
Member

@lalitb lalitb Jan 19, 2023

Choose a reason for hiding this comment

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

What if we remove the variadic template argument from EmitLogRecord() and EmitEvent(), and have a method in LogRecord to add attributes?

LogRecord::SetAttribute(nostd::string_view key, const opentelemetry::common::AttributeValue &value) LogRecord::SetAttributes(const common::KeyValueIterable &attributes)

Copy link
Member Author

Choose a reason for hiding this comment

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

LogRecord already has SetAttribute. I think it's easier to use for users if they do not need to emit log in just one call. Just like Logger::Log, users don't need CreateLogRecord() and then SetAttribute().
MakeAttributes can be used to set attributes easier.

  logger->EmitLogRecord(Severity::kFatal, "Logging an initializer list",
                        Logger::MakeAttributes({{"key1", "value 1"}, {"key2", 2}}));

Copy link
Member

@lalitb lalitb Jan 20, 2023

Choose a reason for hiding this comment

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

Thanks for explanation. For logger API, we will now have two different methods doing similar thing:

logger->Log(Severity::kFatal, "Logging an initializer list", {{"key1", "value 1"}, {"key2", 2}})

And

logger->EmitLogRecord(Severity::kFatal, "Logging an initializer list",
                        Logger::MakeAttributes({{"key1", "value 1"}, {"key2", 2}}));

And the existing (first) one is much simpler. Or did you planned to remove the Logger::Log() method after adding the new method ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have the same question above( #1884 (comment)_ ). I think we can discuss if we should keep Logger::Log.

Copy link
Member

@lalitb lalitb Jan 21, 2023

Choose a reason for hiding this comment

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

I also don't see advantage having both the methods. We can get rid of Logger::Log. Thanks for clarifying.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, Logger::Log is removed now.

api/include/opentelemetry/logs/event_logger.h Outdated Show resolved Hide resolved
api/include/opentelemetry/logs/event_logger.h Outdated Show resolved Hide resolved
api/test/logs/provider_test.cc Outdated Show resolved Hide resolved
Signed-off-by: WenTao Ou <admin@owent.net>
Copy link
Member

@lalitb lalitb left a comment

Choose a reason for hiding this comment

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

LGTM Thanks.

api/include/opentelemetry/logs/logger.h Outdated Show resolved Hide resolved
@lalitb
Copy link
Member

lalitb commented Jan 26, 2023

Thanks @owent for fixing all the comments. This PR was discussed in today's community meeting. As we are using variadic templates/pack-paraments, and other meta-programming concepts in the code, there was interest for other community members to have a look into the PR. The suggestion was to keep this PR open for review till next community meeting (i.e, 30th Jan) for sufficient time for others to review and comment if required :)

@owent
Copy link
Member Author

owent commented Jan 26, 2023

Thanks @owent for fixing all the comments. This PR was discussed in today's community meeting. As we are using variadic templates/pack-paraments, and other meta-programming concepts in the code, there was interest for other community members to have a look into the PR. The suggestion was to keep this PR open for review till next community meeting (i.e, 30th Jan) for sufficient time for others to review and comment if required :)

Thanks.

Copy link
Member

@marcalff marcalff left a comment

Choose a reason for hiding this comment

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

Ok with the coding patterns used.

Comment on lines +65 to +84
template <class... ArgumentType>
void EmitEvent(nostd::string_view event_name, ArgumentType &&... args)
{
nostd::shared_ptr<Logger> delegate_logger = GetDelegateLogger();
if (!delegate_logger)
{
return;
}
nostd::unique_ptr<LogRecord> log_record = delegate_logger->CreateLogRecord();
if (!log_record)
{
return;
}

IgnoreTraitResult(
detail::LogRecordSetterTrait<typename std::decay<ArgumentType>::type>::template Set(
log_record.get(), std::forward<ArgumentType>(args))...);

EmitEvent(event_name, std::move(log_record));
}
Copy link
Member

Choose a reason for hiding this comment

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

@owent

So, with the use of variadic templates, type traits, lamda expressions in the implementation, etc ... there is a considerable complexity here.

Because this is in the public API, users trying to figure out how to use this might have some questions and will need a clear documentation in opentelemetry.io with examples, and I include myself in "users" here.

This also increases complexity for maintenance, because if this breaks, understanding all the details and providing a fix will take more time, compared to more trivial C++ code.

I assume that the complexity here also saves from some repetitive code that would be also hard to maintain otherwise, so there are plus sides as well.

Overall, the complexity argument does not make the code invalid, so I view it as:

  • subjective (people with different experiences with these patterns will have different views)
  • and not objective (a given area in the code is technically broken and needs a fix)

To confirm, ok with me to have this code merged to main, knowing fully that I would need to improve my own variadic template skills as a pre requisite if I ever have to make changes here.

LGTM for this part, did not check everything else yet.

Copy link
Member Author

@owent owent Jan 28, 2023

Choose a reason for hiding this comment

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

Thanks for your feedback. Here is my thinking about this complexity.There is a little different between trace and log APIs, with trace, we always need a variable to hold the lifetime of span(Span or Scope) and decide when to call End() by RAII, so we can set data field of Span one by by one after we create the span.
But in the use case of logs, it's easier for users(I'm also a user of both log and trace.) to use by call just one function, this function is Logger::Log() before and Logger::EmitLogRecord() now.There are also many data fields we need set to LogRecord, log body, trace information, attributes, timestamp and so on.There are three ways to set attributes before(By const T&, std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>, const common::KeyValueIterable &), and user also can decide whether to set other data fields, log body or trace information. Just consider log body, trace context and attributes, there will be more than 2*2*3+2*2=16 possibilities.Actually, we had more than 10 overloads of Logger::Log() before.If we have more options for logs, then the overloads will increase exponentially(I'm trying to add TraceContext now, according to https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/api.md#logrecord).It will also be hard to maintain these overloads, especially we may add more options in the future.

I think we can disscuss more deeply if there is any better idea about how to solve this problem.

The approach of this PR try to use template <class ValueType> struct LogRecordSetterTrait; to implement these overloads by type, and also ignore the argument order.We can use std::decay<ArgumentType>::type to remove const, reference and violate of ArgumentType and use it as the template argument of template <class ValueType>, so we can implement any overload action by just write one template specialization for LogRecordSetterTrait. And the template <class ArgumentType> inline static LogRecord *Set(LogRecord *log_record, ArgumentType &&arg) noexcept use the universal reference so it can accept both left value reference and right value with or without const or violate.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks @owent for the details.

This seems the best solution then ... and great work.

Copy link
Member

@esigo esigo left a comment

Choose a reason for hiding this comment

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

LGTM
Thanks for the great work :)
I agree that, maintaining this will be challenging but I see no better option.

sdk/test/logs/logger_provider_sdk_test.cc Show resolved Hide resolved
@esigo esigo added the ok-to-merge The PR is ok to merge (has two approves or raised by a maintainer/approver and has one approve) label Jan 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ok-to-merge The PR is ok to merge (has two approves or raised by a maintainer/approver and has one approve)
Projects
None yet
5 participants