Beispiel #1
0
        internal bool TryStartElasticsearchSpan(string name, out Span span, Uri instanceUri = null)
        {
            span = null;
            var transaction = ApmAgent.Tracer.CurrentTransaction;

            if (transaction == null)
            {
                return(false);
            }

            span = (Span)ExecutionSegmentCommon.GetCurrentExecutionSegment(ApmAgent)
                   .StartSpan(
                name,
                ApiConstants.TypeDb,
                ApiConstants.SubtypeElasticsearch);

            span.Action = name;
            SetDbContext(span, instanceUri);
            SetDestination(span, instanceUri);

            var id = Activity.Current.Id;

            if (Spans.TryAdd(id, span))
            {
                return(true);
            }

            Logger.Error()?.Log("Failed to register start of span in ConcurrentDictionary {SpanDetails}", span.ToString());
            span = null;
            return(false);
        }
Beispiel #2
0
        public ISpan StartSpan(IApmAgent agent, string method, Uri requestUrl, Func <string, string> headerGetter)
        {
            var resourcePath  = ParseResourcePath(requestUrl.PathAndQuery);
            var operation     = BuildOperationMoniker(method, resourcePath);
            var operationName = GetOperationName(operation);

            var spanName = operationName.Length > 4
                                ? $"Cosmos DB {operationName}"
                                : "Cosmos DB";

            var span = ExecutionSegmentCommon.StartSpanOnCurrentExecutionSegment(agent, spanName, ApiConstants.TypeDb, ApiConstants.SubTypeCosmosDb);

            if (span != null)
            {
                span.Context.Db = new Database();
                foreach (var resource in resourcePath)
                {
                    if (resource.Value != null)
                    {
                        if (resource.Key == "dbs")
                        {
                            span.Context.Db.Instance = resource.Value;
                        }

                        var propertyName = GetPropertyNameForResource(resource.Key);
                        if (!string.IsNullOrEmpty(propertyName))
                        {
                            span.Name += $" {resource.Value}";
                        }
                    }
                }
            }

            return(span);
        }
        private void ProcessStartEvent(TRequest request, Uri requestUrl)
        {
            Logger.Trace()?.Log("Processing start event... Request URL: {RequestUrl}", Http.Sanitize(requestUrl));

            var transaction = ApmAgent.Tracer.CurrentTransaction;

            if (transaction == null)
            {
                Logger.Debug()?.Log("No current transaction, skip creating span for outgoing HTTP request");
                return;
            }

            var span = ExecutionSegmentCommon.StartSpanOnCurrentExecutionSegment(ApmAgent, $"{RequestGetMethod(request)} {requestUrl.Host}",
                                                                                 ApiConstants.TypeExternal, ApiConstants.SubtypeHttp, InstrumentationFlag.HttpClient, true);

            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, TraceContext.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, TraceContext.TraceParentHeaderName, TraceContext.BuildTraceparent(span.OutgoingDistributedTracingData));
            }

            if (transaction is Transaction t)
            {
                if (t.ConfigSnapshot.UseElasticTraceparentHeader)
                {
                    if (!RequestHeadersContain(request, TraceContext.TraceParentHeaderNamePrefixed))
                    {
                        RequestHeadersAdd(request, TraceContext.TraceParentHeaderNamePrefixed,
                                          TraceContext.BuildTraceparent(span.OutgoingDistributedTracingData));
                    }
                }
            }

            if (!RequestHeadersContain(request, TraceContext.TraceStateHeaderName) && span.OutgoingDistributedTracingData != null && span.OutgoingDistributedTracingData.HasTraceState)
            {
                RequestHeadersAdd(request, TraceContext.TraceStateHeaderName, span.OutgoingDistributedTracingData.TraceState.ToTextHeader());
            }

            if (span is Span spanInstance)
            {
                if (!spanInstance.ShouldBeSentToApmServer)
                {
                    return;
                }
            }

            span.Context.Http = new Http {
                Method = RequestGetMethod(request)
            };
            span.Context.Http.SetUrl(requestUrl);
        }
Beispiel #4
0
        /// <summary>
        /// Gets a profiling session for StackExchange.Redis to add redis commands to.
        /// Creates a profiling session per span or transaction
        /// </summary>
        /// <remarks>
        /// See https://stackexchange.github.io/StackExchange.Redis/Profiling_v2.html
        /// </remarks>
        /// <returns>A profiling session for the current span or transaction, or null if the agent is not enabled or not recording</returns>
        public ProfilingSession GetProfilingSession()
        {
            if (!Agent.Config.Enabled || !Agent.Config.Recording)
            {
                return(null);
            }

            var         executionSegment = ExecutionSegmentCommon.GetCurrentExecutionSegment(_agent);
            var         realSpan         = executionSegment as Span;
            Transaction realTransaction  = null;

            // don't profile when there's no real span or transaction
            if (realSpan is null)
            {
                realTransaction = executionSegment as Transaction;
                if (realTransaction is null)
                {
                    return(null);
                }
            }

            var isSpan = realSpan != null;

            if (!_executionSegmentSessions.TryGetValue(executionSegment.Id, out var session))
            {
                _logger.Trace()?.Log("Creating profiling session for {ExecutionSegment} {Id}",
                                     isSpan ? "span" : "transaction",
                                     executionSegment.Id);

                session = new ProfilingSession();

                if (!_executionSegmentSessions.TryAdd(executionSegment.Id, session))
                {
                    _logger.Debug()?.Log("could not add profiling session to tracked sessions for {ExecutionSegment} {Id}",
                                         isSpan ? "span" : "transaction",
                                         executionSegment.Id);
                }

                if (isSpan)
                {
                    realSpan.Ended += (sender, _) => EndProfilingSession(sender, session);
                }
                else
                {
                    realTransaction.Ended += (sender, _) => EndProfilingSession(sender, session);
                }
            }

            return(session);
        }
        private void ProcessStartEvent(TRequest request, Uri requestUrl)
        {
            Logger.Trace()?.Log("Processing start event... Request URL: {RequestUrl}", 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)
            {
                span.Context.Http = new Http {
                    Url = requestUrl.ToString(), Method = RequestGetMethod(request)
                }
            }
            ;
        }
        private void ProcessStartEvent(TRequest request, Uri requestUrl)
        {
            if (_realAgent?.TracerInternal.CurrentSpan is Span currentSpan)
            {
                // if the current span is an exit span, don't create a span for the current request
                if (currentSpan.InstrumentationFlag == InstrumentationFlag.Azure || currentSpan.InstrumentationFlag == InstrumentationFlag.Elasticsearch)
                {
                    return;
                }
            }

            Logger.Trace()?.Log("Processing start event... Request URL: {RequestUrl}", Http.Sanitize(requestUrl));

            var transaction = ApmAgent.Tracer.CurrentTransaction;

            if (transaction == null)
            {
                Logger.Debug()?.Log("No current transaction, skip creating span for outgoing HTTP request");
                return;
            }

            var method = RequestGetMethod(request);

            string HeaderGetter(string header) => RequestTryGetHeader(request, header, out var value) ? value : null;

            ISpan span = null;

            if (_configuration?.HasTracers ?? false)
            {
                using (var httpTracers = _configuration.GetTracers())
                {
                    foreach (var httpSpanTracer in httpTracers)
                    {
                        if (httpSpanTracer.IsMatch(method, requestUrl, HeaderGetter))
                        {
                            span = httpSpanTracer.StartSpan(ApmAgent, method, requestUrl, HeaderGetter);
                            if (span != null)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            if (span is null)
            {
                if (_configuration?.CaptureSpan ?? false)
                {
                    span = ExecutionSegmentCommon.StartSpanOnCurrentExecutionSegment(ApmAgent, $"{method} {requestUrl.Host}",
                                                                                     ApiConstants.TypeExternal, ApiConstants.SubtypeHttp, InstrumentationFlag.HttpClient, true);

                    if (span is null)
                    {
                        Logger.Trace()?.Log("Could not create span for outgoing HTTP request to {RequestUrl}", Http.Sanitize(requestUrl));
                        return;
                    }
                }
                else
                {
                    Logger.Trace()?.Log("Skip creating span for outgoing HTTP request to {RequestUrl} as not to known service", Http.Sanitize(requestUrl));
                    return;
                }
            }

            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, TraceContext.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, TraceContext.TraceParentHeaderName, TraceContext.BuildTraceparent(span.OutgoingDistributedTracingData));
            }

            if (transaction is Transaction t)
            {
                if (t.ConfigSnapshot.UseElasticTraceparentHeader)
                {
                    if (!RequestHeadersContain(request, TraceContext.TraceParentHeaderNamePrefixed))
                    {
                        RequestHeadersAdd(request, TraceContext.TraceParentHeaderNamePrefixed,
                                          TraceContext.BuildTraceparent(span.OutgoingDistributedTracingData));
                    }
                }
            }

            if (!RequestHeadersContain(request, TraceContext.TraceStateHeaderName) && span.OutgoingDistributedTracingData != null && span.OutgoingDistributedTracingData.HasTraceState)
            {
                RequestHeadersAdd(request, TraceContext.TraceStateHeaderName, span.OutgoingDistributedTracingData.TraceState.ToTextHeader());
            }

            if (span is Span realSpan)
            {
                if (!realSpan.ShouldBeSentToApmServer)
                {
                    return;
                }
            }

            span.Context.Http = new Http {
                Method = method
            };
            span.Context.Http.SetUrl(requestUrl);
        }
        public ISpan StartSpan(IApmAgent agent, string method, Uri requestUrl, Func <string, string> headerGetter)
        {
            var    blobUrl = new BlobUrl(requestUrl);
            string action  = null;

            switch (method)
            {
            case "DELETE":
                action = "Delete";
                break;

            case "GET":
                if (requestUrl.Query.Contains("restype=container"))
                {
                    if (requestUrl.Query.Contains("comp=list"))
                    {
                        action = "ListBlobs";
                    }
                    else if (requestUrl.Query.Contains("comp=acl"))
                    {
                        action = "GetAcl";
                    }
                    else
                    {
                        action = "GetProperties";
                    }
                }
                else
                {
                    if (requestUrl.Query.Contains("comp=metadata"))
                    {
                        action = "GetMetadata";
                    }
                    else if (requestUrl.Query.Contains("comp=list"))
                    {
                        action = "ListContainers";
                    }
                    else if (requestUrl.Query.Contains("comp=tags"))
                    {
                        action = requestUrl.Query.Contains("where=") ? "FindTags" : "GetTags";
                    }
                    else
                    {
                        action = "Download";
                    }
                }
                break;

            case "HEAD":
                if (requestUrl.Query.Contains("comp=metadata"))
                {
                    action = "GetMetadata";
                }
                else if (requestUrl.Query.Contains("comp=acl"))
                {
                    action = "GetAcl";
                }
                else
                {
                    action = "GetProperties";
                }
                break;

            case "POST":
                if (requestUrl.Query.Contains("comp=batch"))
                {
                    action = "Batch";
                }
                else if (requestUrl.Query.Contains("comp=query"))
                {
                    action = "Query";
                }
                break;

            case "PUT":
                if (!string.IsNullOrEmpty(headerGetter("x-ms-copy-source")))
                {
                    action = "Copy";
                }
                else if (requestUrl.Query.Contains("comp=copy"))
                {
                    action = "Abort";
                }
                else if (!string.IsNullOrEmpty(headerGetter("x-ms-blob-type")) ||
                         requestUrl.Query.Contains("comp=block") ||
                         requestUrl.Query.Contains("comp=blocklist") ||
                         requestUrl.Query.Contains("comp=page") ||
                         requestUrl.Query.Contains("comp=appendblock"))
                {
                    action = "Upload";
                }
                else if (requestUrl.Query.Contains("comp=metadata"))
                {
                    action = "SetMetadata";
                }
                else if (requestUrl.Query.Contains("comp=acl"))
                {
                    action = "SetAcl";
                }
                else if (requestUrl.Query.Contains("comp=properties"))
                {
                    action = "SetProperties";
                }
                else if (requestUrl.Query.Contains("comp=lease"))
                {
                    action = "Lease";
                }
                else if (requestUrl.Query.Contains("comp=snapshot"))
                {
                    action = "Snapshot";
                }
                else if (requestUrl.Query.Contains("comp=undelete"))
                {
                    action = "Undelete";
                }
                else if (requestUrl.Query.Contains("comp=tags"))
                {
                    action = "SetTags";
                }
                else if (requestUrl.Query.Contains("comp=tier"))
                {
                    action = "SetTier";
                }
                else if (requestUrl.Query.Contains("comp=expiry"))
                {
                    action = "SetExpiry";
                }
                else if (requestUrl.Query.Contains("comp=seal"))
                {
                    action = "Seal";
                }
                else
                {
                    action = "Create";
                }

                break;
            }

            if (action is null)
            {
                return(null);
            }

            var name = $"{AzureBlobStorage.SpanName} {action} {blobUrl.ResourceName}";

            var span = ExecutionSegmentCommon.StartSpanOnCurrentExecutionSegment(agent, name,
                                                                                 ApiConstants.TypeStorage, AzureBlobStorage.SubType, InstrumentationFlag.Azure, true);

            span.Action = action;
            span.Context.Destination = new Destination
            {
                Address = blobUrl.FullyQualifiedNamespace,
                Service = new Destination.DestinationService
                {
                    Name     = AzureBlobStorage.SubType,
                    Resource = $"{AzureBlobStorage.SubType}/{blobUrl.ResourceName}",
                    Type     = ApiConstants.TypeStorage
                }
            };

            if (span is Span realSpan)
            {
                realSpan.InstrumentationFlag = InstrumentationFlag.Azure;
            }

            return(span);
        }