internal TracerProviderSdk( Resource resource, IEnumerable <string> sources, IEnumerable <TracerProviderBuilderSdk.DiagnosticSourceInstrumentationFactory> diagnosticSourceInstrumentationFactories, IEnumerable <TracerProviderBuilderSdk.InstrumentationFactory> instrumentationFactories, Sampler sampler, List <BaseProcessor <Activity> > processors) { this.Resource = resource; this.sampler = sampler; foreach (var processor in processors) { this.AddProcessor(processor); } if (diagnosticSourceInstrumentationFactories.Any()) { this.adapter = new ActivitySourceAdapter(sampler, this.processor); foreach (var instrumentationFactory in diagnosticSourceInstrumentationFactories) { this.instrumentations.Add(instrumentationFactory.Factory(this.adapter)); } } if (instrumentationFactories.Any()) { foreach (var instrumentationFactory in instrumentationFactories) { this.instrumentations.Add(instrumentationFactory.Factory()); } } var listener = new ActivityListener { // Callback when Activity is started. ActivityStarted = (activity) => { OpenTelemetrySdkEventSource.Log.ActivityStarted(activity); if (!activity.IsAllDataRequested) { return; } if (SuppressInstrumentationScope.IncrementIfTriggered() == 0) { this.processor?.OnStart(activity); } }, // Callback when Activity is stopped. ActivityStopped = (activity) => { OpenTelemetrySdkEventSource.Log.ActivityStopped(activity); if (!activity.IsAllDataRequested) { return; } if (SuppressInstrumentationScope.DecrementIfTriggered() == 0) { this.processor?.OnEnd(activity); } }, }; if (sampler is AlwaysOnSampler) { listener.Sample = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? ActivitySamplingResult.AllDataAndRecorded : ActivitySamplingResult.None; } else if (sampler is AlwaysOffSampler) { listener.Sample = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? PropagateOrIgnoreData(options.Parent.TraceId) : ActivitySamplingResult.None; } else { // This delegate informs ActivitySource about sampling decision when the parent context is an ActivityContext. listener.Sample = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? ComputeActivitySamplingResult(options, sampler) : ActivitySamplingResult.None; } if (sources.Any()) { // Sources can be null. This happens when user // is only interested in InstrumentationLibraries // which do not depend on ActivitySources. var wildcardMode = false; // Validation of source name is already done in builder. foreach (var name in sources) { if (name.Contains('*')) { wildcardMode = true; } } if (wildcardMode) { var pattern = "^(" + string.Join("|", from name in sources select '(' + Regex.Escape(name).Replace("\\*", ".*") + ')') + ")$"; var regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); // Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to // or not. listener.ShouldListenTo = (activitySource) => regex.IsMatch(activitySource.Name); } else { var activitySources = new Dictionary <string, bool>(StringComparer.OrdinalIgnoreCase); foreach (var name in sources) { activitySources[name] = true; } // Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to // or not. listener.ShouldListenTo = (activitySource) => activitySources.ContainsKey(activitySource.Name); } } ActivitySource.AddActivityListener(listener); this.listener = listener; }
/// <summary> /// Sets sampler. /// </summary> /// <param name="sampler">Sampler instance.</param> /// <returns>Returns <see cref="TracerProviderBuilder"/> for chaining.</returns> internal TracerProviderBuilder SetSampler(Sampler sampler) { this.sampler = sampler ?? throw new ArgumentNullException(nameof(sampler)); return(this); }
internal TracerProviderSdk( Resource resource, IEnumerable <string> sources, IEnumerable <TracerProviderBuilder.InstrumentationFactory> instrumentationFactories, Sampler sampler, List <ActivityProcessor> processors) { this.resource = resource; this.sampler = sampler; foreach (var processor in processors) { this.AddProcessor(processor); } if (instrumentationFactories.Any()) { this.adapter = new ActivitySourceAdapter(sampler, this.processor, resource); foreach (var instrumentationFactory in instrumentationFactories) { this.instrumentations.Add(instrumentationFactory.Factory(this.adapter)); } } var listener = new ActivityListener { // Callback when Activity is started. ActivityStarted = (activity) => { if (activity.IsAllDataRequested) { activity.SetResource(this.resource); this.processor?.OnStart(activity); } }, // Callback when Activity is stopped. ActivityStopped = (activity) => { if (activity.IsAllDataRequested) { this.processor?.OnEnd(activity); } }, // Setting this to true means TraceId will be always // available in sampling callbacks and will be the actual // traceid used, if activity ends up getting created. AutoGenerateRootContextTraceId = true, }; if (sampler is AlwaysOnSampler) { listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? ActivityDataRequest.AllDataAndRecorded : ActivityDataRequest.None; } else if (sampler is AlwaysOffSampler) { /*TODO: Change options.Parent.SpanId to options.Parent.TraceId * once AutoGenerateRootContextTraceId is removed.*/ listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? PropagateOrIgnoreData(options.Parent.SpanId) : ActivityDataRequest.None; } else { // This delegate informs ActivitySource about sampling decision when the parent context is an ActivityContext. listener.GetRequestedDataUsingContext = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? ComputeActivityDataRequest(options, sampler) : ActivityDataRequest.None; } if (sources.Any()) { // Sources can be null. This happens when user // is only interested in InstrumentationLibraries // which do not depend on ActivitySources. var wildcardMode = false; // Validation of source name is already done in builder. foreach (var name in sources) { if (name.Contains('*')) { wildcardMode = true; } } if (wildcardMode) { var pattern = "^(" + string.Join("|", from name in sources select '(' + Regex.Escape(name).Replace("\\*", ".*") + ')') + ")$"; var regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); // Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to // or not. listener.ShouldListenTo = (activitySource) => regex.IsMatch(activitySource.Name); } else { var activitySources = new Dictionary <string, bool>(StringComparer.OrdinalIgnoreCase); foreach (var name in sources) { activitySources[name] = true; } // Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to // or not. listener.ShouldListenTo = (activitySource) => activitySources.ContainsKey(activitySource.Name); } } ActivitySource.AddActivityListener(listener); this.listener = listener; }
internal TracerProviderSdk( Resource resource, IEnumerable <string> sources, IEnumerable <TracerProviderBuilderBase.InstrumentationFactory> instrumentationFactories, Sampler sampler, List <BaseProcessor <Activity> > processors, HashSet <string> legacyActivityOperationNames) { this.Resource = resource; this.sampler = sampler; this.supportLegacyActivity = legacyActivityOperationNames.Count > 0; bool legacyActivityWildcardMode = false; Regex legacyActivityWildcardModeRegex = null; foreach (var legacyName in legacyActivityOperationNames) { if (legacyName.Contains('*')) { legacyActivityWildcardMode = true; legacyActivityWildcardModeRegex = GetWildcardRegex(legacyActivityOperationNames); break; } } foreach (var processor in processors) { this.AddProcessor(processor); } if (instrumentationFactories.Any()) { foreach (var instrumentationFactory in instrumentationFactories) { this.instrumentations.Add(instrumentationFactory.Factory()); } } var listener = new ActivityListener(); if (this.supportLegacyActivity) { Func <Activity, bool> legacyActivityPredicate = null; if (legacyActivityWildcardMode) { legacyActivityPredicate = activity => legacyActivityWildcardModeRegex.IsMatch(activity.OperationName); } else { legacyActivityPredicate = activity => legacyActivityOperationNames.Contains(activity.OperationName); } listener.ActivityStarted = activity => { OpenTelemetrySdkEventSource.Log.ActivityStarted(activity); if (string.IsNullOrEmpty(activity.Source.Name)) { if (legacyActivityPredicate(activity)) { // Legacy activity matches the user configured list. // Call sampler for the legacy activity // unless suppressed. if (!Sdk.SuppressInstrumentation) { this.getRequestedDataAction(activity); } else { activity.IsAllDataRequested = false; } } else { // Legacy activity doesn't match the user configured list. No need to proceed further. return; } } if (!activity.IsAllDataRequested) { return; } if (SuppressInstrumentationScope.IncrementIfTriggered() == 0) { this.processor?.OnStart(activity); } }; listener.ActivityStopped = activity => { OpenTelemetrySdkEventSource.Log.ActivityStopped(activity); if (string.IsNullOrEmpty(activity.Source.Name) && !legacyActivityPredicate(activity)) { // Legacy activity doesn't match the user configured list. No need to proceed further. return; } if (!activity.IsAllDataRequested) { return; } // Spec says IsRecording must be false once span ends. // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#isrecording // However, Activity has slightly different semantic // than Span and we don't have strong reason to do this // now, as Activity anyway allows read/write always. // Intentionally commenting the following line. // activity.IsAllDataRequested = false; if (SuppressInstrumentationScope.DecrementIfTriggered() == 0) { this.processor?.OnEnd(activity); } }; } else { listener.ActivityStarted = activity => { OpenTelemetrySdkEventSource.Log.ActivityStarted(activity); if (activity.IsAllDataRequested && SuppressInstrumentationScope.IncrementIfTriggered() == 0) { this.processor?.OnStart(activity); } }; listener.ActivityStopped = activity => { OpenTelemetrySdkEventSource.Log.ActivityStopped(activity); if (!activity.IsAllDataRequested) { return; } // Spec says IsRecording must be false once span ends. // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#isrecording // However, Activity has slightly different semantic // than Span and we don't have strong reason to do this // now, as Activity anyway allows read/write always. // Intentionally commenting the following line. // activity.IsAllDataRequested = false; if (SuppressInstrumentationScope.DecrementIfTriggered() == 0) { this.processor?.OnEnd(activity); } }; } if (sampler is AlwaysOnSampler) { listener.Sample = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? ActivitySamplingResult.AllDataAndRecorded : ActivitySamplingResult.None; this.getRequestedDataAction = this.RunGetRequestedDataAlwaysOnSampler; } else if (sampler is AlwaysOffSampler) { listener.Sample = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? PropagateOrIgnoreData(options.Parent.TraceId) : ActivitySamplingResult.None; this.getRequestedDataAction = this.RunGetRequestedDataAlwaysOffSampler; } else { // This delegate informs ActivitySource about sampling decision when the parent context is an ActivityContext. listener.Sample = (ref ActivityCreationOptions <ActivityContext> options) => !Sdk.SuppressInstrumentation ? ComputeActivitySamplingResult(options, sampler) : ActivitySamplingResult.None; this.getRequestedDataAction = this.RunGetRequestedDataOtherSampler; } if (sources.Any()) { // Sources can be null. This happens when user // is only interested in InstrumentationLibraries // which do not depend on ActivitySources. var wildcardMode = false; // Validation of source name is already done in builder. foreach (var name in sources) { if (name.Contains('*')) { wildcardMode = true; break; } } if (wildcardMode) { var regex = GetWildcardRegex(sources); // Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to // or not. listener.ShouldListenTo = (activitySource) => this.supportLegacyActivity ? string.IsNullOrEmpty(activitySource.Name) || regex.IsMatch(activitySource.Name) : regex.IsMatch(activitySource.Name); } else { var activitySources = new HashSet <string>(sources, StringComparer.OrdinalIgnoreCase); if (this.supportLegacyActivity) { activitySources.Add(string.Empty); } // Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to // or not. listener.ShouldListenTo = (activitySource) => activitySources.Contains(activitySource.Name); } } else { if (this.supportLegacyActivity) { listener.ShouldListenTo = (activitySource) => string.IsNullOrEmpty(activitySource.Name); } } ActivitySource.AddActivityListener(listener); this.listener = listener; Regex GetWildcardRegex(IEnumerable <string> collection) { var pattern = '^' + string.Join("|", from name in collection select "(?:" + Regex.Escape(name).Replace("\\*", ".*") + ')') + '$'; return(new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase)); } }
/// <summary> /// Creates TracerProvider with the configuration provided. /// This sets up listeners for all configured ActivitySources and /// sends activities to the pipeline of Sampler, Processor and Exporter. /// </summary> /// <param name="configureTracerProviderBuilder">Action to configure TracerProviderBuilder.</param> /// <returns>TracerProvider instance, which must be disposed upon shutdown.</returns> public static TracerProvider CreateTracerProvider(Action <TracerProviderBuilder> configureTracerProviderBuilder) { var tracerProviderBuilder = new TracerProviderBuilder(); configureTracerProviderBuilder?.Invoke(tracerProviderBuilder); var tracerProviderSdk = new TracerProviderSdk(); Sampler sampler = tracerProviderBuilder.Sampler ?? new AlwaysOnSampler(); ActivityProcessor activityProcessor; if (tracerProviderBuilder.ProcessingPipelines == null || !tracerProviderBuilder.ProcessingPipelines.Any()) { // if there are no pipelines are configured, use noop processor activityProcessor = new NoopActivityProcessor(); } else if (tracerProviderBuilder.ProcessingPipelines.Count == 1) { // if there is only one pipeline - use it's outer processor as a // single processor on the tracerSdk. var processorFactory = tracerProviderBuilder.ProcessingPipelines[0]; activityProcessor = processorFactory.Build(); } else { // if there are more pipelines, use processor that will broadcast to all pipelines var processors = new ActivityProcessor[tracerProviderBuilder.ProcessingPipelines.Count]; for (int i = 0; i < tracerProviderBuilder.ProcessingPipelines.Count; i++) { processors[i] = tracerProviderBuilder.ProcessingPipelines[i].Build(); } activityProcessor = new BroadcastActivityProcessor(processors); } tracerProviderSdk.Resource = tracerProviderBuilder.Resource; var activitySource = new ActivitySourceAdapter(sampler, activityProcessor, tracerProviderSdk.Resource); if (tracerProviderBuilder.InstrumentationFactories != null) { foreach (var instrumentation in tracerProviderBuilder.InstrumentationFactories) { tracerProviderSdk.Instrumentations.Add(instrumentation.Factory(activitySource)); } } // This is what subscribes to Activities. // Think of this as the replacement for DiagnosticListener.AllListeners.Subscribe(onNext => diagnosticListener.Subscribe(..)); tracerProviderSdk.ActivityListener = new ActivityListener { // Callback when Activity is started. ActivityStarted = (activity) => { if (activity.IsAllDataRequested) { activity.SetResource(tracerProviderSdk.Resource); } activityProcessor.OnStart(activity); }, // Callback when Activity is stopped. ActivityStopped = activityProcessor.OnEnd, // Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to // or not ShouldListenTo = (activitySource) => tracerProviderBuilder.ActivitySourceNames?.Contains(activitySource.Name.ToUpperInvariant()) ?? false, // The following parameter is not used now. GetRequestedDataUsingParentId = (ref ActivityCreationOptions <string> options) => ActivityDataRequest.AllData, // This delegate informs ActivitySource about sampling decision when the parent context is an ActivityContext. GetRequestedDataUsingContext = (ref ActivityCreationOptions <ActivityContext> options) => ComputeActivityDataRequest(options, sampler), }; ActivitySource.AddActivityListener(tracerProviderSdk.ActivityListener); tracerProviderSdk.ActivityProcessor = activityProcessor; return(tracerProviderSdk); }
/// <summary> /// Sets sampler. /// </summary> /// <param name="tracerProviderBuilder">TracerProviderBuilder instance.</param> /// <param name="sampler">Sampler instance.</param> /// <returns>Returns <see cref="TracerProviderBuilder"/> for chaining.</returns> public static TracerProviderBuilder SetSampler(this TracerProviderBuilder tracerProviderBuilder, Sampler sampler) { if (tracerProviderBuilder is TracerProviderBuilderSdk tracerProviderBuilderSdk) { tracerProviderBuilderSdk.SetSampler(sampler); } return(tracerProviderBuilder); }
private Span( string name, SpanContext parentSpanContext, ActivityAndTracestate activityAndTracestate, bool createdFromActivity, SpanKind spanKind, SpanCreationOptions spanCreationOptions, Sampler sampler, TracerConfiguration tracerConfiguration, SpanProcessor spanProcessor, Resource libraryResource) { this.Name = name; this.LibraryResource = libraryResource; IEnumerable <Link> links = null; if (spanCreationOptions != null) { links = spanCreationOptions.Links ?? spanCreationOptions.LinksFactory?.Invoke(); this.startTimestamp = spanCreationOptions.StartTimestamp; } if (this.startTimestamp == default) { this.startTimestamp = PreciseTimestamp.GetUtcNow(); } this.sampler = sampler; this.tracerConfiguration = tracerConfiguration; this.spanProcessor = spanProcessor; this.Kind = spanKind; this.createdFromActivity = createdFromActivity; this.Activity = activityAndTracestate.Activity; var tracestate = activityAndTracestate.Tracestate; this.IsRecording = MakeSamplingDecision( parentSpanContext, name, spanCreationOptions?.Attributes, links, // we'll enumerate again, but double enumeration over small collection is cheaper than allocation this.Activity.TraceId, this.Activity.SpanId, this.sampler); this.Activity.ActivityTraceFlags = this.IsRecording ? this.Activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded : this.Activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; // this context is definitely not remote, setting isRemote to false this.Context = new SpanContext(this.Activity.TraceId, this.Activity.SpanId, this.Activity.ActivityTraceFlags, false, tracestate); if (this.IsRecording) { this.SetLinks(links); if (spanCreationOptions?.Attributes != null) { foreach (var attribute in spanCreationOptions.Attributes) { this.SetAttribute(attribute); } } this.spanProcessor.OnStart(this); } }
#pragma warning restore CA2000 // Dispose objects before losing scope /// <summary> /// Adds a <see cref="Sampler"/> which will automatically sample any span under a traceId registered through <see cref="ActivityTraceListenerManager"/>. /// </summary> /// <param name="tracerProviderBuilder"><see cref="TracerProviderBuilder"/>.</param> /// <param name="innerSampler"><see cref="Sampler"/>.</param> /// <returns>Returns the supplied <see cref="TracerProviderBuilder"/> for chaining.</returns> public static TracerProviderBuilder SetActivityTraceListenerSampler(this TracerProviderBuilder tracerProviderBuilder, Sampler innerSampler) => tracerProviderBuilder.SetSampler(new ActivityTraceListenerSampler(innerSampler));
/// <summary> /// Initializes a new instance of the <see cref="ParentBasedSampler"/> class. /// </summary> /// <param name="delegateSampler">The <see cref="Sampler"/> to be called to decide whether or not to sample a root trace.</param> public ParentBasedSampler(Sampler delegateSampler) { this.delegateSampler = delegateSampler ?? throw new ArgumentNullException(nameof(delegateSampler)); this.Description = $"ParentBased{{{delegateSampler.Description}}}"; }