/// <summary> /// Adds the needed services for Google Cloud Tracing. Used with <see cref="UseGoogleTrace"/>. /// </summary> /// <param name="services">The service collection. Cannot be null.</param> /// <param name="setupAction">Action to set up options. Cannot be null.</param> /// <remarks> /// If <see cref="RetryOptions.ExceptionHandling"/> is set to <see cref="ExceptionHandling.Propagate"/> /// and the <see cref="RequestDelegate"/> executed by this middleware throws ad exception and this /// diagnostics library also throws an exception trying to report it and <see cref="AggregateException"/> /// with both exceptions will be thrown. Otherwise only the exception from the <see cref="RequestDelegate"/> /// will be thrown. /// </remarks> public static IServiceCollection AddGoogleTrace( this IServiceCollection services, Action <TraceServiceOptions> setupAction) { GaxPreconditions.CheckNotNull(services, nameof(services)); GaxPreconditions.CheckNotNull(setupAction, nameof(setupAction)); var serviceOptions = new TraceServiceOptions(); setupAction(serviceOptions); var client = serviceOptions.Client ?? TraceServiceClient.Create(); var options = serviceOptions.Options ?? TraceOptions.Create(); var traceFallbackPredicate = serviceOptions.TraceFallbackPredicate ?? TraceDecisionPredicate.Default; var projectId = Project.GetAndCheckProjectId(serviceOptions.ProjectId); var consumer = ManagedTracer.CreateConsumer(client, options); var tracerFactory = ManagedTracer.CreateTracerFactory(projectId, consumer, options); services.AddScoped(CreateTraceHeaderContext); services.AddSingleton(tracerFactory); // Only add the HttpContextAccessor if it's not already added. // This is needed to get the `TraceHeaderContext`. See `CreateTraceHeaderContext`. services.TryAddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton(ManagedTracer.CreateDelegatingTracer(() => ContextTracerManager.GetCurrentTracer())); services.AddSingleton(new TraceHeaderPropagatingHandler(() => ContextTracerManager.GetCurrentTracer())); return(services.AddSingleton(traceFallbackPredicate)); }
/// <summary> /// Invokes the next <see cref="RequestDelegate"/> and traces the time /// taken for the next delegate to run, reporting the results to the /// Stackdriver Trace API. /// </summary> /// <param name="httpContext">The current HTTP context.</param> /// <param name="traceHeaderContext">Information from the current request header. Must not be null.</param> public async Task Invoke(HttpContext httpContext, TraceHeaderContext traceHeaderContext) { GaxPreconditions.CheckNotNull(traceHeaderContext, nameof(traceHeaderContext)); // Create a tracer for the given request and set it on the context manager so // the tracer can be used in other places. var tracer = _tracerFactory(traceHeaderContext); ContextTracerManager.SetCurrentTracer(tracer); if (tracer.GetCurrentTraceId() == null) { await _next(httpContext).ConfigureAwait(false); } else { if (traceHeaderContext.TraceId != null) { // Set the trace updated trace header on the response. var updatedHeaderContext = TraceHeaderContext.Create( tracer.GetCurrentTraceId(), tracer.GetCurrentSpanId() ?? 0, true); httpContext.Response.Headers.Add( TraceHeaderContext.TraceHeader, updatedHeaderContext.ToString()); } // Trace the delegate and annotate it with information from the current // HTTP context. var traceName = await _nameProvider.GetTraceNameAsync(httpContext).ConfigureAwait(false); var span = tracer.StartSpan(traceName); try { await _next(httpContext).ConfigureAwait(false); } catch (Exception exception) { try { StackTrace stackTrace = new StackTrace(exception, true); tracer.SetStackTrace(stackTrace); } catch (Exception innerException) { throw new AggregateException(innerException, exception); } throw; } finally { tracer.AnnotateSpan(Labels.AgentLabel); tracer.AnnotateSpan(Labels.FromHttpContext(httpContext)); span.Dispose(); } } }
/// <summary> /// Invokes the next <see cref="RequestDelegate"/> and traces the time /// taken for the next delegate to run, reporting the results to the /// Stackdriver Trace API. /// </summary> /// <param name="httpContext">The current http context.</param> /// <param name="traceHeaderContext">Information from the current requrest header. Cannot be null.</param> public async Task Invoke(HttpContext httpContext, TraceHeaderContext traceHeaderContext) { GaxPreconditions.CheckNotNull(traceHeaderContext, nameof(traceHeaderContext)); // Create a tracer for the given request and set it on the HttpContext so // the tracer can be used in other places. var tracer = _tracerFactory.CreateTracer(traceHeaderContext); ContextTracerManager.SetCurrentTracer(_accessor, tracer); if (tracer.GetCurrentTraceId() == null) { await _next(httpContext).ConfigureAwait(false); } else { if (traceHeaderContext.TraceId != null) { // Set the trace updated trace header on the response. var updatedHeaderContext = TraceHeaderContext.Create( tracer.GetCurrentTraceId(), tracer.GetCurrentSpanId() ?? 0, true); httpContext.Response.Headers.Add( TraceHeaderContext.TraceHeader, updatedHeaderContext.ToString()); } // Trace the delegate and annotate it with information from the current // http context. tracer.StartSpan(httpContext.Request.Path); try { await _next(httpContext).ConfigureAwait(false); } catch (Exception e) { StackTrace stackTrace = new StackTrace(e, true); tracer.SetStackTrace(stackTrace); throw; } finally { tracer.AnnotateSpan(Labels.AgentLabel); tracer.AnnotateSpan(Labels.FromHttpContext(httpContext)); tracer.EndSpan(); } } }
private void SetTraceAndSpanIfAny(LogEntry entry) { if (_traceTarget is null) { return; } string traceId = null; ulong? spanId = null; // If there's currently a Google trace and span use that one. // This means that the Google Trace component of the diagnostics library // has been initialized. if (ContextTracerManager.GetCurrentTracer() is IManagedTracer tracer && tracer.GetCurrentTraceId() is string googleTraceId) { traceId = googleTraceId; spanId = tracer.GetCurrentSpanId(); }
/// <summary> /// Creates a singleton <see cref="IManagedTracer"/> that is a <see cref="DelegatingTracer"/>. /// The <see cref="DelegatingTracer"/> will use an <see cref="IHttpContextAccessor"/> under the hood /// to get the current tracer which is set by the <see cref="ContextTracerManager"/>. /// </summary> internal static IManagedTracer CreateManagedTracer(IServiceProvider provider) { var accessor = provider.GetServiceCheckNotNull <IHttpContextAccessor>(); return(new DelegatingTracer(() => ContextTracerManager.GetCurrentTracer(accessor))); }