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; } }
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; } }
/// <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; } }
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; } }
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); } }
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); }
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 } }
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); }
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; } }
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); }
public override void Before(MethodInfo methodUnderTest) => _originalFormat = Activity.DefaultIdFormat;
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(); }
/// <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);
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); }