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

OTEP: Logger.Enabled #4290

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open

Conversation

pellared
Copy link
Member

@pellared pellared commented Nov 12, 2024

The main purpose of this OTEP is to have an agreement that the SDK needs both:

  • a "hook" for Logger.Enabled for customization and flexibility
  • new LoggerConfig fields to conveniently address the most popular use cases such as minimum severity level

The 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).

Copy link

This PR was marked stale due to lack of activity. It will be closed in 7 days.

@github-actions github-actions bot added the Stale label Nov 21, 2024
@pellared pellared removed the Stale label Nov 21, 2024
Copy link

This PR was marked stale due to lack of activity. It will be closed in 7 days.

@github-actions github-actions bot added the Stale label Nov 29, 2024
@pellared pellared removed the Stale label Nov 29, 2024
@carlosalberto carlosalberto added the OTEP OpenTelemetry Enhancement Proposal (OTEP) label Dec 5, 2024
@pellared pellared self-assigned this Dec 10, 2024
@pellared pellared changed the title [WIP] [OTEP] Logger.Enabled [WIP] OTEP: Logger.Enabled Dec 10, 2024
@pellared pellared changed the title [WIP] OTEP: Logger.Enabled OTEP: Logger.Enabled Dec 16, 2024
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`
Copy link
Contributor

@lmolkova lmolkova Dec 16, 2024

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

Copy link
Member Author

@pellared pellared Dec 17, 2024

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.

Copy link
Member Author

@pellared pellared Dec 18, 2024

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) {
Copy link
Contributor

@lmolkova lmolkova Dec 17, 2024

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.

Copy link
Contributor

@lmolkova lmolkova Dec 17, 2024

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.

Copy link
Member Author

@pellared pellared Dec 17, 2024

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.

  1. 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).
  2. 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.

@pellared pellared marked this pull request as ready for review December 18, 2024 10:56
@pellared pellared requested review from a team as code owners December 18, 2024 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
OTEP OpenTelemetry Enhancement Proposal (OTEP)
Projects
Status: In Progress
Status: In progress
Development

Successfully merging this pull request may close these issues.

Specify how Logs SDK implements Enabled
5 participants