-
Notifications
You must be signed in to change notification settings - Fork 894
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
base: main
Are you sure you want to change the base?
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. |
Co-authored-by: Cijo Thomas <[email protected]>
is going to emit a log record. | ||
|
||
For (3), the user can declaratively configure the Logs SDK | ||
using `LoggerConfigurator` to set the `minimum_severity_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.
I think we'll (eventually) have more config options for severity, sampling, and other things, e.g. a few extra scenarios I can think of:
- minimum level for this event name
- maximum level to sample at (e.g. you only want to sample info and lower)
- minimum level to record exception stack traces at
- sampling on/off rate for this event name
- sample based on spans, but also record all logs without parent span (e.g. startup logs are quite important)
- throttling config (record this event name, but at most X times per time unit)
- ...
We'll need to think how to structure them on LoggerConfig
API so it's extendable but only introduce options we have an immediate need for).
E.g. I think we may need something like LoggerConfig.sampling_strategy = always_on | span_based
instead of disabled_on_sampled_out_spans
.
Also we'd need to consider having a callback in addition to pure declarative config (e.g. for cases like sampling based on spans, but also startup logs are all in). For this we should consider having a sampler-like interface which would have implementations similar to tracer sampler and then we might want to steal declarative config from tracer_provider.sampler
.
TL;DR:
- let's figure out what we should do first - declarative config vs api extensibility
- let's come up with extendable options
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.
We'll need to think how to structure them on LoggerConfig API so it's extendable but only introduce options we have an immediate need for).
Adding a field is a backwards compatible change. I agree that we should add new fields (options) only if we have a big demand for it.
we'd need to consider having a callback in addition to pure declarative config
For more ad I think the LogRecordProcessor
comes into play. It should handle the most advanced scenarios.
I will do my best to describe it "Future possibilities" section.
let's figure out what we should do first - declarative config vs api extensibility
IMO extensibility (LogRecordProcessor.Enabled
) is more important as it can also solve the (3) and (4) use cases even without the config extensions.
let's come up with extendable options
I would like to know if there is a use cases that the proposal of adding LogRecordProcessor.Enabled
would not solve.
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.
Also we'd need to consider having a callback in addition to pure declarative config
I added "Alternatives: Dynamic Evaluation in LoggerConfig"
I think we'll (eventually) have more config options for severity, sampling, and other things, e.g. a few extra scenarios I can think of:
I added "Future possibilities: Extending LoggerConfig
We would need to closer look at the alternatives when prototyping and during the Development phase of the spec.
|
||
processors := l.provider.processors() | ||
for _, p := range processors { | ||
if p.Enabled(ctx, param) { |
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.
what would you expect processors to do inside Enabled
? If this is a delegating processor, would it call the underlying one? When I'm implementing a processor that does filtering, I should assume it's in the certain position in the pipeline?
I.e. it sounds like there should be just one processor in the pipeline that decides if log should be created. If so, should it be a processor responsibility at all or can we call this component LogFilter
, LogSampler
, etc and make it a configurable behavior independent of the processor?
If this component returns true, all processing pipelines would get log record and can drop it if they are filtering too.
If it returns false, none of them should. This is the same as you have outlined in the otep, just extracts filtering from 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.
Some examples of logging filters:
Here's a good old .NET ILogger filter
builder.Logging.AddFilter((provider, category, logLevel) =>
{
if (provider.Contains("ConsoleLoggerProvider")
&& category.Contains("Controller")
&& logLevel >= LogLevel.Information)
{
return true;
}
else if (provider.Contains("ConsoleLoggerProvider")
&& category.Contains("Microsoft")
&& logLevel >= LogLevel.Information)
{
return true;
}
else
{
return false;
}
});
or good old log4j
private void updateLoggers(final Appender appender, final Configuration config) {
final Level level = null;
final Filter filter = null;
for (final LoggerConfig loggerConfig : config.getLoggers().values()) {
loggerConfig.addAppender(appender, level, filter);
}
config.getRootLogger().addAppender(appender, level, filter);
}
I.e. composition instead of inheritance - when the logging is configured, each appender/provider pipeline gets one dedicated filter independent of the appender/provider logic.
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.
what would you expect processors to do inside
Enabled
? If this is a delegating processor, would it call the underlying one? When I'm implementing a processor that does filtering, I should assume it's in the certain position in the pipeline?
Depends on the processor.
- If this is an exporting processor, e.g. for user_events then
Enabled
, returns results only for itself. Example: https://github.com/open-telemetry/opentelemetry-rust-contrib/blob/30a3f4eeac0be5e4337f020b7e5bfe0273298858/opentelemetry-user-events-logs/src/logs/exporter.rs#L158-L165. Use case (5). - For a filtering processor it needs to wrap some other processor for which it applies the filtering. Then it makes it possible to have two batching processors with different filters. Example: https://github.com/open-telemetry/opentelemetry-go-contrib/blob/a5391050f37752bbcb62140ead0301981af9d748/processors/minsev/minsev.go#L74-L84 Use case (6).
I.e. it sounds like there should be just one processor in the pipeline that decides if log should be created. If so, should it be a processor responsibility at all or can we call this component
LogFilter
,LogSampler
, etc and make it a configurable behavior independent of the processor?If this component returns true, all processing pipelines would get log record and can drop it if they are filtering too. If it returns false, none of them should. This is the same as you have outlined in the otep, just extracts filtering from processing.
I think is that it does not suites well (5) nor (6) use cases.
For (5) would someone configuring an user_events exporter would have to configure a filterer as well as processor?
For (6) it would not be even possible to e.g. allowing distinct minimum severity levels for different processorsas the filters would run before the processors.
The other way is that the processors themselves would accept a filterer, but I do not think we want to Logs SDK to become https://logging.apache.org/log4j/2.x/manual/architecture.html (which by the way does not offer an Enabled
API).
I.e. composition instead of inheritance - when the logging is configured, each appender/provider pipeline gets one dedicated filter independent of the appender/provider logic.
Filtering processor mentioned before wraps an existing processor so it is following the principle to favor composition over inheritance.
I added "Alternatives: Separate LogRecordFilterer Abstraction".
We would need to closer look at the alternatives when prototyping and during the Development phase of the spec.
The main purpose of this OTEP is to have an agreement that the SDK needs both:
Logger.Enabled
for customization and flexibilityLoggerConfig
fields to conveniently address the most popular use cases such as minimum severity levelThe OTEP tries to elaborate that it should work well with (trace) sampling as well as events.
Fixes #4207
New issues are going to be created if this OTEP is accepted and merged.
Each "alternative" and "open question" would also have a new issue for tracking purposes (to explicitly reject or accept them).