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;
        }
예제 #2
0
 /// <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);
 }
예제 #3
0
        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));
            }
        }
예제 #5
0
        /// <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);
        }
예제 #6
0
        /// <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);
        }
예제 #7
0
        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}}}";
        }