コード例 #1
0
        public async Task ActivityIdIsStampedOnRequest()
        {
            using var testListener = new TestDiagnosticListener("Azure.Core");

            ActivityIdFormat previousFormat = Activity.DefaultIdFormat;

            Activity.DefaultIdFormat = ActivityIdFormat.W3C;
            try
            {
                Activity activity = null;

                MockTransport mockTransport = CreateMockTransport(_ =>
                {
                    activity = Activity.Current;
                    return(new MockResponse(201));
                });

                Task <Response> requestTask = SendRequestAsync(mockTransport, request =>
                {
                    request.Method = RequestMethod.Get;
                    request.Uri.Reset(new Uri("http://example.com"));
                }, s_enabledPolicy);

                await requestTask;

                Assert.True(mockTransport.SingleRequest.TryGetHeader("traceparent", out string requestId));
                Assert.AreEqual(activity.Id, requestId);
            }
            finally
            {
                Activity.DefaultIdFormat = previousFormat;
            }
        }
コード例 #2
0
        public async Task CurrentActivityIsInjectedIntoRequestW3C()
        {
            ActivityIdFormat previousFormat = Activity.DefaultIdFormat;

            Activity.DefaultIdFormat = ActivityIdFormat.W3C;
            try
            {
                var transport = new MockTransport(new MockResponse(200));

                var activity = new Activity("Dummy");

                activity.Start();
                activity.TraceStateString = "trace";

                await SendGetRequest(transport, s_enabledPolicy);

                activity.Stop();

                Assert.True(transport.SingleRequest.TryGetHeader("traceparent", out string requestId));
                Assert.AreEqual(activity.Id, requestId);

                Assert.True(transport.SingleRequest.TryGetHeader("tracestate", out string traceState));
                Assert.AreEqual("trace", traceState);
            }
            finally
            {
                Activity.DefaultIdFormat = previousFormat;
            }
        }
コード例 #3
0
        /// <summary>
        /// Construct a new <see cref="ActivityCreationOptions{T}"/> object.
        /// </summary>
        /// <param name="source">The trace Activity source<see cref="ActivitySource"/> used to request creating the Activity object.</param>
        /// <param name="name">The operation name of the Activity.</param>
        /// <param name="parent">The requested parent to create the Activity object with. The parent either be a parent Id represented as string or it can be a parent context <see cref="ActivityContext"/>.</param>
        /// <param name="kind"><see cref="ActivityKind"/> to create the Activity object with.</param>
        /// <param name="tags">Key-value pairs list for the tags to create the Activity object with.<see cref="ActivityContext"/></param>
        /// <param name="links"><see cref="ActivityLink"/> list to create the Activity object with.</param>
        /// <param name="idFormat">The default Id format to use.</param>
        internal ActivityCreationOptions(ActivitySource source, string name, T parent, ActivityKind kind, IEnumerable <KeyValuePair <string, object?> >?tags, IEnumerable <ActivityLink>?links, ActivityIdFormat idFormat)
        {
            Source   = source;
            Name     = name;
            Kind     = kind;
            Parent   = parent;
            Tags     = tags;
            Links    = links;
            IdFormat = idFormat;

            if (IdFormat == ActivityIdFormat.Unknown && Activity.ForceDefaultIdFormat)
            {
                IdFormat = Activity.DefaultIdFormat;
            }

            _samplerTags = null;

            if (parent is ActivityContext ac && ac != default)
            {
                _context = ac;
                if (IdFormat == ActivityIdFormat.Unknown)
                {
                    IdFormat = ActivityIdFormat.W3C;
                }
            }
コード例 #4
0
        public async Task HttpActivityNeverSuppressed()
        {
            using var _ = SetAppConfigSwitch();

            ActivityIdFormat previousFormat = Activity.DefaultIdFormat;

            Activity.DefaultIdFormat = ActivityIdFormat.W3C;

            using var clientListener = new TestActivitySourceListener("Azure.Clients.ClientName");
            DiagnosticScopeFactory clientDiagnostics = new DiagnosticScopeFactory("Azure.Clients", "Microsoft.Azure.Core.Cool.Tests", true, true);

            using DiagnosticScope outerScope = clientDiagnostics.CreateScope("ClientName.ActivityName", DiagnosticScope.ActivityKind.Internal);
            outerScope.Start();

            try
            {
                using var testListener = new TestActivitySourceListener("Azure.Core.Http");

                MockTransport mockTransport = CreateMockTransport(_ => new MockResponse(201));

                Task <Response> requestTask = SendRequestAsync(mockTransport, request =>
                {
                    request.Method = RequestMethod.Get;
                }, s_enabledPolicy);

                await requestTask;

                Assert.AreEqual(1, testListener.Activities.Count);
                CollectionAssert.Contains(testListener.Activities.Single().Tags, new KeyValuePair <string, string>("http.status_code", "201"));
            }
            finally
            {
                Activity.DefaultIdFormat = previousFormat;
            }
        }
コード例 #5
0
    private static void ValidateActivities(ActivityIdFormat idFormat, Activity client, HeaderDictionary proxy, HeaderDictionary downstream)
    {
        var baggage = string.Join(", ", client.Baggage.Select(pair => $"{pair.Key}={pair.Value}"));

        Assert.Equal(baggage, proxy[Baggage]);
        Assert.Equal(baggage, downstream[Baggage]);

        if (idFormat == ActivityIdFormat.W3C)
        {
            Assert.True(ActivityContext.TryParse(proxy[TraceParent], proxy[TraceState], out var proxyContext));
            Assert.True(ActivityContext.TryParse(downstream[TraceParent], downstream[TraceState], out var downstreamContext));
            Assert.Equal(client.TraceStateString, proxyContext.TraceState);
            Assert.Equal(client.TraceStateString, downstreamContext.TraceState);
            var proxyTraceId      = proxyContext.TraceId.ToHexString();
            var proxySpanId       = proxyContext.SpanId.ToHexString();
            var downstreamTraceId = downstreamContext.TraceId.ToHexString();
            var downstreamSpanId  = downstreamContext.SpanId.ToHexString();
            Assert.Equal(client.TraceId.ToHexString(), proxyTraceId);
            Assert.Equal(client.TraceId.ToHexString(), downstreamTraceId);
            Assert.NotEqual(proxySpanId, downstreamSpanId);
        }
        else
        {
            var proxyId      = proxy[RequestId].ToString();
            var downstreamId = downstream[RequestId].ToString();
            Assert.StartsWith(client.Id, proxyId);
            Assert.StartsWith(proxyId, downstreamId);
            Assert.NotEqual(proxyId, downstreamId);
        }
    }
コード例 #6
0
        private bool IsRequestInstrumented(HttpWebRequest request)
        {
            ActivityIdFormat Format = Activity.ForceDefaultIdFormat
                ? Activity.DefaultIdFormat
                : (Activity.Current?.IdFormat ?? Activity.DefaultIdFormat);

            return(Format == ActivityIdFormat.W3C
                ? request.Headers.Get(TraceParentHeaderName) != null
                : request.Headers.Get(RequestIdHeaderName) != null);
        }
コード例 #7
0
    private static void ValidateActivities(ActivityIdFormat idFormat, Activity client, HeaderDictionary proxy, HeaderDictionary downstream)
    {
        var baggage = string.Join(", ", client.Baggage.Select(pair => $"{pair.Key}={pair.Value}"));

        Assert.Equal(baggage, proxy[Baggage]);
        Assert.Equal(baggage, downstream[Baggage]);

        if (idFormat == ActivityIdFormat.W3C)
        {
#if NET
            Assert.True(ActivityContext.TryParse(proxy[TraceParent], proxy[TraceState], out var proxyContext));
            Assert.True(ActivityContext.TryParse(downstream[TraceParent], downstream[TraceState], out var downstreamContext));
            Assert.Equal(client.TraceStateString, proxyContext.TraceState);
            Assert.Equal(client.TraceStateString, downstreamContext.TraceState);
            var proxyTraceId      = proxyContext.TraceId.ToHexString();
            var proxySpanId       = proxyContext.SpanId.ToHexString();
            var downstreamTraceId = downstreamContext.TraceId.ToHexString();
            var downstreamSpanId  = downstreamContext.SpanId.ToHexString();
#else
            // 3.1 does not have ActivityContext
            Assert.Equal(client.TraceStateString, proxy[TraceState]);
            Assert.Equal(client.TraceStateString, downstream[TraceState]);
            var proxyTraceId      = proxy[TraceParent].ToString().Split('-')[1];
            var proxySpanId       = proxy[TraceParent].ToString().Split('-')[2];
            var downstreamTraceId = downstream[TraceParent].ToString().Split('-')[1];
            var downstreamSpanId  = downstream[TraceParent].ToString().Split('-')[2];
#endif

            Assert.Equal(client.TraceId.ToHexString(), proxyTraceId);
            Assert.Equal(client.TraceId.ToHexString(), downstreamTraceId);

#if NET6_0_OR_GREATER
            Assert.NotEqual(proxySpanId, downstreamSpanId);
#else
            // Before 6.0, YARP is just pass-through as far as distributed tracing is concerned
            Assert.Equal(proxySpanId, downstreamSpanId);
#endif
        }
        else
        {
            var proxyId      = proxy[RequestId].ToString();
            var downstreamId = downstream[RequestId].ToString();

            Assert.StartsWith(client.Id, proxyId);
            Assert.StartsWith(proxyId, downstreamId);

#if NET6_0_OR_GREATER
            Assert.NotEqual(proxyId, downstreamId);
#else
            // Before 6.0, YARP is just pass-through as far as distributed tracing is concerned
            Assert.Equal(proxyId, downstreamId);
#endif
        }
    }
コード例 #8
0
    public async Task DistributedTracing_Works(ActivityIdFormat idFormat)
    {
        var proxyHeaders      = new HeaderDictionary();
        var downstreamHeaders = new HeaderDictionary();

        var test = new TestEnvironment(
            async context =>
        {
            foreach (var header in context.Request.Headers)
            {
                downstreamHeaders.Add(header);
            }
            await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("Hello"));
        },
            proxyBuilder => { },
            proxyApp =>
        {
            proxyApp.Use(next => context =>
            {
                foreach (var header in context.Request.Headers)
                {
                    proxyHeaders.Add(header);
                }
                return(next(context));
            });
        });

        var clientActivity = new Activity("Foo");

        clientActivity.SetIdFormat(idFormat);
        clientActivity.TraceStateString = "Bar";
        clientActivity.AddBaggage("One", "1");
        clientActivity.AddBaggage("Two", "2");
        clientActivity.Start();

        await test.Invoke(async uri =>
        {
            using var client = new HttpClient();
            Assert.Equal("Hello", await client.GetStringAsync(uri));
        });

        Assert.NotEmpty(proxyHeaders);
        Assert.NotEmpty(downstreamHeaders);

        ValidateActivities(idFormat, clientActivity, proxyHeaders, downstreamHeaders);
    }
コード例 #9
0
        public async Task ActivitySourceActivityStartedOnRequest()
        {
            using var _ = SetAppConfigSwitch();

            ActivityIdFormat previousFormat = Activity.DefaultIdFormat;

            Activity.DefaultIdFormat = ActivityIdFormat.W3C;
            try
            {
                Activity activity = null;
                using var testListener = new TestActivitySourceListener("Azure.Core.Http");

                MockTransport mockTransport = CreateMockTransport(_ =>
                {
                    activity = Activity.Current;
                    MockResponse mockResponse = new MockResponse(201);
                    mockResponse.AddHeader(new HttpHeader("x-ms-request-id", "server request id"));
                    return(mockResponse);
                });

                string          clientRequestId = null;
                Task <Response> requestTask     = SendRequestAsync(mockTransport, request =>
                {
                    request.Method = RequestMethod.Get;
                    request.Uri.Reset(new Uri("http://example.com"));
                    request.Headers.Add("User-Agent", "agent");
                    clientRequestId = request.ClientRequestId;
                }, s_enabledPolicy);

                await requestTask;

                Assert.AreEqual(activity, testListener.Activities.Single());
                CollectionAssert.Contains(activity.Tags, new KeyValuePair <string, string>("http.status_code", "201"));
                CollectionAssert.Contains(activity.Tags, new KeyValuePair <string, string>("http.url", "http://example.com/"));
                CollectionAssert.Contains(activity.Tags, new KeyValuePair <string, string>("http.method", "GET"));
                CollectionAssert.Contains(activity.Tags, new KeyValuePair <string, string>("http.user_agent", "agent"));
                CollectionAssert.Contains(activity.Tags, new KeyValuePair <string, string>("requestId", clientRequestId));
                CollectionAssert.Contains(activity.Tags, new KeyValuePair <string, string>("serviceRequestId", "server request id"));
                CollectionAssert.Contains(activity.Tags, new KeyValuePair <string, string>("az.namespace", "Microsoft.Azure.Core.Cool.Tests"));
            }
            finally
            {
                Activity.DefaultIdFormat = previousFormat;
            }
        }
コード例 #10
0
        private static bool TryCreateActivity(IDictionary <string, object>[] arguments, [MaybeNullWhen(false)] out ActivityItem item)
        {
            string?          activityId    = null;
            string?          operationName = null;
            string?          spanId        = null;
            string?          parentSpanId  = null;
            string?          traceId       = null;
            DateTime         startTime     = default;
            ActivityIdFormat idFormat      = default;

            foreach (var arg in arguments)
            {
                var key   = (string)arg["Key"];
                var value = (string)arg["Value"];

                if (key == "ActivityId")
                {
                    activityId = value;
                }
                else if (key == "ActivityOperationName")
                {
                    operationName = value;
                }
                else if (key == "ActivitySpanId")
                {
                    spanId = value;
                }
                else if (key == "ActivityTraceId")
                {
                    traceId = value;
                }
                else if (key == "ActivityParentSpanId")
                {
                    parentSpanId = value;
                }
                else if (key == "ActivityStartTime")
                {
                    startTime = new DateTime(long.Parse(value), DateTimeKind.Utc);
                }
                else if (key == "ActivityIdFormat")
                {
                    idFormat = Enum.Parse <ActivityIdFormat>(value);
                }
            }

            if (string.IsNullOrEmpty(activityId))
            {
                // Not a 3.1 application (we can detect this earlier)
                item = null !;
                return(false);
            }

            if (idFormat == ActivityIdFormat.Hierarchical)
            {
                // We need W3C to make it work
                item = null !;
                return(false);
            }

            // This is what open telemetry currently does
            // https://github.com/open-telemetry/opentelemetry-dotnet/blob/4ba732af062ddc2759c02aebbc91335aaa3f7173/src/OpenTelemetry.Collector.AspNetCore/Implementation/HttpInListener.cs#L65-L92

            item = new ActivityItem(activityId)
            {
                Name                                 = operationName,
                SpanId                               = ActivitySpanId.CreateFromString(spanId),
                TraceId                              = ActivityTraceId.CreateFromString(traceId),
                ParentSpanId                         = parentSpanId == "0000000000000000" ? default : ActivitySpanId.CreateFromString(parentSpanId),
                                           StartTime = startTime,
            };

            return(true);
        }
コード例 #11
0
 public override void Before(MethodInfo methodUnderTest) => _originalFormat = Activity.DefaultIdFormat;
コード例 #12
0
        public void ProcessEvents(string applicationName,
                                  string serviceName,
                                  int processId,
                                  string replicaName,
                                  ReplicaStatus replica,
                                  CancellationToken cancellationToken)
        {
            var hasEventPipe = false;

            for (int i = 0; i < 10; ++i)
            {
                if (DiagnosticsClient.GetPublishedProcesses().Contains(processId))
                {
                    hasEventPipe = true;
                    break;
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                Thread.Sleep(500);
            }

            if (!hasEventPipe)
            {
                _logger.LogInformation("Process id {PID}, does not support event pipe", processId);
                return;
            }

            _logger.LogInformation("Listening for event pipe events for {ServiceName} on process id {PID}", replicaName, processId);

            // Create the logger factory for this replica
            using var loggerFactory = LoggerFactory.Create(builder => ConfigureLogging(serviceName, replicaName, builder));

            var processor = new SimpleSpanProcessor(CreateSpanExporter(serviceName, replicaName));

            var providers = new List <EventPipeProvider>()
            {
                // Runtime Metrics
                new EventPipeProvider(
                    SystemRuntimeEventSourceName,
                    EventLevel.Informational,
                    (long)ClrTraceEventParser.Keywords.None,
                    new Dictionary <string, string>()
                {
                    { "EventCounterIntervalSec", "1" }
                }
                    ),
                new EventPipeProvider(
                    MicrosoftAspNetCoreHostingEventSourceName,
                    EventLevel.Informational,
                    (long)ClrTraceEventParser.Keywords.None,
                    new Dictionary <string, string>()
                {
                    { "EventCounterIntervalSec", "1" }
                }
                    ),
                new EventPipeProvider(
                    GrpcAspNetCoreServer,
                    EventLevel.Informational,
                    (long)ClrTraceEventParser.Keywords.None,
                    new Dictionary <string, string>()
                {
                    { "EventCounterIntervalSec", "1" }
                }
                    ),

                // Application Metrics
                new EventPipeProvider(
                    applicationName,
                    EventLevel.Informational,
                    (long)ClrTraceEventParser.Keywords.None,
                    new Dictionary <string, string>()
                {
                    { "EventCounterIntervalSec", "1" }
                }
                    ),

                // Logging
                new EventPipeProvider(
                    MicrosoftExtensionsLoggingProviderName,
                    EventLevel.LogAlways,
                    (long)(LoggingEventSource.Keywords.JsonMessage | LoggingEventSource.Keywords.FormattedMessage)
                    ),

                // Distributed Tracing

                // Activity correlation
                new EventPipeProvider(TplEventSource,
                                      keywords: 0x80,
                                      eventLevel: EventLevel.LogAlways),

                // Diagnostic source events
                new EventPipeProvider(DiagnosticSourceEventSource,
                                      keywords: 0x1 | 0x2,
                                      eventLevel: EventLevel.Verbose,
                                      arguments: new Dictionary <string, string>
                {
                    { "FilterAndPayloadSpecs", DiagnosticFilterString }
                })
            };

            while (!cancellationToken.IsCancellationRequested)
            {
                EventPipeSession session = null;
                var client = new DiagnosticsClient(processId);

                try
                {
                    session = client.StartEventPipeSession(providers);
                }
                catch (EndOfStreamException)
                {
                    break;
                }
                catch (Exception ex)
                {
                    if (!cancellationToken.IsCancellationRequested)
                    {
                        _logger.LogDebug(0, ex, "Failed to start the event pipe session");
                    }

                    // We can't even start the session, wait until the process boots up again to start another metrics thread
                    break;
                }

                void StopSession()
                {
                    try
                    {
                        session.Stop();
                    }
                    catch (EndOfStreamException)
                    {
                        // If the app we're monitoring exits abruptly, this may throw in which case we just swallow the exception and exit gracefully.
                    }
                    // We may time out if the process ended before we sent StopTracing command. We can just exit in that case.
                    catch (TimeoutException)
                    {
                    }
                    // On Unix platforms, we may actually get a PNSE since the pipe is gone with the process, and Runtime Client Library
                    // does not know how to distinguish a situation where there is no pipe to begin with, or where the process has exited
                    // before dotnet-counters and got rid of a pipe that once existed.
                    // Since we are catching this in StopMonitor() we know that the pipe once existed (otherwise the exception would've
                    // been thrown in StartMonitor directly)
                    catch (PlatformNotSupportedException)
                    {
                    }
                }

                using var _ = cancellationToken.Register(() => StopSession());

                try
                {
                    var source     = new EventPipeEventSource(session.EventStream);
                    var activities = new Dictionary <string, ActivityItem>();

                    source.Dynamic.All += traceEvent =>
                    {
                        try
                        {
                            // Uncomment to debug the diagnostics source event source
                            //if (traceEvent.EventName == "Message")
                            //{
                            //    _logger.LogTrace("[" + replicaName + "]:" + traceEvent.PayloadValue(0));
                            //}
                            //// Distributed tracing
                            // else
                            if (traceEvent.EventName == "Activity1Start/Start")
                            {
                                var listenerEventName = (string)traceEvent.PayloadByName("EventName");

                                if (traceEvent.PayloadByName("Arguments") is IDictionary <string, object>[] arguments)
                                {
                                    string           activityId    = null;
                                    string           parentId      = null;
                                    string           operationName = null;
                                    string           httpMethod    = null;
                                    string           path          = null;
                                    string           spanId        = null;
                                    string           parentSpanId  = null;
                                    string           traceId       = null;
                                    DateTime         startTime     = default;
                                    ActivityIdFormat idFormat      = default;

                                    foreach (var arg in arguments)
                                    {
                                        var key   = (string)arg["Key"];
                                        var value = (string)arg["Value"];

                                        if (key == "ActivityId")
                                        {
                                            activityId = value;
                                        }
                                        else if (key == "ActivityParentId")
                                        {
                                            parentId = value;
                                        }
                                        else if (key == "ActivityOperationName")
                                        {
                                            operationName = value;
                                        }
                                        else if (key == "ActivitySpanId")
                                        {
                                            spanId = value;
                                        }
                                        else if (key == "ActivityTraceId")
                                        {
                                            traceId = value;
                                        }
                                        else if (key == "ActivityParentSpanId")
                                        {
                                            parentSpanId = value;
                                        }
                                        else if (key == "Method")
                                        {
                                            httpMethod = value;
                                        }
                                        else if (key == "Path")
                                        {
                                            path = value;
                                        }
                                        else if (key == "ActivityStartTime")
                                        {
                                            startTime = new DateTime(long.Parse(value), DateTimeKind.Utc);
                                        }
                                        else if (key == "ActivityIdFormat")
                                        {
                                            idFormat = Enum.Parse <ActivityIdFormat>(value);
                                        }
                                    }

                                    if (string.IsNullOrEmpty(activityId))
                                    {
                                        // Not a 3.1 application (we can detect this earlier)
                                        return;
                                    }

                                    if (idFormat == ActivityIdFormat.Hierarchical)
                                    {
                                        // We need W3C to make it work
                                        return;
                                    }

                                    // This is what open telemetry currently does
                                    // https://github.com/open-telemetry/opentelemetry-dotnet/blob/4ba732af062ddc2759c02aebbc91335aaa3f7173/src/OpenTelemetry.Collector.AspNetCore/Implementation/HttpInListener.cs#L65-L92

                                    var item = new ActivityItem()
                                    {
                                        Name                                 = path,
                                        SpanId                               = ActivitySpanId.CreateFromString(spanId),
                                        TraceId                              = ActivityTraceId.CreateFromString(traceId),
                                        ParentSpanId                         = parentSpanId == "0000000000000000" ? default : ActivitySpanId.CreateFromString(parentSpanId),
                                                                   StartTime = startTime,
                                    };

                                    item.Attributes[SpanAttributeConstants.HttpMethodKey] = httpMethod;
                                    item.Attributes[SpanAttributeConstants.HttpPathKey]   = path;

                                    activities[activityId] = item;
                                }
                            }
                            else if (traceEvent.EventName == "Activity1Stop/Stop")
                            {
                                var listenerEventName = (string)traceEvent.PayloadByName("EventName");

                                if (traceEvent.PayloadByName("Arguments") is IDictionary <string, object>[] arguments)
                                {
                                    string   activityId = null;
                                    TimeSpan duration   = default;
                                    int      statusCode = 0;

                                    foreach (var arg in arguments)
                                    {
                                        var key   = (string)arg["Key"];
                                        var value = (string)arg["Value"];

                                        if (key == "ActivityId")
                                        {
                                            activityId = value;
                                        }
                                        else if (key == "StatusCode")
                                        {
                                            statusCode = int.Parse(value);
                                        }
                                        else if (key == "ActivityDuration")
                                        {
                                            duration = new TimeSpan(long.Parse(value));
                                        }
                                    }

                                    if (string.IsNullOrEmpty(activityId))
                                    {
                                        // Not a 3.1 application (we can detect this earlier)
                                        return;
                                    }

                                    if (activities.TryGetValue(activityId, out var item))
                                    {
                                        item.Attributes[SpanAttributeConstants.HttpStatusCodeKey] = statusCode;

                                        item.EndTime = item.StartTime + duration;

                                        var spanData = new SpanData(item.Name,
                                                                    new SpanContext(item.TraceId, item.SpanId, ActivityTraceFlags.Recorded),
                                                                    item.ParentSpanId,
                                                                    SpanKind.Server,
                                                                    item.StartTime,
                                                                    item.Attributes,
                                                                    Enumerable.Empty <Event>(),
                                                                    Enumerable.Empty <Link>(),
                                                                    null,
                                                                    Status.Ok,
                                                                    item.EndTime);

                                        processor.OnEnd(spanData);

                                        activities.Remove(activityId);
                                    }
                                }
                            }
                            else if (traceEvent.EventName == "Activity2Start/Start")
                            {
                                var listenerEventName = (string)traceEvent.PayloadByName("EventName");

                                _logger.LogDebug("[" + replicaName + "]: " + listenerEventName + " fired");
                            }
                            else if (traceEvent.EventName == "Activity2Stop/Stop")
                            {
                                var listenerEventName = (string)traceEvent.PayloadByName("EventName");

                                _logger.LogDebug("[" + replicaName + "]: " + listenerEventName + " fired");
                            }

                            // Metrics
                            else if (traceEvent.EventName.Equals("EventCounters"))
                            {
                                var payloadVal   = (IDictionary <string, object>)traceEvent.PayloadValue(0);
                                var eventPayload = (IDictionary <string, object>)payloadVal["Payload"];

                                ICounterPayload payload = CounterPayload.FromPayload(eventPayload);

                                replica.Metrics[traceEvent.ProviderName + "/" + payload.Name] = payload.Value;
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "Error processing counter for {ProviderName}:{EventName}", traceEvent.ProviderName, traceEvent.EventName);
                        }
                    };

                    // Logging
                    string lastFormattedMessage = "";

                    var logActivities = new Dictionary <Guid, LogActivityItem>();
                    var stack         = new Stack <Guid>();

                    source.Dynamic.AddCallbackForProviderEvent(MicrosoftExtensionsLoggingProviderName, "ActivityJsonStart/Start", (traceEvent) =>
                    {
                        var factoryId    = (int)traceEvent.PayloadByName("FactoryID");
                        var categoryName = (string)traceEvent.PayloadByName("LoggerName");
                        var argsJson     = (string)traceEvent.PayloadByName("ArgumentsJson");

                        // TODO: Store this information by logger factory id
                        var item = new LogActivityItem
                        {
                            ActivityID   = traceEvent.ActivityID,
                            ScopedObject = new LogObject(JsonDocument.Parse(argsJson).RootElement),
                        };

                        if (stack.TryPeek(out var parentId) && logActivities.TryGetValue(parentId, out var parentItem))
                        {
                            item.Parent = parentItem;
                        }

                        stack.Push(traceEvent.ActivityID);
                        logActivities[traceEvent.ActivityID] = item;
                    });

                    source.Dynamic.AddCallbackForProviderEvent(MicrosoftExtensionsLoggingProviderName, "ActivityJsonStop/Stop", (traceEvent) =>
                    {
                        var factoryId    = (int)traceEvent.PayloadByName("FactoryID");
                        var categoryName = (string)traceEvent.PayloadByName("LoggerName");

                        stack.Pop();
                        logActivities.Remove(traceEvent.ActivityID);
                    });

                    source.Dynamic.AddCallbackForProviderEvent(MicrosoftExtensionsLoggingProviderName, "MessageJson", (traceEvent) =>
                    {
                        // Level, FactoryID, LoggerName, EventID, EventName, ExceptionJson, ArgumentsJson
                        var logLevel      = (LogLevel)traceEvent.PayloadByName("Level");
                        var factoryId     = (int)traceEvent.PayloadByName("FactoryID");
                        var categoryName  = (string)traceEvent.PayloadByName("LoggerName");
                        var eventId       = (int)traceEvent.PayloadByName("EventId");
                        var eventName     = (string)traceEvent.PayloadByName("EventName");
                        var exceptionJson = (string)traceEvent.PayloadByName("ExceptionJson");
                        var argsJson      = (string)traceEvent.PayloadByName("ArgumentsJson");

                        // There's a bug that causes some of the columns to get mixed up
                        if (eventName.StartsWith("{"))
                        {
                            argsJson      = exceptionJson;
                            exceptionJson = eventName;
                            eventName     = null;
                        }

                        if (string.IsNullOrEmpty(argsJson))
                        {
                            return;
                        }

                        Exception exception = null;

                        var logger = loggerFactory.CreateLogger(categoryName);

                        var scopes = new List <IDisposable>();

                        if (logActivities.TryGetValue(traceEvent.ActivityID, out var logActivityItem))
                        {
                            // REVIEW: Does order matter here? We're combining everything anyways.
                            while (logActivityItem != null)
                            {
                                scopes.Add(logger.BeginScope(logActivityItem.ScopedObject));

                                logActivityItem = logActivityItem.Parent;
                            }
                        }

                        try
                        {
                            if (exceptionJson != "{}")
                            {
                                var exceptionMessage = JsonSerializer.Deserialize <JsonElement>(exceptionJson);
                                exception            = new LoggerException(exceptionMessage);
                            }

                            var message = JsonSerializer.Deserialize <JsonElement>(argsJson);
                            if (message.TryGetProperty("{OriginalFormat}", out var formatElement))
                            {
                                var formatString = formatElement.GetString();
                                var formatter    = new LogValuesFormatter(formatString);
                                object[] args    = new object[formatter.ValueNames.Count];
                                for (int i = 0; i < args.Length; i++)
                                {
                                    args[i] = message.GetProperty(formatter.ValueNames[i]).GetString();
                                }

                                logger.Log(logLevel, new EventId(eventId, eventName), exception, formatString, args);
                            }
                            else
                            {
                                var obj = new LogObject(message, lastFormattedMessage);
                                logger.Log(logLevel, new EventId(eventId, eventName), obj, exception, LogObject.Callback);
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.LogDebug(ex, "Error processing log entry for {ServiceName}", replicaName);
                        }
                        finally
                        {
                            scopes.ForEach(d => d.Dispose());
                        }
                    });

                    source.Dynamic.AddCallbackForProviderEvent(MicrosoftExtensionsLoggingProviderName, "FormattedMessage", (traceEvent) =>
                    {
                        // Level, FactoryID, LoggerName, EventID, EventName, FormattedMessage
                        var logLevel         = (LogLevel)traceEvent.PayloadByName("Level");
                        var factoryId        = (int)traceEvent.PayloadByName("FactoryID");
                        var categoryName     = (string)traceEvent.PayloadByName("LoggerName");
                        var eventId          = (int)traceEvent.PayloadByName("EventId");
                        var eventName        = (string)traceEvent.PayloadByName("EventName");
                        var formattedMessage = (string)traceEvent.PayloadByName("FormattedMessage");

                        if (string.IsNullOrEmpty(formattedMessage))
                        {
                            formattedMessage = eventName;
                            eventName        = "";
                        }

                        lastFormattedMessage = formattedMessage;
                    });

                    source.Process();
                }
コード例 #13
0
ファイル: ActivitySource.cs プロジェクト: pedrobsaila/runtime
 /// <summary>
 /// Creates a new <see cref="Activity"/> object if there is any listener to the Activity, returns null otherwise.
 /// If the Activity object is created, it will not automatically start. Callers will need to call <see cref="Activity.Start()"/> to start it.
 /// </summary>
 /// <param name="name">The operation name of the Activity.</param>
 /// <param name="kind">The <see cref="ActivityKind"/></param>
 /// <param name="parentContext">The parent <see cref="ActivityContext"/> object to initialize the created Activity object with.</param>
 /// <param name="tags">The optional tags list to initialize the created Activity object with.</param>
 /// <param name="links">The optional <see cref="ActivityLink"/> list to initialize the created Activity object with.</param>
 /// <param name="idFormat">The default Id format to use.</param>
 /// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
 /// <remarks>
 /// If the Activity object is created, it will not start automatically. Callers need to call <see cref="Activity.Start()"/> to start it.
 /// </remarks>
 public Activity?CreateActivity(string name, ActivityKind kind, ActivityContext parentContext, IEnumerable <KeyValuePair <string, object?> >?tags = null, IEnumerable <ActivityLink>?links = null, ActivityIdFormat idFormat = ActivityIdFormat.Unknown)
 => CreateActivity(name, kind, parentContext, null, tags, links, default, startIt: false, idFormat);
コード例 #14
0
ファイル: ActivitySource.cs プロジェクト: pedrobsaila/runtime
        private Activity?CreateActivity(string name, ActivityKind kind, ActivityContext context, string?parentId, IEnumerable <KeyValuePair <string, object?> >?tags,
                                        IEnumerable <ActivityLink>?links, DateTimeOffset startTime, bool startIt = true, ActivityIdFormat idFormat = ActivityIdFormat.Unknown)
        {
            // _listeners can get assigned to null in Dispose.
            SynchronizedList <ActivityListener>?listeners = _listeners;

            if (listeners == null || listeners.Count == 0)
            {
                return(null);
            }

            Activity?activity = null;
            ActivityTagsCollection?samplerTags;
            string?traceState;

            ActivitySamplingResult samplingResult = ActivitySamplingResult.None;

            if (parentId != null)
            {
                ActivityCreationOptions <string>          aco        = default;
                ActivityCreationOptions <ActivityContext> acoContext = default;

                aco = new ActivityCreationOptions <string>(this, name, parentId, kind, tags, links, idFormat);
                if (aco.IdFormat == ActivityIdFormat.W3C)
                {
                    // acoContext is used only in the Sample calls which called only when we have W3C Id format.
                    acoContext = new ActivityCreationOptions <ActivityContext>(this, name, aco.GetContext(), kind, tags, links, ActivityIdFormat.W3C);
                }

                listeners.EnumWithFunc((ActivityListener listener, ref ActivityCreationOptions <string> data, ref ActivitySamplingResult result, ref ActivityCreationOptions <ActivityContext> dataWithContext) => {
                    SampleActivity <string>?sampleUsingParentId = listener.SampleUsingParentId;
                    if (sampleUsingParentId != null)
                    {
                        ActivitySamplingResult sr = sampleUsingParentId(ref data);
                        dataWithContext.SetTraceState(data.TraceState); // Keep the trace state in sync between data and dataWithContext

                        if (sr > result)
                        {
                            result = sr;
                        }
                    }
                    else if (data.IdFormat == ActivityIdFormat.W3C)
                    {
                        // In case we have a parent Id and the listener not providing the SampleUsingParentId, we'll try to find out if the following conditions are true:
                        //   - The listener is providing the Sample callback
                        //   - Can convert the parent Id to a Context. ActivityCreationOptions.TraceId != default means parent id converted to a valid context.
                        // Then we can call the listener Sample callback with the constructed context.
                        SampleActivity <ActivityContext>?sample = listener.Sample;
                        if (sample != null)
                        {
                            ActivitySamplingResult sr = sample(ref dataWithContext);
                            data.SetTraceState(dataWithContext.TraceState); // Keep the trace state in sync between data and dataWithContext

                            if (sr > result)
                            {
                                result = sr;
                            }
                        }
                    }
                }, ref aco, ref samplingResult, ref acoContext);

                if (context == default)
                {
                    if (aco.GetContext() != default)
                    {
                        context  = aco.GetContext();
                        parentId = null;
                    }
                    else if (acoContext.GetContext() != default)
                    {
                        context  = acoContext.GetContext();
                        parentId = null;
                    }
                }

                samplerTags = aco.GetSamplingTags();
                ActivityTagsCollection?atc = acoContext.GetSamplingTags();
                if (atc != null)
                {
                    if (samplerTags == null)
                    {
                        samplerTags = atc;
                    }
                    else
                    {
                        foreach (KeyValuePair <string, object?> tag in atc)
                        {
                            samplerTags.Add(tag);
                        }
                    }
                }

                idFormat   = aco.IdFormat;
                traceState = aco.TraceState;
            }
            else
            {
                bool useCurrentActivityContext = context == default && Activity.Current != null;
                var  aco = new ActivityCreationOptions <ActivityContext>(this, name, useCurrentActivityContext ? Activity.Current !.Context : context, kind, tags, links, idFormat);
                listeners.EnumWithFunc((ActivityListener listener, ref ActivityCreationOptions <ActivityContext> data, ref ActivitySamplingResult result, ref ActivityCreationOptions <ActivityContext> unused) => {
                    SampleActivity <ActivityContext>?sample = listener.Sample;
                    if (sample != null)
                    {
                        ActivitySamplingResult dr = sample(ref data);
                        if (dr > result)
                        {
                            result = dr;
                        }
                    }
                }, ref aco, ref samplingResult, ref aco);

                if (!useCurrentActivityContext)
                {
                    // We use the context stored inside ActivityCreationOptions as it is possible the trace id get automatically generated during the sampling.
                    // We don't use the context stored inside ActivityCreationOptions only in case if we used Activity.Current context, the reason is we need to
                    // create the new child activity with Parent set to Activity.Current.
                    context = aco.GetContext();
                }

                samplerTags = aco.GetSamplingTags();
                idFormat    = aco.IdFormat;
                traceState  = aco.TraceState;
            }

            if (samplingResult != ActivitySamplingResult.None)
            {
                activity = Activity.Create(this, name, kind, parentId, context, tags, links, startTime, samplerTags, samplingResult, startIt, idFormat, traceState);
            }

            return(activity);
        }