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

Multiple processors on tracer: configuration #286

Merged
merged 7 commits into from
Oct 22, 2019

Conversation

lmolkova
Copy link

@lmolkova lmolkova commented Oct 17, 2019

Fixes #226: tracers should allow multiple processors to be registered on them.
Processors are executed sequentially.

At the same time it should be possible to chain processors (e.g. filter or enrich + export).

This PR attempts to fix both.

In the example

  • we add pipeline that exports each span to Zipkin,
  • another pipeline that exports to AppInsights, but filters some spans out first
  • adds debugging processor that just writes all spans to debug output

all pipelines work together on each span.

using (var tracerFactory = TracerFactory.Create(builder => builder
    .UseZipkin(o =>
    {
        o.ServiceName = "test-zipkin";
        o.Endpoint = new Uri(zipkinUri);
    })
    .UseApplicationInsights(
        o => o.InstrumentationKey = "e0fbfdfa-e302-49c8-9310-c3919b163c00", 
        p => p.AddProcessor(next => new FilteringSpanProcessor(next)))
    .UseDebuggingProcessor()))
{
}

Assuming there are Use extensions it looks not that bad.
But advanced use case (without Use sugar) looks a bit scary

 using (var tracerFactory = TracerFactory.Create(builder => builder
	.AddProcessorPipeline(p => p
		.AddProcessor(next => new FilteringSpanProcessor(next))
		.SetExporter(new ZipkinTraceExporter(new ZipkinTraceExporterOptions
		{
			ServiceName = "test",
		})))))
{
}

@lmolkova
Copy link
Author

@pakrym @JamesNK would be great to hear your feedback on this if you have time

@lmolkova lmolkova changed the title Multiple processors on tracer: configuration [Draft] Multiple processors on tracer: configuration Oct 17, 2019
var tasks = new List<Task>();
foreach (var processor in this.processors)
{
tasks.Add(processor.ShutdownAsync(cancellationToken));
Copy link
Member

Choose a reason for hiding this comment

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

perhaps some protection from processors returning null task can be added here.

Copy link
Member

Choose a reason for hiding this comment

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

so "WhenAll" will not crash

Copy link
Author

Choose a reason for hiding this comment

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

still, protecting from anything in multi-processor makes no sense (it's optional and only present when advanced config with multiple processors is used), if some processor is misbehaving - we should not hide it here. may be catch in span.End()

Copy link
Author

@lmolkova lmolkova Oct 20, 2019

Choose a reason for hiding this comment

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

i did exception handling in start/stop, but not her. We don't await WhenAny here and chances are it throws OperationCancelledException, so users will have to try/catch it anyway.

Copy link
Member

@SergeyKanzhelev SergeyKanzhelev left a comment

Choose a reason for hiding this comment

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

I think this design is great for filtering and enriching telemetry.

@pcwiese
Copy link
Contributor

pcwiese commented Oct 17, 2019

Is this going to be the recommended approach for Span filtering? e.g. if I don't want to export any spans for a health check endpoint would people put that logic in a filtering processor?

@lmolkova
Copy link
Author

lmolkova commented Oct 17, 2019

Is this going to be the recommended approach for Span filtering? e.g. if I don't want to export any spans for a health check endpoint would people put that logic in a filtering processor?

Nope.

But this is the only one available today. There are discussions: open-telemetry/opentelemetry-specification#300 and #251.

Basically

  • you filter by processor OR
  • you may give noop tracer to certain libraries, but this is not configurable today. OR
  • there are future hooks for incoming and outgoing requests filtering. They will happen extremely early so if you can filter there, the instrumentation will not even start (and this is .NET for certain libraries only):

https://github.com/open-telemetry/opentelemetry-dotnet/blob/master/src/OpenTelemetry.Collector.Dependencies/HttpClientCollectorOptions.cs#L42

https://github.com/open-telemetry/opentelemetry-dotnet/blob/master/src/OpenTelemetry.Collector.AspNetCore/AspNetCoreCollectorOptions.cs#L32

@SergeyKanzhelev
Copy link
Member

@lmolkova is the "draft" in title still means draft? Or we can merge it in?

@lmolkova
Copy link
Author

@SergeyKanzhelev still draft, need docs, tests, some clean up. I will probably finish it on the next week.

@lmolkova lmolkova changed the base branch from dontRequestExporterOnProcessor to master October 18, 2019 18:59
@lmolkova lmolkova changed the title [Draft] Multiple processors on tracer: configuration Multiple processors on tracer: configuration Oct 20, 2019
@lmolkova
Copy link
Author

Not draft anymore. Will keep it open for a few days to get more feedback.

@lmolkova
Copy link
Author

merging now. Feel free to add feedback here or file an issue if you are not happy with it.

@lmolkova lmolkova merged commit c6c87ab into open-telemetry:master Oct 22, 2019
@pcwiese
Copy link
Contributor

pcwiese commented Jan 27, 2020

@lmolkova
Whats the current status for filtering inbound requests using:

https://github.com/open-telemetry/opentelemetry-dotnet/blob/master/src/OpenTelemetry.Collector.AspNetCore/AspNetCoreCollectorOptions.cs#L32

I'd like to filter some things by specifying my own predicate here but I see this is still only internally available. Is there a reason for that?

@lmolkova
Copy link
Author

@pcwiese

I'd like to filter some things by specifying my own predicate here but I see this is still only internally available. Is there a reason for that?

The reason is filtering API is not very user friendly and very likely to change. Since this is rather advanced configuration and not the first priority (P1 is shipping OTel core packages in beta and GA), I decided to keep it internal until we have any cycles to understand requirements for this API and make it usable. Also, it seems there is no 'early' filtering concept available in OTel.

If you need it, I believe there are three ways to proceed:

  1. implement filtering with SpanProcessor. No changes to OTel required, but perf may be impacted.
  2. implement filtering with Sampler that filters based on attributes/span name. Should not require any OTel changes. Improves perf comparing to the 1.
  3. play around with filtering internal API, design it well and make it public. Gives least possible perf impact. Since there are similar filtering API for any Activity/DiagnosticSource instrumented library, I wanted to understand if there is anything common for different libraries. Perhaps there is nothing in common and each adapter can have it's own filter (or none).

@alanwest alanwest mentioned this pull request May 25, 2020
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement multi-processor registration on Tracer
3 participants