/// <inheritdoc />
        public IApmSpan Start(string spanKind, string operationName, string type, IDictionary <string, string> existingContext = null, string resourceName = null)
        {
            if (!global::Elastic.Apm.Agent.IsConfigured)
            {
                return(NullApmSpan.Instance);
            }

            var tracer = global::Elastic.Apm.Agent.Tracer;
            DistributedTracingData?distributedTracingData = null;

            if (existingContext != null && existingContext.TryGetValue("ElasticDTD", out var d))
            {
                distributedTracingData = DistributedTracingData.TryDeserializeFromString(d);
            }

            if (tracer.CurrentTransaction == null)
            {
                return(new ElasticSpan(tracer.StartTransaction(
                                           operationName,
                                           type,
                                           distributedTracingData)));
            }

            return(new ElasticSpan(
                       tracer.CurrentTransaction.StartSpan(operationName, type, resourceName)));
        }
Exemple #2
0
        public static DistributedTracingData GetTracingData(string traceId, string parentId, bool flagRecorded)
        {
            //NOTE: Not ideal - this really should just be using the constructor but it is marked private
            var serialized = TraceParent.BuildTraceparent(traceId, parentId, flagRecorded);

            return(DistributedTracingData.TryDeserializeFromString(serialized));
        }
 public Task <T> CaptureTransaction <T>(
     string name, string type,
     Func <ITransaction, Task <T> > func,
     DistributedTracingData distributedTracingData = null)
 {
     return(Agent.Tracer.CaptureTransaction(name, type, func, distributedTracingData));
 }
        /// <inheritdoc />
        public IApmSpan StartOperation(ApiOperationDescriptor operation, string spanKind, IDictionary <string, string> existingContext = null)
        {
            if (!global::Elastic.Apm.Agent.IsConfigured)
            {
                return(NullApmSpan.Instance);
            }

            var tracer = global::Elastic.Apm.Agent.Tracer;

            if (tracer.CurrentTransaction != null)
            {
                // If a transaction is already underway we want to set it's name for a more accurate picture (i.e. this is
                // a HTTP call but we want to use the operation name not the HTTP route name).
                tracer.CurrentTransaction.Name = operation.Name;

                // We will also start a new span as there may be work before and after in the framework that should
                // be tracked separately from the Blueprint processing work.
                return(new ElasticSpan(
                           tracer.CurrentTransaction.StartSpan(operation.Name, ApiConstants.TypeRequest, "operation", ApiConstants.ActionExec)));
            }

            DistributedTracingData?distributedTracingData = null;

            if (existingContext != null && existingContext.TryGetValue("ElasticDTD", out var d))
            {
                distributedTracingData = DistributedTracingData.TryDeserializeFromString(d);
            }

            return(new ElasticSpan(tracer.StartTransaction(
                                       operation.Name,
                                       spanKind,
                                       distributedTracingData)));
        }
 public ITransaction StartTransaction(
     string name,
     string type,
     DistributedTracingData distributedTracingData = null)
 {
     return(Agent.Tracer.StartTransaction(name, type, distributedTracingData));
 }
Exemple #6
0
        private static void Main(string[] args)
        {
            if (args.Length == 1)             //in case it's started with an argument we try to parse the argument as a DistributedTracingData
            {
                WriteLineToConsole($"Callee process started - continuing trace with distributed tracing data: {args[0]}");
                var transaction2 = Agent.Tracer.StartTransaction("Transaction2", "TestTransaction",
                                                                 DistributedTracingData.TryDeserializeFromString(args[0]));

                try
                {
                    transaction2.CaptureSpan("TestSpan", "TestSpanType", () => Thread.Sleep(200));
                }
                finally
                {
                    transaction2.End();
                }

                Thread.Sleep(1000);
                WriteLineToConsole("About to exit");
            }
            else
            {
                WriteLineToConsole("Started");
                PassDistributedTracingData();

                //WIP: if the process terminates the agent
                //potentially does not have time to send the transaction to the server.
                Thread.Sleep(1000);

                WriteLineToConsole("About to exit - press any key...");
                Console.ReadKey();
            }
        }
        private void HandleStart(RabbitMqEvent <RabbitMqHandleParams> evt)
        {
            try
            {
                var prms = evt.Params;
                DistributedTracingData tracingData = null;
                if (prms.Properties != null && prms.Properties.Headers.TryGetValue(Constants.HeaderKey, out object tracingDataBlob) && tracingDataBlob is byte[])
                {
                    tracingData = DistributedTracingData.TryDeserializeFromString(Encoding.UTF8.GetString((byte[])tracingDataBlob));
                }

                if (MatchesIgnoreMessageQueues(prms.RoutingKey))
                {
                    return;
                }

                var transaction = _ApmAgent.Tracer.StartTransaction(prms.RoutingKey, Constants.Type, tracingData);

                if (!_processingQueries.TryAdd(prms.Id, transaction))
                {
                    return;
                }

                transaction.SetLabel(nameof(prms.RoutingKey), prms.RoutingKey);
                transaction.SetLabel(nameof(prms.ConsumerTag), prms.ConsumerTag);
                transaction.SetLabel(nameof(prms.DeliveryTag), $"{prms.DeliveryTag}");
                transaction.SetLabel(nameof(prms.Exchange), prms.Exchange);
                transaction.SetLabel(nameof(prms.Redelivered), $"{prms.ConsumerTag}");
                transaction.SetLabel(nameof(prms.Body), prms.Body != null ? System.Text.Encoding.UTF8.GetString(prms.Body) : string.Empty);
            }
            catch
            {
            }
        }
        public override IDisposable Consume(IQueue queue,
                                            Func <byte[], MessageProperties, MessageReceivedInfo, Task> onMessage,
                                            Action <IConsumerConfiguration> configure)
        {
            async Task NewOnMessage(byte[] body, MessageProperties properties, MessageReceivedInfo receivedInfo)
            {
                if (!Agent.IsConfigured)
                {
                    await onMessage(body, properties, receivedInfo).ConfigureAwait(false);

                    return;
                }

                DistributedTracingData tracingData = null;

                if (properties.Headers.ContainsKey("traceparent") &&
                    properties.Headers["traceparent"] is string traceparent)
                {
                    tracingData = DistributedTracingData.TryDeserializeFromString(traceparent);
                }

                var transaction = Agent.Tracer.StartTransaction(receivedInfo.Queue, "event", tracingData);

                try
                {
                    await onMessage(body, properties, receivedInfo).ConfigureAwait(false);
                }
                finally
                {
                    transaction.End();
                }
            }

            return(base.Consume(queue, NewOnMessage, configure));
        }
 public void CaptureTransaction(
     string name,
     string type,
     Action action,
     DistributedTracingData distributedTracingData = null)
 {
     Agent.Tracer.CaptureTransaction(name, type, action, distributedTracingData);
 }
Exemple #10
0
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender,
            IConfigurationReader configurationReader
            )
        {
            Timestamp = TimeUtils.TimestampNow();
            var idBytes = new byte[8];

            Id      = RandomGenerator.GenerateRandomBytesAsString(idBytes);
            _logger = logger?.Scoped($"{nameof(Transaction)}.{Id}");

            _sender = sender;
            _configurationReader = configurationReader;

            Name          = name;
            HasCustomName = false;
            Type          = type;

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                var traceIdBytes = new byte[16];
                TraceId   = RandomGenerator.GenerateRandomBytesAsString(traceIdBytes);
                IsSampled = sampler.DecideIfToSample(idBytes);
            }
            else
            {
                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
            }

            SpanCount = new SpanCount();

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on incoming distributed tracing data ({DistributedTracingData})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, distributedTracingData, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
            else
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, sampler, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
        }
Exemple #11
0
        public override Task <Hipstershop.Cart> GetCart(GetCartRequest request, ServerCallContext context)
        {
            DistributedTracingData distributedTracingData = getDistributedTracingData(context);

            return(Agent.Tracer.CaptureTransaction("GetCart", ApiConstants.TypeRequest, (t) => {
                t.Labels["userId"] = request.UserId;
                return cartStore.GetCartAsync(request.UserId);
            }, distributedTracingData));
        }
Exemple #12
0
        public void HandleProcessError_should_call_CaptureTransaction_as_expected(Message message,
                                                                                  bool withTransactionName, bool withTrace)
        {
            var transactionName = withTransactionName ? TransactionName : HandleProcessErrorTransaction;
            var traceParent     = withTrace ? DistributedTracingData.TryDeserializeFromString(TraceParent) : null;

            _elasticApmMessageProcessor.HandleProcessError(message, _publisherMock.Object, new Exception(), _processorMock.Object.HandleProcessError);

            _elasticApmTracerMock.Verify(e => e.CaptureTransaction(transactionName, It.IsAny <string>(), It.IsAny <Func <bool> >(), traceParent));
        }
Exemple #13
0
        public void ProcessMessage_should_call_CaptureTransaction_as_expected(Message message, bool withTransactionName,
                                                                              bool withTrace)
        {
            var transactionName = withTransactionName ? TransactionName : ProcessMessageTransacion;
            var traceParent     = withTrace ? DistributedTracingData.TryDeserializeFromString(TraceParent) : null;

            _elasticApmMessageProcessor.ProcessMessage(message, _publisherMock.Object, _processorMock.Object.ProcessMessage);

            _elasticApmTracerMock.Verify(e => e.CaptureTransaction(transactionName, It.IsAny <string>(), It.IsAny <Action>(), traceParent));
        }
Exemple #14
0
        public async override Task <Empty> EmptyCart(EmptyCartRequest request, ServerCallContext context)
        {
            DistributedTracingData distributedTracingData = getDistributedTracingData(context);

            await Agent.Tracer.CaptureTransaction("EmptyCart", ApiConstants.TypeRequest, async (t) => {
                t.Labels["userId"] = request.UserId;
                await cartStore.EmptyCartAsync(request.UserId);
            }, distributedTracingData);

            return(Empty);
        }
Exemple #15
0
        private DistributedTracingData getDistributedTracingData(ServerCallContext context)
        {
            Metadata.Entry         metadataEntry          = context.RequestHeaders.FirstOrDefault(m => String.Equals(m.Key.ToLower(), "elastic-apm-traceparent", StringComparison.Ordinal));
            DistributedTracingData distributedTracingData = null;

            if (metadataEntry != null && !metadataEntry.Equals(default(Metadata.Entry)) && metadataEntry.Value != null)
            {
                distributedTracingData = DistributedTracingData.TryDeserializeFromString(metadataEntry.Value);
            }
            return(distributedTracingData);
        }
Exemple #16
0
        public async override Task <Empty> AddItem(AddItemRequest request, Grpc.Core.ServerCallContext context)
        {
            DistributedTracingData distributedTracingData = getDistributedTracingData(context);
            await Agent.Tracer.CaptureTransaction("AddItem", ApiConstants.TypeRequest, async (t) => {
                t.Labels["userId"]    = request.UserId;
                t.Labels["productId"] = request.Item.ProductId;
                t.Labels["quantity"]  = request.Item.Quantity.ToString();
                await cartStore.AddItemAsync(request.UserId, request.Item.ProductId, request.Item.Quantity);
            }, distributedTracingData);

            return(Empty);
        }
        static void Main(string[] args)
        {
            if (args.Length == 1)             //in case it's started with an argument we try to parse the argument as a DistributedTracingData
            {
                Console.WriteLine($"Process started - continuing trace with distributed tracing data: {args[0]}");

                var distributedTracingData = DistributedTracingData.TryDeserializeFromString(args[0]);
                var transaction2           = Agent.Tracer.StartTransaction("Transaction2", "TestTransaction", distributedTracingData);

                try
                {
                    transaction2.CaptureSpan("TestSpan", "TestSpanType", () => Thread.Sleep(200));
                }
                finally
                {
                    transaction2.End();
                }

                Thread.Sleep(2000);

                Console.WriteLine("About to exit - press any key...");
                Console.ReadKey();
            }
            else
            {
                Console.WriteLine("Started");

                Console.WriteLine("Capturing a transaction");
                var transaction = Agent.Tracer.StartTransaction("Transaction1", "TestTransaction");

                try
                {
                    var outgoingDistributedTracingData = Agent.Tracer.CurrentTransaction?.OutgoingDistributedTracingData?.SerializeToString();

                    Console.WriteLine($"The value of the distributed tracing data: {outgoingDistributedTracingData}");
                    Console.WriteLine($"Waiting 20 sec to continue the trace");

                    Thread.Sleep(20000);
                }
                finally
                {
                    transaction.End();
                }

                //WIP: if the process terminates the agent
                //potentially does not have time to send the transaction to the server.


                Console.WriteLine("About to exit - press any key...");
                Console.ReadKey();
            }
        }
Exemple #18
0
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender
            )
        {
            _logger = logger?.Scoped(nameof(Transaction));
            _sender = sender;
            _start  = DateTimeOffset.UtcNow;

            Name          = name;
            HasCustomName = false;
            Type          = type;
            var idBytes = new byte[8];

            Id = RandomGenerator.GenerateRandomBytesAsString(idBytes);

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                var traceIdBytes = new byte[16];
                TraceId   = RandomGenerator.GenerateRandomBytesAsString(traceIdBytes);
                IsSampled = sampler.DecideIfToSample(idBytes);
            }
            else
            {
                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
            }

            SpanCount = new SpanCount();

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()?.Log("New Transaction instance created: {Transaction}. " +
                                     "IsSampled ({IsSampled}) is based on incoming distributed tracing data ({DistributedTracingData})",
                                     this, IsSampled, distributedTracingData);
            }
            else
            {
                _logger.Trace()?.Log("New Transaction instance created: {Transaction}. " +
                                     "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})",
                                     this, IsSampled, sampler);
            }
        }
Exemple #19
0
        public async Task <CommandResult> Handle(TCommand command, RequestContext context)
        {
            var  commandName      = command.GetType().FullName;
            bool shouldCreateSpan = traceSettings?.ShouldCreateSpan(commandName) ?? false;

            CommandResult commandResult = null;

            if (!shouldCreateSpan)
            {
                commandResult = await decoratedRequestHandler.Handle(command, context);
            }

            if (!context.Headers.ContainsKey(DistributedTracingHeader.DistributedTracingDataKey))
            {
                string outgoingDistributedTracingData =
                    (tracer.CurrentSpan?.OutgoingDistributedTracingData
                     ?? tracer.CurrentTransaction?.OutgoingDistributedTracingData)?.SerializeToString();

                context.Headers.Add(DistributedTracingHeader.DistributedTracingDataKey, outgoingDistributedTracingData);
            }

            if (tracer.CurrentTransaction == null)
            {
                await tracer.CaptureTransaction(commandName.ToTransactionName(), commandName.ToTransactionType(), async(transaction) =>
                {
                    // transaction.Labels["TK"] = "kadirzade";
                    await tracer.CurrentTransaction.CaptureSpan(commandName.ToSpanName(), commandName.ToSpanType(), async(span) =>
                    {
                        commandResult = await decoratedRequestHandler.Handle(command, context);
                    });
                }, DistributedTracingData.TryDeserializeFromString(context.Headers[DistributedTracingHeader.DistributedTracingDataKey]));
            }
            else
            {
                await tracer.CurrentTransaction.CaptureSpan(commandName.ToSpanName(), commandName.ToSpanType(), async (span) =>
                {
                    commandResult         = await decoratedRequestHandler.Handle(command, context);
                    span.Labels["result"] = commandResult.FormatResult();
                });
            }

            return(commandResult);
        }
        public async Task <QueryResult <TResponse> > Handle(TQuery query, RequestContext context)
        {
            var  queryName        = query.GetType().FullName;
            bool shouldCreateSpan = traceSettings?.ShouldCreateSpan(queryName) ?? false;
            QueryResult <TResponse> queryResult = null;

            if (!shouldCreateSpan)
            {
                queryResult = await decoratedRequestHandler.Handle(query, context);
            }

            if (!context.Headers.ContainsKey(DistributedTracingHeader.DistributedTracingDataKey))
            {
                string outgoingDistributedTracingData =
                    (tracer.CurrentSpan?.OutgoingDistributedTracingData
                     ?? tracer.CurrentTransaction?.OutgoingDistributedTracingData)?.SerializeToString();

                context.Headers.Add(DistributedTracingHeader.DistributedTracingDataKey, outgoingDistributedTracingData);
            }

            if (tracer.CurrentTransaction == null)
            {
                await tracer.CaptureTransaction(queryName.ToTransactionName(), queryName.ToTransactionType(), async() =>
                {
                    await tracer.CurrentTransaction.CaptureSpan(queryName.ToSpanName(), queryName.ToSpanType(), async(span) =>
                    {
                        queryResult = await decoratedRequestHandler.Handle(query, context);
                    });
                }, DistributedTracingData.TryDeserializeFromString(context.Headers[DistributedTracingHeader.DistributedTracingDataKey]));
            }
            else
            {
                await tracer.CurrentTransaction.CaptureSpan(queryName.ToSpanName(), queryName.ToSpanType(), async (span) =>
                {
                    queryResult           = await decoratedRequestHandler.Handle(query, context);
                    span.Labels["result"] = queryResult.FormatResult();
                    span.Type             = "";
                });
            }

            return(queryResult);
        }
        private void FillSpanLinks(PropertyFetcherCollection cachedProperties, IExecutionSegment segment, KeyValuePair <string, object> kv)
        {
            var messages = cachedProperties.Fetch(kv.Value, "Messages") as IEnumerable <object>;

            var spanLinks = new List <SpanLink>();

            if (messages != null)
            {
                foreach (var message in messages)
                {
                    if (message.GetType().GetProperty("UserProperties")?.GetValue(message) is Dictionary <string, object> properties)
                    {
                        foreach (var property in properties)
                        {
                            if (property.Key.Equals("Diagnostic-Id", StringComparison.InvariantCultureIgnoreCase))
                            {
                                var parsedTraceParent = DistributedTracingData.TryDeserializeFromString(property.Value.ToString());
                                if (parsedTraceParent != null)
                                {
                                    spanLinks.Add(new SpanLink(parsedTraceParent.ParentId, parsedTraceParent.TraceId));
                                }
                            }
                        }
                    }
                }
            }

            switch (segment)
            {
            case Model.Transaction t: t.InsertSpanLinkInternal(spanLinks);
                break;

            case Model.Span s: s.InsertSpanLinkInternal(spanLinks);
                break;
            }
        }
        public bool HandleProcessError(IMessage message, IPublisher publisher, Exception error, Func <IMessage, IPublisher, Exception, bool> handleProcessError)
        {
            var method = new StackFrame().GetMethod();
            var name   = GetName(message, method, handleProcessError.GetMethodInfo().Name);

            if (!message.Headers.ContainsKey(ApmConstants.TraceParent))
            {
                return(_elasticApmtracer.CaptureTransaction(name, TransactionType, () => handleProcessError(message, publisher, error)));
            }
            var traceParent = message.Headers[ApmConstants.TraceParent].ToString();

            return(_elasticApmtracer.CaptureTransaction(name, TransactionType, () => handleProcessError(message, publisher, error), DistributedTracingData.TryDeserializeFromString(traceParent)));
        }
Exemple #23
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseAllElasticApm(_configuration);

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    var output = new StringBuilder();
                    output.Append("<ul>");
                    output.Append("<li><a href=\"/trans1/1\">Distributed Transaction 1</a></li>");
                    output.Append("<li><a href=\"/trans2\">Distributed Transaction 2</a></li>");
                    output.Append("</ul>");

                    context.Response.ContentType = "text/html";
                    await context.Response.WriteAsync(output.ToString());
                });

                endpoints.MapGet("/trans1/1", async context =>
                {
                    var trans = Agent.Tracer.StartTransaction("Dist Trans 1", ApiConstants.TypeRequest);
                    await trans.CaptureSpan("step 1 processing", ApiConstants.ActionExec, async() => await Task.Delay(30));
                    trans.End();

                    var trace = trans.OutgoingDistributedTracingData.SerializeToString();

                    await context.Response.WriteAsync($"<a href=\"/trans1/2?s={trace}\">Continue</a>");
                });

                endpoints.MapGet("/trans1/2", async context =>
                {
                    var deser = DistributedTracingData.TryDeserializeFromString(context.Request.Query["s"]);

                    var trans = Agent.Tracer.StartTransaction("Dist Trans 1", ApiConstants.TypeRequest,
                                                              deser);
                    await trans.CaptureSpan("step 2 processing", ApiConstants.ActionExec, async() => await Task.Delay(15));
                    trans.End();

                    var trace = trans.OutgoingDistributedTracingData.SerializeToString();

                    await context.Response.WriteAsync($"<a href=\"/trans1/3?s={trace}\">Continue</a>");
                });

                endpoints.MapGet("/trans1/3", async context =>
                {
                    var deser = DistributedTracingData.TryDeserializeFromString(context.Request.Query["s"]);

                    var trans = Agent.Tracer.StartTransaction("Dist Trans 1", ApiConstants.TypeRequest,
                                                              deser);
                    await trans.CaptureSpan("step 3 processing", ApiConstants.ActionExec, async() => await Task.Delay(40));
                    trans.End();

                    await context.Response.WriteAsync($"<a href=\"/trans1/1\">Restart</a> / <a href=\"/\">Exit</a>");
                });

                endpoints.MapGet("/trans2", async context =>
                {
                    // transaction 1
                    var trans1 = Agent.Tracer.StartTransaction("Dist Trans 2", ApiConstants.TypeRequest);
                    await trans1.CaptureSpan("step 1 processing", ApiConstants.ActionExec, async() => await Task.Delay(30));
                    trans1.End();

                    // transaction 2
                    var trans2 = Agent.Tracer.StartTransaction("Dist Trans 2", ApiConstants.TypeRequest,
                                                               DistributedTracingData.TryDeserializeFromString(trans1.OutgoingDistributedTracingData.SerializeToString()));
                    await trans2.CaptureSpan("step 2 processing", ApiConstants.ActionExec, async() => await Task.Delay(30));
                    trans2.End();

                    // transaction 3
                    var trans3 = Agent.Tracer.StartTransaction("Dist Trans 2", ApiConstants.TypeRequest,
                                                               DistributedTracingData.TryDeserializeFromString(trans2.OutgoingDistributedTracingData.SerializeToString()));
                    await trans3.CaptureSpan("step 3 processing", ApiConstants.ActionExec, async() => await Task.Delay(30));
                    trans3.End();

                    await context.Response.WriteAsync($"<a href=\"/trans2\">Restart</a> / <a href=\"/\">Exit</a>");
                });
            });
        }
Exemple #24
0
        internal static ITransaction CreateConsumerTransaction(
            IApmAgent agent,
            string topic,
            Partition?partition,
            Offset?offset,
            IMessage message)
        {
            ITransaction transaction = null;

            try
            {
                if (agent.Tracer.CurrentTransaction is not null)
                {
                    return(null);
                }

                if (agent is ApmAgent apmAgent)
                {
                    var matcher = WildcardMatcher.AnyMatch(apmAgent.ConfigurationStore.CurrentSnapshot.IgnoreMessageQueues, topic);
                    if (matcher != null)
                    {
                        agent.Logger.Trace()
                        ?.Log(
                            "Not tracing message from {Queue} because it matched IgnoreMessageQueues pattern {Matcher}",
                            topic,
                            matcher.GetMatcher());

                        return(null);
                    }
                }

                DistributedTracingData distributedTracingData = null;
                if (message?.Headers != null)
                {
                    var headers = new KafkaHeadersCollection(message.Headers, agent.Logger);
                    try
                    {
                        var traceParent = string.Join(",", headers.GetValues(TraceContext.TraceParentBinaryHeaderName));
                        var traceState  = headers.GetValues(TraceContext.TraceStateHeaderName).FirstOrDefault();
                        distributedTracingData = TraceContext.TryExtractTracingData(traceParent, traceState);
                    }
                    catch (Exception ex)
                    {
                        agent.Logger.Error()?.LogException(ex, "Error extracting propagated headers from Kafka message");
                    }
                }

                var name = string.IsNullOrEmpty(topic)
                                        ? "Kafka RECEIVE"
                                        : $"Kafka RECEIVE from {topic}";

                transaction = agent.Tracer.StartTransaction(name, ApiConstants.TypeMessaging, distributedTracingData);

                if (partition is not null)
                {
                    transaction.SetLabel("partition", partition.ToString());
                }

                if (offset is not null)
                {
                    transaction.SetLabel("offset", offset.ToString());
                }

                // record only queue topic name and age on context for now. capture body and headers potentially in future
                transaction.Context.Message = new Message {
                    Queue = new Queue {
                        Name = topic
                    }
                };
                if (transaction is Transaction realTransaction && message is not null && message.Timestamp.Type != 0)
                {
                    var consumeTime = TimeUtils.ToDateTime(realTransaction.Timestamp);
                    var produceTime = message.Timestamp.UtcDateTime;

                    var age = Math.Max(0, (consumeTime - produceTime).TotalMilliseconds);
                    if (age > 0 && age < MaxAge)
                    {
                        transaction.Context.Message.Age = new Age {
                            Ms = (long)age
                        }
                    }
                    ;
                }

                if (message is not null && message.Value is null)
                {
                    transaction.SetLabel("tombstone", "true");
                }
            }
            catch (Exception ex)
            {
                agent.Logger.Error()?.LogException(ex, "Error creating or populating transaction.");
            }

            return(transaction);
        }
 private static DistributedTracingData BuildDistributedTracingData(string traceId, string parentId, string traceFlags) =>
 DistributedTracingData.TryDeserializeFromString(
     "00-" +                     // version
     (traceId == null ? "" : $"{traceId}") +
     (parentId == null ? "" : $"-{parentId}") +
     (traceFlags == null ? "" : $"-{traceFlags}"));
Exemple #26
0
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender,
            IConfigSnapshot configSnapshot,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer
            )
        {
            ConfigSnapshot = configSnapshot;
            Timestamp      = TimeUtils.TimestampNow();
            var idBytes = new byte[8];

            Id      = RandomGenerator.GenerateRandomBytesAsString(idBytes);
            _logger = logger?.Scoped($"{nameof(Transaction)}.{Id}");

            _sender = sender;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;

            Name          = name;
            HasCustomName = false;
            Type          = type;

            StartActivity();

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                // Here we ignore Activity.Current.ActivityTraceFlags because it starts out without setting the IsSampled flag, so relying on that would mean a transaction is never sampled.
                IsSampled = sampler.DecideIfToSample(idBytes);

                if (Activity.Current != null && Activity.Current.IdFormat == ActivityIdFormat.W3C)
                {
                    TraceId  = Activity.Current.TraceId.ToString();
                    ParentId = Activity.Current.ParentId;

                    // Also mark the sampling decision on the Activity
                    if (IsSampled)
                    {
                        Activity.Current.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
                    }
                }
                else
                {
                    TraceId = _activity.TraceId.ToString();
                }
            }
            else
            {
                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
                _traceState = distributedTracingData.TraceState;
            }

            SpanCount = new SpanCount();

            _currentExecutionSegmentsContainer.CurrentTransaction = this;

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on incoming distributed tracing data ({DistributedTracingData})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, distributedTracingData, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
            else
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, sampler, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }

            void StartActivity()
            {
                _activity = new Activity("ElasticApm.Transaction");
                _activity.SetIdFormat(ActivityIdFormat.W3C);
                _activity.Start();
            }
        }
        public void ProcessMessage(IMessage message, IPublisher publisher, Action <IMessage, IPublisher> processMessage)
        {
            var method = new StackFrame().GetMethod();
            var name   = GetName(message, method, processMessage.GetMethodInfo().Name);
            var traceAsyncTransaction = GetTraceAsyncTransaction(message);

            if (message.Headers.ContainsKey(ApmConstants.TraceParent) && traceAsyncTransaction)
            {
                var traceParent = message.Headers[ApmConstants.TraceParent].ToString();
                _elasticApmtracer.CaptureTransaction(name, TransactionType, () => processMessage(message, publisher), DistributedTracingData.TryDeserializeFromString(traceParent));
            }
            else
            {
                _elasticApmtracer.CaptureTransaction(name, TransactionType, () => processMessage(message, publisher));
            }
        }
Exemple #28
0
 public static string BuildTraceparent(DistributedTracingData distributedTracingData)
 => $"00-{distributedTracingData.TraceId}-{distributedTracingData.ParentId}-{(distributedTracingData.FlagRecorded ? "01" : "00")}";
Exemple #29
0
        /// <summary>
        /// Creates a new transaction
        /// </summary>
        /// <param name="logger">The logger which logs debug information during the transaction  creation process</param>
        /// <param name="name">The name of the transaction</param>
        /// <param name="type">The type of the transaction</param>
        /// <param name="sampler">The sampler implementation which makes the sampling decision</param>
        /// <param name="distributedTracingData">Distributed tracing data, in case this transaction is part of a distributed trace</param>
        /// <param name="sender">The IPayloadSender implementation which will record this transaction</param>
        /// <param name="configSnapshot">The current configuration snapshot which contains the up-do-date config setting values</param>
        /// <param name="currentExecutionSegmentsContainer" />
        /// The ExecutionSegmentsContainer which makes sure this transaction flows
        /// <param name="ignoreActivity">
        /// If set the transaction will ignore Activity.Current and it's trace id,
        /// otherwise the agent will try to keep ids in-sync across async work-flows
        /// </param>
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender,
            IConfigSnapshot configSnapshot,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer,
            bool ignoreActivity = false
            )
        {
            ConfigSnapshot = configSnapshot;
            Timestamp      = TimeUtils.TimestampNow();

            _logger = logger?.Scoped($"{nameof(Transaction)}");

            _sender = sender;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;

            Name          = name;
            HasCustomName = false;
            Type          = type;

            // For each transaction start, we fire an Activity
            // If Activity.Current is null, then we create one with this and set its traceid, which will flow to all child activities and we also reuse it in Elastic APM, so it'll be the same on all Activities and in Elastic
            // If Activity.Current is not null, we pick up its traceid and apply it in Elastic APM
            if (!ignoreActivity)
            {
                StartActivity();
            }

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                // Here we ignore Activity.Current.ActivityTraceFlags because it starts out without setting the IsSampled flag, so relying on that would mean a transaction is never sampled.
                // To be sure activity creation was successful let's check on it
                if (_activity != null)
                {
                    // In case activity creation was successful, let's reuse the ids
                    Id      = _activity.SpanId.ToHexString();
                    TraceId = _activity.TraceId.ToHexString();

                    var idBytesFromActivity = new Span <byte>(new byte[16]);
                    _activity.TraceId.CopyTo(idBytesFromActivity);
                    // Read right most bits. From W3C TraceContext: "it is important for trace-id to carry "uniqueness" and "randomness" in the right part of the trace-id..."
                    idBytesFromActivity = idBytesFromActivity.Slice(8);
                    IsSampled           = sampler.DecideIfToSample(idBytesFromActivity.ToArray());
                }
                else
                {
                    // In case from some reason the activity creation was not successful, let's create new random ids
                    var idBytes = new byte[8];
                    Id        = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                    IsSampled = sampler.DecideIfToSample(idBytes);

                    idBytes = new byte[16];
                    TraceId = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                }

                // PrentId could be also set here, but currently in the UI each trace must start with a transaction where the ParentId is null,
                // so to avoid https://github.com/elastic/apm-agent-dotnet/issues/883 we don't set it yet.
            }
            else
            {
                if (_activity != null)
                {
                    Id = _activity.SpanId.ToHexString();
                }
                else
                {
                    var idBytes = new byte[8];
                    Id = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                }
                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
                _traceState = distributedTracingData.TraceState;
            }

            // Also mark the sampling decision on the Activity
            if (IsSampled && _activity != null && !ignoreActivity)
            {
                _activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
            }

            SpanCount = new SpanCount();
            _currentExecutionSegmentsContainer.CurrentTransaction = this;

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on incoming distributed tracing data ({DistributedTracingData})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, distributedTracingData, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
            else
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, sampler, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }

            void StartActivity()
            {
                _activity = new Activity(ApmTransactionActivityName);
                _activity.SetIdFormat(ActivityIdFormat.W3C);
                _activity.Start();
            }
        }
Exemple #30
0
        /// <summary>
        /// Creates a new transaction
        /// </summary>
        /// <param name="logger">The logger which logs debug information during the transaction  creation process</param>
        /// <param name="name">The name of the transaction</param>
        /// <param name="type">The type of the transaction</param>
        /// <param name="sampler">The sampler implementation which makes the sampling decision</param>
        /// <param name="distributedTracingData">Distributed tracing data, in case this transaction is part of a distributed trace</param>
        /// <param name="sender">The IPayloadSender implementation which will record this transaction</param>
        /// <param name="configSnapshot">The current configuration snapshot which contains the up-do-date config setting values</param>
        /// <param name="currentExecutionSegmentsContainer" />
        /// The ExecutionSegmentsContainer which makes sure this transaction flows
        /// <param name="apmServerInfo">Component to fetch info about APM Server (e.g. APM Server version)</param>
        /// <param name="ignoreActivity">
        /// If set the transaction will ignore Activity.Current and it's trace id,
        /// otherwise the agent will try to keep ids in-sync across async work-flows
        /// </param>
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender,
            IConfigSnapshot configSnapshot,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer,
            IApmServerInfo apmServerInfo,
            bool ignoreActivity = false
            )
        {
            ConfigSnapshot = configSnapshot;
            Timestamp      = TimeUtils.TimestampNow();

            _logger        = logger?.Scoped(nameof(Transaction));
            _apmServerInfo = apmServerInfo;
            _sender        = sender;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;

            Name          = name;
            HasCustomName = false;
            Type          = type;

            // For each new transaction, start an Activity if we're not ignoring them.
            // If Activity.Current is not null, the started activity will be a child activity,
            // so the traceid and tracestate of the parent will flow to it.
            if (!ignoreActivity)
            {
                _activity = StartActivity();
            }

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                // We consider a newly created transaction **without** explicitly passed distributed tracing data
                // to be a root transaction.
                // Ignore the created activity ActivityTraceFlags because it starts out without setting the IsSampled flag,
                // so relying on that would mean a transaction is never sampled.
                if (_activity != null)
                {
                    // If an activity was created, reuse its id
                    Id      = _activity.SpanId.ToHexString();
                    TraceId = _activity.TraceId.ToHexString();

                    var idBytesFromActivity = new Span <byte>(new byte[16]);
                    _activity.TraceId.CopyTo(idBytesFromActivity);

                    // Read right most bits. From W3C TraceContext: "it is important for trace-id to carry "uniqueness" and "randomness"
                    // in the right part of the trace-id..."
                    idBytesFromActivity = idBytesFromActivity.Slice(8);

                    _traceState = new TraceState();

                    // If activity has a tracestate, populate the transaction tracestate with it.
                    if (!string.IsNullOrEmpty(_activity.TraceStateString))
                    {
                        _traceState.AddTextHeader(_activity.TraceStateString);
                    }

                    IsSampled = sampler.DecideIfToSample(idBytesFromActivity.ToArray());

                    // In the unlikely event that tracestate populated from activity contains an es vendor key, the tracestate
                    // is mutated to set the sample rate defined by the sampler, because we consider a transaction without
                    // explicitly passed distributedTracingData to be a **root** transaction. The end result
                    // is that activity tracestate will be propagated, along with the sample rate defined by this transaction.
                    if (IsSampled)
                    {
                        SampleRate = sampler.Rate;
                        _traceState.SetSampleRate(sampler.Rate);
                    }
                    else
                    {
                        SampleRate = 0;
                        _traceState.SetSampleRate(0);
                    }

                    // sync the activity tracestate with the tracestate of the transaction
                    _activity.TraceStateString = _traceState.ToTextHeader();
                }
                else
                {
                    // If no activity is created, create new random ids
                    var idBytes = new byte[8];
                    Id        = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                    IsSampled = sampler.DecideIfToSample(idBytes);

                    idBytes = new byte[16];
                    TraceId = RandomGenerator.GenerateRandomBytesAsString(idBytes);

                    if (IsSampled)
                    {
                        _traceState = new TraceState(sampler.Rate);
                        SampleRate  = sampler.Rate;
                    }
                    else
                    {
                        _traceState = new TraceState(0);
                        SampleRate  = 0;
                    }
                }

                // ParentId could be also set here, but currently in the UI each trace must start with a transaction where the ParentId is null,
                // so to avoid https://github.com/elastic/apm-agent-dotnet/issues/883 we don't set it yet.
            }
            else
            {
                if (_activity != null)
                {
                    Id = _activity.SpanId.ToHexString();

                    // try to set the parent id and tracestate on the created activity, based on passed distributed tracing data.
                    // This is so that the distributed tracing data will flow to any child activities
                    try
                    {
                        _activity.SetParentId(
                            ActivityTraceId.CreateFromString(distributedTracingData.TraceId.AsSpan()),
                            ActivitySpanId.CreateFromString(distributedTracingData.ParentId.AsSpan()),
                            distributedTracingData.FlagRecorded ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None);

                        if (distributedTracingData.HasTraceState)
                        {
                            _activity.TraceStateString = distributedTracingData.TraceState.ToTextHeader();
                        }
                    }
                    catch (Exception e)
                    {
                        _logger.Error()?.LogException(e, "Error setting trace context on created activity");
                    }
                }
                else
                {
                    var idBytes = new byte[8];
                    Id = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                }

                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
                _traceState = distributedTracingData.TraceState;

                // If there is no tracestate or no valid "es" vendor entry with an "s" (sample rate) attribute, then the agent must
                // omit sample rate from non-root transactions and their spans.
                // See https://github.com/elastic/apm/blob/master/specs/agents/tracing-sampling.md#propagation
                if (_traceState?.SampleRate is null)
                {
                    SampleRate = null;
                }
                else
                {
                    SampleRate = _traceState.SampleRate.Value;
                }
            }

            // Also mark the sampling decision on the Activity
            if (IsSampled && _activity != null)
            {
                _activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
            }

            SpanCount = new SpanCount();
            _currentExecutionSegmentsContainer.CurrentTransaction = this;

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) and SampleRate ({SampleRate}) is based on incoming distributed tracing data ({DistributedTracingData})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, SampleRate, distributedTracingData, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
            else
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, sampler, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
        }