-
Notifications
You must be signed in to change notification settings - Fork 906
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
OTEP: Logger.Enabled #4290
OTEP: Logger.Enabled #4290
Conversation
This PR was marked stale due to lack of activity. It will be closed in 7 days. |
This PR was marked stale due to lack of activity. It will be closed in 7 days. |
oteps/logs/4290-logger-enabled.md
Outdated
2. Avoid allocating memory to store a log record, avoid performing computationally expensive operations and avoid exporting when emitting a log or event record is unnecessary. | ||
3. Configure a minimum a log serverity level on the SDK level. | ||
4. Filter out log and event records when they are not inside a recording span. | ||
5. Have **fine-grained** filtering control for logging pipelines without using an OpenTelemetry Collector (e.g. mobile devices, serverless, IoT). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I am not so sure about.. Do we expect OTel SDK to provide such control, mimicking what is often provided by the Logging libraries themselves? They often provide sophisticated mechanisms, and it may not be worth replicating everything in OTel...
We can explicitly state that our desire is to provide some control, but not necessarily matching/competing with existing logging libraries?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not expect the Logs API to be as user-friendly as the logging libraries popular for given language.
However, I think that the SDK (the logging backend) should be able handle different logging processing scenarios. E.g. sending verbose logs via user_events and warning logs via OTLP (probably I could come up with better examples). I heard that someone wanted to send non-event log records and event records to different destinations. Moreover, there is the following question in the Events Basics OTEP:
How to support routing logs from the Logs API to a language-specific logging library while simultaneously routing logs from the language-specific logging library to an OpenTelemetry Logging Exporter?
I see logging libraries as logs frontend and Logs SDK as logs backend. Logs API is the thing which bridges between the frontend and backend.
Side note: I think that even without this use case we would get the same design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved it as the last use-case given that it may be seen as the least important. a4257ef
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if the current way it is described looks good overall.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cijothomas, PTAL
I want to also add that some users may want to directly use Logs API without using any bridged library. See: #4352
EDIT: After a demo of the OTel Go Logs design it looks like an acceptable solution, but the OTEP should call out explicitly the optimizations we have done. E.g. that we are not looping through processors when none of them implements // Enabled returns true if at least one Processor held by the LoggerProvider
// that created the logger will process param for the provided context and param.
//
// If it is not possible to definitively determine the param will be
// processed, true will be returned by default. A value of false will only be
// returned if it can be positively verified that no Processor will process.
func (l *logger) Enabled(ctx context.Context, param log.EnabledParameters) bool {
// If there are more Processors than FilterProcessors we cannot be sure
// that all Processors will drop the record. Therefore, return true.
//
// If all Processors are FilterProcessors, check if any is enabled.
return len(l.provider.processors) > len(l.provider.fltrs) || anyEnabled(ctx, param, l.provider.fltrs)
} I also plan creating a PR for #4364 as this does not seem controversial. This should simplify this OTEP (assuming the PR will be merged). |
avoid performing computationally expensive operations, | ||
and avoid exporting | ||
when emitting a log or event record is unnecessary. | ||
3. Configure a minimum log severity level on the SDK level. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, this is configurable on a per-processor level, not just SDK wide, so you can have alternate destinations for different log severity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I will add this use case. Also given later I already described:
MinimumSeverityLevelProcessor
is for configuring log processing pipelines.
It is the only choice when one would like to set the minimum severity level
for a certain exporting pipeline.
For example, one batching processor would be exporting logs only above info level
via OTLP and a second simple processor would be exporting all logs to stdout.
|
||
## Explanation | ||
|
||
For (1) (2), the user can use the Logs API `Logger.Enabled` function, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These number references are confusing as the seem to be referencing something from another section.
- A declarative configuration to conveniently address | ||
the most popular use cases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be doing too much. Why not just focus on adding logger enabled like the title indicates?
There is nothing preventing having both `LoggerConfig.min_severity` | ||
and something like a `MinimumSeverityLevelProcessor`. | ||
|
||
`LoggerConfig.min_severity` is a configuration for concrete loggers. | ||
For instance, it would be a easy to use feature when one would like to change | ||
the minimum severity level for all loggers with names matching | ||
`MyCompany.NoisyModule.*` wildcard pattern. | ||
With `LoggerConfigurator` the user is not able to change/apply a processor. | ||
|
||
`MinimumSeverityLevelProcessor` is for configuring log processing pipelines. | ||
It is the only choice when one would like to set the minimum severity level | ||
for a certain exporting pipeline. | ||
For example, one batching processor would be exporting logs only above info level | ||
via OTLP and a second simple processor would be exporting all logs to stdout. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having two "layers" of setting up the severity filtering may be very confusing for the users.
The behavior of LoggerConfig.min_severity
could be achieved also via a filter processor that takes into consideration the InstrumentationScope
when filtering by severity level.
Most probably for languages like Java LoggerConfig.min_severity
won't be helpful as the severity level is already set on the bridged logging libraries.
The important piece is to support cases like described here: https://github.com/open-telemetry/opentelemetry-specification/pull/4290/files#r1925688450 which is not possible via LoggerConfig.min_severity
@jack-berg, @trask, any opinion?
@jack-berg, are all OTel SDKs are supposed to support LoggerConfig
(assuming it gets stabilized) or is it is assumed as an optional feature? Couldn't we provide processors which result in the same behavior?
CC @MrAlias
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
psuedo-code of configuration that could be possible when we use processors instead of logger config
logs:
processors:
- min_sev_processor:
level: info
processor:
batch_processor:
exporter: otlp_log_exporter
- simple_processor:
exporter: stdoutlog_exporter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side note: I think that LoggerConfig
functionality could be replaced with a LogRecordProcessor
decorator that implements the same thing. I may try to prototype it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that LoggerConfig functionality could be replaced with a LogRecordProcessor decorator that implements the same thing. I may try to prototype it.
Here is a PR for OTel Go that improves the filtering processor so that it has instrumentation scope and resource in its input.
Therefore, it can replace the the need of LoggerConfig.min_severity
as one can make a processor that just filters the log records by severity and instrumentation scope.
This PR was marked stale due to lack of activity. It will be closed in 7 days. |
I moving this OTEP to draft per this #4290 (comment). I start leaning towards just working on #4363 and maybe some clarifying spec improvements. |
avoid performing computationally expensive operations, | ||
and avoid exporting | ||
when emitting a log or event record is unnecessary. | ||
3. Configure a minimum log severity level on the SDK level. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Configure a minimum log severity level on the SDK level.
Sharing my experience from OTel .NET, Rust. Such a capability is already provided by the existing Logging library.
For example, the following one-liner is enough to tell .NET's ILogger to only send INFO and above to OpenTelemetry.
builder.AddFilter<OpenTelemetryLoggerProvider>("*", LogLevel.INFO);
Similar in Rust. The following tells the logging library to send only Info and above to OTel
let filter_otel = EnvFilter::new("info");
let otel_layer = otel_layer.with_filter(filter_otel);
Can you share scenarios where this functionality had to be provided by OTel itself? Is this only for scenarios where users use OTel Logs/Event API and they need to achieve filtering?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this only for scenarios where users use OTel Logs/Event API and they need to achieve filtering?
This is one of the reasons. Thanks to it we get rid the overhead that would caused by bridge. Making complex OTel Events could cause significant overhead because the conversions done in the brigde have to rely on reflection. In Go, it would be better if instrumentation libraries use OTel Log's API directly. Partialy related: https://github.com/open-telemetry/opentelemetry-go/blob/main/log/DESIGN.md#reuse-slog
For OTel Semantical events and log records pecific processing/filtering it would be easier for the users to use the OTel Data Model.
We could provide filtering and processors that are related to OTel domain. E.g. filtering by span context. If we do not have this in via SDK then we would need to create such filter for each logging library.
Finally, I guess we would like to have some support for filtering in OTel Configuration files in the future.
of a `LoggerConfig` for given `Logger`. | ||
|
||
For (4), the user can use the Tracing API to check whether | ||
there is a sampled span in the current context before creating |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the user here referring to end-users? Are you expecting users to do this pattern?
do_something_important()
{
logger.log("I did something important", results = {results()});
}
do_something_important()
{
if(Span::GetActiveSpan().IsRecording())
{
logger.log("I did something important", results = {results()});
}
else
{
//don't do logging.
}
}
I think this should be a SDK setting and instrumentations should not have to worry about this. They always call logger.log. Upto SDK to decide (based on config) if the log must be dropped as the active span is not recorded.
(Sorry If I misunderstood the section!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It could be also implemented via a Processor which filters out not recorded spans.
<!-- markdownlint-enable no-hard-tabs --> | ||
|
||
The addition of `LogRecordProcessor.Enabled` is necessary for | ||
use cases where filtering is dynamic and coupled to processing, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to get more clarification on this.
For 5 (when exporting to OS kernel facilities like etw/user-events), this is primarily a perf saving mechanism. If OS says nobody is interested in your log/event, then the producer can short circuit. This has to be done very quickly and typically only has 1-2 top-level parameter like Severity/EventName/ScopeName.
6 is referring to scenarios where users want to do filtering, primarily to control volume of telemetry exported. This usually is done based on LogRecord (i.e body/attributes/name/severity etc.). I believe this is already doable by writing normal LogRecordProcessors, like the one shown in #4407
In other words, I see 5 requires LogProcessor.Enabled, but 6 does not require.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. My description of 6 is indeed not clear. I meant that the perf saving mechanism works when the procesors are composed - supports the advanced scenarios which I described in #4407
@cijothomas, I just want to call out that I really appreciate your comments even though this PR is in draft state and not updated. It helps me preparing #4363, which I guess you are also in favor of. For now I am closing this PR. I will focus on #4407. I will create an OTEP for it only if it would occur to be necessary. However, I think that the discussions we already had show that there is a need for adding an opt-in Among Go SIG we were evaluating a few times an alternative to provide some new "filter" abstraction which is decoupled from the "processor". However, we faced more issues than benefits going this route (some if this is described here, but there were more issues: open-telemetry/opentelemetry-go#5825 (comment) . With the current opt-in It is worth to adding that Rust design is similar and it also has an I also want to call out form https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/0265-event-vision.md#open-questions:
To support this we would need a log record processor which bridges the Logs API calls to given logging library. For such case we would need an |
Full agree with this. (I also shared this opinion in offline discussion as well) |
Per #6271 (comment) > We agreed that we can move `FilterProcessor` directly to `sdk/log` as Logs SDK does not look to be stabilized soon. - Add the possibility to filter based on the resource and scope which is available for the SDK. The scope information is the most important as it gives the possibility to e.g. filter out logs emitted for a given logger. Thus e.g. open-telemetry/opentelemetry-specification#4364 is not necessary. See open-telemetry/opentelemetry-specification#4290 (comment) for more context. - It is going be an example for open-telemetry/opentelemetry-specification#4363 There is a little overhead (IMO totally acceptable) because of data transformation. Most importantly, there is no new heap allocation. ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/log cpu: 13th Gen Intel(R) Core(TM) i7-13800H │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ LoggerEnabled-20 4.589n ± 1% 319.750n ± 16% +6867.75% (p=0.000 n=10) │ old.txt │ new.txt │ │ B/op │ B/op vs base │ LoggerEnabled-20 0.000Ki ± 0% 1.093Ki ± 13% ? (p=0.000 n=10) │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ LoggerEnabled-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ ¹ all samples are equal ``` `Logger.Enabled` is still more efficient than `Logger.Emit` (benchmarks from #6315). ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/log cpu: 13th Gen Intel(R) Core(TM) i7-13800H BenchmarkLoggerEmit/5_attributes-20 559934 2391 ns/op 39088 B/op 1 allocs/op BenchmarkLoggerEmit/10_attributes-20 1000000 5910 ns/op 49483 B/op 5 allocs/op BenchmarkLoggerEnabled-20 1605697 968.7 ns/op 1272 B/op 0 allocs/op PASS ok go.opentelemetry.io/otel/sdk/log 10.789s ``` Prior art: - #6271 - #6286 I also created for tracking purposes: - #6328
The purpose of this OTEP is to have an agreement that the SDK needs both:
Logger.Enabled
for customization and flexibility e.g. via extendingLogRecordProcessor
withEnabled
method, Add Enabled to LogRecordProcessor #4363LoggerConfig
fields to conveniently address the most popular use cases, especially configuring the minimum severity level, Add min_severity to LoggerConfig #4364The OTEP tries to elaborate how/why it should work well with (trace) sampling as well as Events API.
Fixes #4207
Side note: I can create issues for each "open question" for tracking purposes.