public void Publish(string message) { Activity activity = null; if (diagnosticSource.IsEnabled("Sample.RabbitMQ")) { activity = new Activity("Publish to RabbitMQ"); activity.AddTag("operation", "publish"); activity.AddTag("host", HostName); activity.AddTag("queue", QueueName); diagnosticSource.StartActivity(activity, null); } var props = channel.CreateBasicProperties(); props.Headers = new Dictionary <string, object>(); props.Headers.Add(TraceParent.HeaderKey, TraceParent.FromCurrentActivity().ToString()); channel.BasicPublish("", QueueName, props, System.Text.Encoding.UTF8.GetBytes(message)); if (activity != null) { diagnosticSource.StopActivity(activity, null); } }
public void SetTracing(WebRequest request) { if (Agent.Tracer.CurrentTransaction == null) { return; } if (!string.IsNullOrWhiteSpace(request.Headers[TraceParent.TraceParentHeaderName])) { return; } timer = new Stopwatch(); timer.Start(); transaction = Agent.Tracer.CurrentTransaction; span = transaction.StartSpan( $"{request.Method} {request.RequestUri.Host}", "proxy", ApiConstants.SubtypeHttp); if (transaction.IsSampled) { span.Context.Http = new Http { Method = request.Method, Url = request.RequestUri.ToString() }; } request.Headers.Add(TraceParent.TraceParentHeaderName, TraceParent.BuildTraceparent(span.TraceId, span.ParentId, span.IsSampled)); }
protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancel) { if (InnerHandler == null) { InnerHandler = new HttpClientHandler(); } var transaction = GetTransaction(request); if (!request.Headers.Contains(TraceParent.TraceParentHeaderName)) { //Outgoing request var traceString = TraceParent.BuildTraceparent(transaction.TraceId, transaction.ParentId, transaction.IsSampled); request.Headers.Add(TraceParent.TraceParentHeaderName, traceString); } if (transaction.IsSampled) { RecordRequest(request, transaction); } var httpResponse = await base.SendAsync(request, cancel); transaction.Result = $"HTTP {(int)httpResponse.StatusCode}"; if (transaction.IsSampled) { RecordResponse(httpResponse, transaction); } transaction.End(); return(httpResponse); }
public void ValidateTraceParentWithInvalidLength() { const string traceParent = "99-af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"; //TraceId is 1 char shorter than expected var res = TraceParent.TryExtractTraceparent(traceParent); res.Should().BeNull(); }
public void ValidateTraceParentWithInvalidTraceIdLength() { const string traceParent = "00-0af7651916cd43dd8448eb211c80319ca-7ad6b7169203331-01"; //TraceId is 1 char longer than expected, and parentId is 1 char longer var res = TraceParent.TryExtractTraceparent(traceParent); res.Should().BeNull(); }
public void ParseTraceparentHeader() { const string traceParent = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"; var res = TraceParent.TryExtractTraceparent(traceParent); Debug.WriteLine($"{res}"); }
public void FromString_Exception() { var ex = Assert.Throws <ArgumentException>(() => { TraceParent.FromString("foobar"); }); Assert.Contains("foobar", ex.Message); ex = Assert.Throws <ArgumentException>(() => { TraceParent.FromString(""); }); Assert.Contains("Traceparent", ex.Message); }
private Transaction StartTransaction(HttpContext context) { try { Transaction transaction; var transactionName = $"{context.Request.Method} {context.Request.Path}"; if (context.Request.Headers.ContainsKey(TraceParent.TraceParentHeaderName)) { var headerValue = context.Request.Headers[TraceParent.TraceParentHeaderName].ToString(); var distributedTracingData = TraceParent.TryExtractTraceparent(headerValue); if (distributedTracingData != null) { _logger.Debug() ?.Log( "Incoming request with {TraceParentHeaderName} header. DistributedTracingData: {DistributedTracingData}. Continuing trace.", TraceParent.TraceParentHeaderName, distributedTracingData); transaction = _tracer.StartTransactionInternal( transactionName, ApiConstants.TypeRequest, distributedTracingData); } else { _logger.Debug() ?.Log( "Incoming request with invalid {TraceParentHeaderName} header (received value: {TraceParentHeaderValue}). Starting trace with new trace id.", TraceParent.TraceParentHeaderName, headerValue); transaction = _tracer.StartTransactionInternal(transactionName, ApiConstants.TypeRequest); } } else { _logger.Debug()?.Log("Incoming request. Starting Trace."); transaction = _tracer.StartTransactionInternal(transactionName, ApiConstants.TypeRequest); } if (transaction.IsSampled) { FillSampledTransactionContextRequest(context, transaction); } return(transaction); } catch (Exception ex) { _logger?.Error()?.LogException(ex, "Exception thrown while trying to start transaction"); return(null); } }
private static void HexAssertValid(this string thisObj, int sizeInBits) { thisObj.NonEmptyAssertValid(); (sizeInBits % 8).Should().Be(0, $"sizeInBits should be divisible by 8 but sizeInBits is {sizeInBits}"); var numHexChars = sizeInBits / 8 * 2; var because = $"String should be {numHexChars} hex digits ({sizeInBits}-bits) but the actual value is `{thisObj}' (length: {thisObj.Length})"; thisObj.Length.Should().Be(numHexChars, because); // 2 hex chars per byte TraceParent.IsHex(thisObj).Should().BeTrue(because); }
private static DistributedTracingData ExtractIncomingDistributedTracingData(HttpRequest httpRequest) { var headerValue = httpRequest.Headers.Get(TraceParent.TraceParentHeaderName); if (headerValue == null) { Logger.Debug()?.Log("Incoming request doesn't {TraceParentHeaderName} header - " + "it means request doesn't have incoming distributed tracing data", TraceParent.TraceParentHeaderName); return(null); } return(TraceParent.TryExtractTraceparent(headerValue)); }
public void ValidateTraceParentWithFutureVersion() { const string traceParent = "99-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"; //Best attempt, even if it's a future version we still try to read the traceId and parentId var res = TraceParent.TryExtractTraceparent(traceParent); res.Should().NotBeNull(); res.TraceId.Should().Be("0af7651916cd43dd8448eb211c80319c"); res.ParentId.Should().Be("b7ad6b7169203331"); res.FlagRecorded.Should().BeTrue(); }
public void FromString_NormalCase() { var expectedVersion = "00"; var expectedTraceId = "0af7651916cd43dd8448eb211c80319c"; var expectedSpanId = "00f067aa0ba902b7"; var expectedTraceFlags = "01"; var actual = TraceParent.FromString($"{expectedVersion}-{expectedTraceId}-{expectedSpanId}-{expectedTraceFlags}"); Assert.Equal(expectedVersion, actual.Version); Assert.Equal(expectedTraceId, actual.TraceId); Assert.Equal(expectedSpanId, actual.SpanId); Assert.Equal(expectedTraceFlags, actual.TraceFlags); }
protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancel) { if (InnerHandler == null) { InnerHandler = new HttpClientHandler(); } if (Agent.Tracer.CurrentTransaction == null) { return(await base.SendAsync(request, cancel)); } var transaction = Agent.Tracer.CurrentTransaction; var span = transaction.StartSpan( $"{request.Method.Method} {request.RequestUri.Host}", ApiConstants.TypeExternal, ApiConstants.SubtypeHttp); if (transaction.IsSampled) { span.Context.Http = new Http { Method = request.Method.Method, Url = request.RequestUri.ToString() }; } if (!request.Headers.Contains(TraceParent.TraceParentHeaderName)) { request.Headers.Add(TraceParent.TraceParentHeaderName, TraceParent.BuildTraceparent(span.TraceId, span.ParentId, span.IsSampled)); } try { var httpResponse = await base.SendAsync(request, cancel); if (transaction.IsSampled) { span.Context.Http.StatusCode = (int)httpResponse.StatusCode; } span.End(); return(httpResponse); } catch (Exception ex) { span.CaptureException(ex); throw; } }
public void ParseValidTraceParentRecorded() { const string traceParent = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"; var res = TraceParent.TryExtractTraceparent(traceParent); res.Should().NotBeNull(); res.TraceId.Should().Be("0af7651916cd43dd8448eb211c80319c"); res.ParentId.Should().Be("b7ad6b7169203331"); res.FlagRecorded.Should().BeTrue(); //try also with flag options C6 const string traceParent2 = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-E7"; var res2 = TraceParent.TryExtractTraceparent(traceParent2); res2.FlagRecorded.Should().BeTrue(); }
private void ProcessStartEvent(object eventValue, TRequest request, Uri requestUrl) { _logger.Trace()?.Log("Processing start event... Request URL: {RequestUrl}", requestUrl); if (Agent.TransactionContainer.Transactions == null || Agent.TransactionContainer.Transactions.Value == null) { _logger.Debug()?.Log("No active transaction, skip creating span for outgoing HTTP request"); return; } var transaction = Agent.TransactionContainer.Transactions.Value; var span = transaction.StartSpanInternal( $"{RequestGetMethod(request)} {requestUrl.Host}", ApiConstants.TypeExternal, ApiConstants.SubtypeHttp); if (!ProcessingRequests.TryAdd(request, span)) { // Sergey_Kleyman_TODO: Implement error handling _logger.Error()?.Log("Failed to add to ProcessingRequests - ???"); return; } if (!RequestHeadersContain(request, TraceParent.TraceParentHeaderName)) { // We call TraceParent.BuildTraceparent explicitly instead of DistributedTracingData.SerializeToString because // in the future we might change DistributedTracingData.SerializeToString to use some other internal format // but here we want the string to be in W3C 'traceparent' header format. RequestHeadersAdd(request, TraceParent.TraceParentHeaderName, TraceParent.BuildTraceparent(span.OutgoingDistributedTracingData)); } if (transaction.IsSampled) { span.Context.Http = new Http { Url = requestUrl.ToString(), Method = RequestGetMethod(request) }; var frames = new StackTrace(true).GetFrames(); var stackFrames = StacktraceHelper.GenerateApmStackTrace(frames, _logger, span.Name); span.StackTrace = stackFrames; } }
private void ProcessStartEvent(TRequest request, Uri requestUrl) { Logger.Trace()?.Log("Processing start event... Request URL: {RequestUrl}", Http.Sanitize(requestUrl)); var transaction = _agent.Tracer.CurrentTransaction; if (transaction == null) { Logger.Debug()?.Log("No current transaction, skip creating span for outgoing HTTP request"); return; } var span = (Span)ExecutionSegmentCommon.GetCurrentExecutionSegment(_agent) .StartSpan( $"{RequestGetMethod(request)} {requestUrl.Host}", ApiConstants.TypeExternal, ApiConstants.SubtypeHttp); if (!ProcessingRequests.TryAdd(request, span)) { // Consider improving error reporting - see https://github.com/elastic/apm-agent-dotnet/issues/280 Logger.Error()?.Log("Failed to add to ProcessingRequests - ???"); return; } if (!RequestHeadersContain(request, TraceParent.TraceParentHeaderName)) { // We call TraceParent.BuildTraceparent explicitly instead of DistributedTracingData.SerializeToString because // in the future we might change DistributedTracingData.SerializeToString to use some other internal format // but here we want the string to be in W3C 'traceparent' header format. RequestHeadersAdd(request, TraceParent.TraceParentHeaderName, TraceParent.BuildTraceparent(span.OutgoingDistributedTracingData)); } if (!span.ShouldBeSentToApmServer) { return; } span.Context.Http = new Http { Method = RequestGetMethod(request) }; span.Context.Http.SetUrl(requestUrl); }
private void ProcessStartEvent(object eventValue, TRequest request, Uri requestUrl) { _logger.Trace()?.Log("Processing start event... Request URL: {RequestUrl}", requestUrl); if (Agent.TransactionContainer.Transactions == null || Agent.TransactionContainer.Transactions.Value == null) { _logger.Debug()?.Log("No active transaction, skip creating span for outgoing HTTP request"); return; } var transaction = Agent.TransactionContainer.Transactions.Value; var span = transaction.StartSpanInternal( $"{RequestGetMethod(request)} {requestUrl.Host}", ApiConstants.TypeExternal, ApiConstants.SubtypeHttp); if (!ProcessingRequests.TryAdd(request, span)) { // Consider improving error reporting - see https://github.com/elastic/apm-agent-dotnet/issues/280 _logger.Error()?.Log("Failed to add to ProcessingRequests - ???"); return; } if (!RequestHeadersContain(request, TraceParent.TraceParentHeaderName)) { // We call TraceParent.BuildTraceparent explicitly instead of DistributedTracingData.SerializeToString because // in the future we might change DistributedTracingData.SerializeToString to use some other internal format // but here we want the string to be in W3C 'traceparent' header format. RequestHeadersAdd(request, TraceParent.TraceParentHeaderName, TraceParent.BuildTraceparent(span.OutgoingDistributedTracingData)); } if (transaction.IsSampled) { span.Context.Http = new Http { Url = requestUrl.ToString(), Method = RequestGetMethod(request) }; } }
/// <summary> /// Serializes this instance to a string. /// This method should be used at the caller side and the return value should be passed to the (possibly remote) callee /// side. /// <see cref="TryDeserializeFromString" /> should be used to deserialize the instance at the callee side. /// </summary> /// <returns> /// String containing the instance in serialized form. /// </returns> public string SerializeToString() => TraceParent.BuildTraceparent(this);
/// <summary> /// Deserializes an instance from a string. /// This method should be used at the callee side and the deserialized instance can be passed to /// <see cref="ITracer.StartTransaction" />. /// </summary> /// <param name="serialized">should be a return value from a call to <see cref="SerializeToString" />.</param> /// <returns> /// Instance deserialized from /// <param name="serialized" /> /// . /// </returns> public static DistributedTracingData TryDeserializeFromString(string serialized) => TraceParent.TryExtractTraceparent(serialized);
public async Task InvokeAsync(HttpContext context) { Transaction transaction; var transactionName = $"{context.Request.Method} {context.Request.Path}"; if (context.Request.Headers.ContainsKey(TraceParent.TraceParentHeaderName)) { var headerValue = context.Request.Headers[TraceParent.TraceParentHeaderName].ToString(); var distributedTracingData = TraceParent.TryExtractTraceparent(headerValue); if (distributedTracingData != null) { _logger.Debug() ?.Log( "Incoming request with {TraceParentHeaderName} header. DistributedTracingData: {DistributedTracingData}. Continuing trace.", TraceParent.TraceParentHeaderName, distributedTracingData); transaction = _tracer.StartTransactionInternal( transactionName, ApiConstants.TypeRequest, distributedTracingData); } else { _logger.Debug() ?.Log( "Incoming request with invalid {TraceParentHeaderName} header (received value: {TraceParentHeaderValue}). Starting trace with new trace id.", TraceParent.TraceParentHeaderName, headerValue); transaction = _tracer.StartTransactionInternal(transactionName, ApiConstants.TypeRequest); } } else { _logger.Debug()?.Log("Incoming request. Starting Trace."); transaction = _tracer.StartTransactionInternal(transactionName, ApiConstants.TypeRequest); } if (transaction.IsSampled) { FillSampledTransactionContextRequest(context, transaction); } try { await _next(context); } catch (Exception e) when((Helpers.ExceptionFilter.Capture(e, transaction, context, _configurationReader, _logger))) { } finally { if (!transaction.HasCustomName) { //fixup Transaction.Name - e.g. /user/profile/1 -> /user/profile/{id} var routeData = (context.Features[typeof(IRoutingFeature)] as IRoutingFeature)?.RouteData; if (routeData != null) { var name = GetNameFromRouteContext(routeData.Values); if (!string.IsNullOrWhiteSpace(name)) { transaction.Name = $"{context.Request.Method} {name}"; } } } transaction.Result = Transaction.StatusCodeToResult(GetProtocolName(context.Request.Protocol), context.Response.StatusCode); if (transaction.IsSampled) { FillSampledTransactionContextResponse(context, transaction); FillSampledTransactionContextUser(context, transaction); } transaction.End(); } }
private async Task ProcessWebQueueMessageAsync(object sender, BasicDeliverEventArgs @event) { //var correlationId = @event.BasicProperties.CorrelationId; TraceParent traceParent = null; if (@event.BasicProperties.Headers.TryGetValue(TraceParent.HeaderKey, out var rawTraceParent) && rawTraceParent is byte[] binRawTraceParent) { traceParent = TraceParent.CreateFromString(Encoding.UTF8.GetString(binRawTraceParent)); } ISpan span = null; IOperationHolder <RequestTelemetry> operation = null; try { if (traceParent == null) { throw new Exception("Trace information not found in message"); } if (tracer != null) { var traceId = ActivityTraceId.CreateFromString(traceParent.TraceId); var parentId = ActivitySpanId.CreateFromString(traceParent.SpanId); var parentContext = new SpanContext(traceId, parentId, ActivityTraceFlags.Recorded, isRemote: true); tracer.StartActiveSpan("Process single RabbitMQ message", parentContext, SpanKind.Consumer, out span); span.SetAttribute("queue", Constants.WebQueueName); } using (operation = telemetryClient?.StartOperation <RequestTelemetry>("Process single RabbitMQ message", traceParent.TraceId, traceParent.SpanId)) { if (operation != null) { operation.Telemetry.Properties.Add("queue", Constants.WebQueueName); } using (logger.BeginScope("processing message {correlationId}", traceParent.TraceId)) { var apiFullUrl = $"{apiUrl}/api/time/dbtime"; var time = await httpClientFactory.CreateClient().GetStringAsync(apiFullUrl); // Get the payload var message = JsonSerializer.Deserialize <EnqueuedMessage>(@event.Body, jsonSerializerOptions); if (!string.IsNullOrEmpty(message.EventName)) { span?.AddEvent(message.EventName); telemetryClient?.TrackEvent(message.EventName); } if (logger.IsEnabled(LogLevel.Debug)) { logger.LogDebug("Processed message: {message}", Encoding.UTF8.GetString(@event.Body)); } } } } catch (Exception ex) { if (span != null) { span.Status = Status.Internal.WithDescription(ex.ToString()); } if (operation != null) { operation.Telemetry.Success = false; telemetryClient.TrackException(ex); } } finally { span?.End(); } }