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);
        }
Beispiel #4
0
        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();
        }
Beispiel #5
0
        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();
        }
Beispiel #6
0
        public void ParseTraceparentHeader()
        {
            const string traceParent = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01";

            var res = TraceParent.TryExtractTraceparent(traceParent);

            Debug.WriteLine($"{res}");
        }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        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);
            }
        }
Beispiel #9
0
		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);
		}
Beispiel #10
0
        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));
        }
Beispiel #11
0
        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();
        }
Beispiel #12
0
        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);
        }
Beispiel #13
0
        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;
            }
        }
Beispiel #14
0
        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;
            }
        }
Beispiel #16
0
        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();
            }
        }
Beispiel #21
0
        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();
            }
        }