public Span(
            string name,
            string type,
            string parentId,
            string traceId,
            Transaction enclosingTransaction,
            bool isSampled,
            IPayloadSender payloadSender,
            IApmLogger logger
            )
        {
            _start                = DateTimeOffset.UtcNow;
            _payloadSender        = payloadSender;
            _enclosingTransaction = enclosingTransaction;
            _logger               = logger?.Scoped(nameof(Span));
            Name      = name;
            Type      = type;
            IsSampled = isSampled;

            Id       = RandomGenerator.GenerateRandomBytesAsString(new byte[8]);
            ParentId = parentId;
            TraceId  = traceId;

            if (IsSampled)
            {
                // Started spans should be counted only for sampled transactions
                enclosingTransaction.SpanCount.Started++;

                // Spans are sent only for sampled transactions so it's only worth capturing stack trace for sampled spans
                StackTrace = StacktraceHelper.GenerateApmStackTrace(new StackTrace(true).GetFrames(), _logger, $"Span `{Name}'");
            }
        }
Beispiel #2
0
        public void CaptureException(Exception exception, string culprit = null, bool isHandled = false)
        {
            var capturedCulprit = string.IsNullOrEmpty(culprit) ? "PublicAPI-CaptureException" : culprit;
            var error           = new Error.Err
            {
                Culprit   = capturedCulprit,
                Exception = new CapturedException
                {
                    Message = exception.Message,
                    Type    = exception.GetType().FullName,
                    Handled = isHandled
                },
                Transaction = new Error.Err.Trans
                {
                    Id = Id
                }
            };

            if (!string.IsNullOrEmpty(exception.StackTrace))
            {
                error.Exception.StacktTrace
                    = StacktraceHelper.GenerateApmStackTrace(new StackTrace(exception).GetFrames(), _logger,
                                                             "failed capturing stacktrace");
            }

            error.Context = Context;
            _sender.QueueError(new Error {
                Errors = new List <Error.Err> {
                    error
                }, Service = Service
            });
        }
Beispiel #3
0
        public void CaptureError(string message, string culprit, StackFrame[] frames)
        {
            var error = new Error.Err
            {
                Culprit   = culprit,
                Exception = new CapturedException
                {
                    Message = message
                },
                Transaction = new Error.Err.Trans
                {
                    Id = Id
                }
            };

            if (frames != null)
            {
                error.Exception.StacktTrace
                    = StacktraceHelper.GenerateApmStackTrace(frames, _logger, "failed capturing stacktrace");
            }

            error.Context = Context;
            _sender.QueueError(new Error {
                Errors = new List <Error.Err> {
                    error
                }, Service = Service
            });
        }
        public void CaptureException(Exception exception, string culprit = null, bool isHandled = false)
        {
            var capturedCulprit = String.IsNullOrEmpty(culprit) ? "PublicAPI-CaptureException" : culprit;
            var error           = new Error.Err
            {
                Culprit   = capturedCulprit,
                Exception = new CapturedException
                {
                    Message = exception.Message,
                    Type    = exception.GetType().FullName,
                    Handled = isHandled
                },
                Transaction = new Error.Err.Trans
                {
                    Id = this.Id
                }
            };

            if (!String.IsNullOrEmpty(exception.StackTrace))
            {
                error.Exception.Stacktrace
                    = StacktraceHelper.GenerateApmStackTrace(new System.Diagnostics.StackTrace(exception).GetFrames(), Api.ElasticApm.PublicApiLogger, "failed capturing stacktrace");
            }

            error.Context = this.Context;
            Apm.Agent.PayloadSender.QueueError(new Error {
                Errors = new List <Error.Err> {
                    error
                }, Service = this.service
            });
        }
Beispiel #5
0
        public void End()
        {
            if (Duration.HasValue)
            {
                _logger.Trace()
                ?.Log("Ended {Span} (with Duration already set)." +
                      " Start time: {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms",
                      this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp, Duration);
            }
            else
            {
                Assertion.IfEnabled?.That(!_isEnded,
                                          $"Span's Duration doesn't have value even though {nameof(End)} method was already called." +
                                          $" It contradicts the invariant enforced by {nameof(End)} method - Duration should have value when {nameof(End)} method exits" +
                                          $" and {nameof(_isEnded)} field is set to true only when {nameof(End)} method exits." +
                                          $" Context: this: {this}; {nameof(_isEnded)}: {_isEnded}");

                var endTimestamp = TimeUtils.TimestampNow();
                Duration = TimeUtils.DurationBetweenTimestamps(Timestamp, endTimestamp);
                _logger.Trace()
                ?.Log("Ended {Span}. Start time: {Time} (as timestamp: {Timestamp})," +
                      " End time: {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms",
                      this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp,
                      TimeUtils.FormatTimestampForLog(endTimestamp), endTimestamp, Duration);
            }

            var isFirstEndCall = !_isEnded;

            _isEnded = true;

            if (ShouldBeSentToApmServer && isFirstEndCall)
            {
                DeduceDestination();

                // Spans are sent only for sampled transactions so it's only worth capturing stack trace for sampled spans
                // ReSharper disable once CompareOfFloatsByEqualityOperator
                if (ConfigSnapshot.StackTraceLimit != 0 && ConfigSnapshot.SpanFramesMinDurationInMilliseconds != 0)
                {
                    if (Duration >= ConfigSnapshot.SpanFramesMinDurationInMilliseconds ||
                        ConfigSnapshot.SpanFramesMinDurationInMilliseconds < 0)
                    {
                        StackTrace = StacktraceHelper.GenerateApmStackTrace(new StackTrace(true).GetFrames(), _logger,
                                                                            ConfigSnapshot, $"Span `{Name}'");
                    }
                }

                _payloadSender.QueueSpan(this);
            }

            if (isFirstEndCall)
            {
                _currentExecutionSegmentsContainer.CurrentSpan = _parentSpan;
            }
        }
Beispiel #6
0
        public void CaptureException(Exception exception, string culprit = null, bool isHandled = false, string parentId = null)
        {
            var capturedCulprit = string.IsNullOrEmpty(culprit) ? "PublicAPI-CaptureException" : culprit;

            var ed = new CapturedException
            {
                Message    = exception.Message,
                Type       = exception.GetType().FullName,
                Handled    = isHandled,
                Stacktrace = StacktraceHelper.GenerateApmStackTrace(exception, _logger,
                                                                    $"{nameof(Transaction)}.{nameof(CaptureException)}"),
            };

            _sender.QueueError(new Error(ed, TraceId, Id, parentId ?? Id)
            {
                Culprit = capturedCulprit, Context = Context
            });
        }
        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 #8
0
        public void CaptureError(string message, string culprit, StackFrame[] frames, string parentId = null)
        {
            var capturedCulprit = string.IsNullOrEmpty(culprit) ? "PublicAPI-CaptureException" : culprit;

            var ed = new CapturedException()
            {
                Message = message,
            };

            if (frames != null)
            {
                ed.Stacktrace
                    = StacktraceHelper.GenerateApmStackTrace(frames, _logger, "failed capturing stacktrace");
            }

            _sender.QueueError(new Error(ed, TraceId, Id, parentId ?? Id)
            {
                Culprit = capturedCulprit, Context = Context
            });
        }
Beispiel #9
0
        public void CaptureError(string message, string culprit, StackFrame[] frames, string parentId = null)
        {
            var capturedCulprit = string.IsNullOrEmpty(culprit) ? "PublicAPI-CaptureException" : culprit;

            var capturedException = new CapturedException()
            {
                Message = message,
            };

            if (frames != null)
            {
                capturedException.Stacktrace
                    = StacktraceHelper.GenerateApmStackTrace(frames, _logger, $"{nameof(Span)}.{nameof(CaptureError)}");
            }

            _payloadSender.QueueError(
                new Error(capturedException, TraceId, Id, parentId ?? Id)
            {
                Culprit = capturedCulprit                                                                             /*, Context = Context */
            });
        }
Beispiel #10
0
        public Span(
            string name,
            string type,
            string parentId,
            string traceId,
            Transaction enclosingTransaction,
            bool isSampled,
            IPayloadSender payloadSender,
            IApmLogger logger
            )
        {
            Timestamp = TimeUtils.TimestampNow();
            Id        = RandomGenerator.GenerateRandomBytesAsString(new byte[8]);
            _logger   = logger?.Scoped($"{nameof(Span)}.{Id}");

            _payloadSender        = payloadSender;
            _enclosingTransaction = enclosingTransaction;
            Name      = name;
            Type      = type;
            IsSampled = isSampled;

            ParentId = parentId;
            TraceId  = traceId;

            if (IsSampled)
            {
                // Started spans should be counted only for sampled transactions
                enclosingTransaction.SpanCount.Started++;

                // Spans are sent only for sampled transactions so it's only worth capturing stack trace for sampled spans
                StackTrace = StacktraceHelper.GenerateApmStackTrace(new StackTrace(true).GetFrames(), _logger, $"Span `{Name}'");
            }

            _logger.Trace()
            ?.Log("New Span instance created: {Span}. Start time: {Time} (as timestamp: {Timestamp})",
                  this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
        }
Beispiel #11
0
        public void OnNext(KeyValuePair <string, object> kv)
        {
            if (kv.Value == null || String.IsNullOrEmpty(kv.Key))
            {
                return;
            }

            if (!(kv.Value.GetType().GetTypeInfo().GetDeclaredProperty("Request")?.GetValue(kv.Value) is HttpRequestMessage request))
            {
                return;
            }

            if (IsRequestFiltered(request?.RequestUri))
            {
                return;
            }

            switch (kv.Key)
            {
            case "System.Net.Http.Exception":
                var exception   = kv.Value.GetType().GetTypeInfo().GetDeclaredProperty("Exception").GetValue(kv.Value) as Exception;
                var transaction = TransactionContainer.Transactions?.Value[0];

                var error = new Error
                {
                    Errors = new List <Error.Err>
                    {
                        new Error.Err
                        {
                            Culprit   = "Failed outgoing HTTP request",
                            Exception = new CapturedException
                            {
                                Message = exception.Message,
                                Type    = exception.GetType().FullName
                                          //Handled  TODO: this exception can be handled later
                            },
                            Transaction = new Error.Err.Trans
                            {
                                Id = transaction.Id
                            },
                            Id        = Guid.NewGuid(),
                            Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ")
                        }
                    },

                    Service = transaction.service
                };

                if (!String.IsNullOrEmpty(exception.StackTrace))
                {
                    error.Errors[0].Exception.Stacktrace
                        = StacktraceHelper.GenerateApmStackTrace(new System.Diagnostics.StackTrace(exception).GetFrames(), logger, "failed outgoing HTTP request");
                }

                if (transaction.Context != null)
                {
                    error.Errors[0].Context = transaction.Context;
                }

                Agent.PayloadSender.QueueError(error);

                break;

            case "System.Net.Http.HttpRequestOut.Start":     //TODO: look for consts
                if (TransactionContainer.Transactions == null || TransactionContainer.Transactions.Value == null)
                {
                    return;
                }

                var transactionStartTime = TransactionContainer.Transactions.Value[0].TimestampInDateTime;
                var utcNow = DateTime.UtcNow;

                var http = new Http
                {
                    Url    = request?.RequestUri?.ToString(),
                    Method = request?.Method?.Method,
                };

                var span = new Span
                {
                    Start   = (decimal)(utcNow - transactionStartTime).TotalMilliseconds,
                    Name    = $"{request?.Method} {request?.RequestUri?.Host?.ToString()}",
                    Type    = Consts.EXTERNAL,
                    Subtype = Consts.HTTP,
                    Context = new Span.ContextC
                    {
                        Http = http
                    }
                };

                if (processingRequests.TryAdd(request, span))
                {
                    var frames      = new System.Diagnostics.StackTrace().GetFrames();
                    var stackFrames = StacktraceHelper.GenerateApmStackTrace(frames, logger, span.Name);
                    span.Stacktrace = stackFrames;
                }
                break;

            case "System.Net.Http.HttpRequestOut.Stop":
                var response = kv.Value.GetType().GetTypeInfo().GetDeclaredProperty("Response").GetValue(kv.Value) as HttpResponseMessage;

                if (processingRequests.TryRemove(request, out Span mspan))
                {
                    //TODO: response can be null if for example the request Task is Faulted.
                    //E.g. writing this from an airplane without internet, and requestTaskStatus is "Faulted" and response is null
                    //How do we report this? There is no response code in that case.
                    if (response != null)
                    {
                        mspan.Context.Http.Status_code = (int)response.StatusCode;
                    }

                    //TODO: there are better ways
                    var endTime = (DateTime.UtcNow - TransactionContainer.Transactions.Value[0].TimestampInDateTime).TotalMilliseconds;
                    mspan.Duration = endTime - (double)mspan.Start;

                    TransactionContainer.Transactions?.Value[0]?.Spans?.Add(mspan);
                }
                else
                {
                    logger.LogWarning($"Failed capturing request"
                                      + (!String.IsNullOrEmpty(request?.RequestUri?.AbsoluteUri) && !String.IsNullOrEmpty(request?.Method?.ToString()) ? $" '{request?.Method.ToString()} " : " ")
                                      + (String.IsNullOrEmpty(request?.RequestUri?.AbsoluteUri) ? "" : $"{request?.RequestUri.AbsoluteUri}' ")
                                      + "in System.Net.Http.HttpRequestOut.Stop. This Span will be skipped in case it wasn't captured before.");
                }
                break;
            }
        }
Beispiel #12
0
        public void OnNext(KeyValuePair <string, object> kv)
        {
            if (kv.Value == null || String.IsNullOrEmpty(kv.Key))
            {
                return;
            }

            if (!(kv.Value.GetType().GetTypeInfo().GetDeclaredProperty("Request")?.GetValue(kv.Value) is HttpRequestMessage request))
            {
                return;
            }

            if (IsRequestFiltered(request?.RequestUri))
            {
                return;
            }

            switch (kv.Key)
            {
            case "System.Net.Http.Exception":
                var exception   = kv.Value.GetType().GetTypeInfo().GetDeclaredProperty("Exception").GetValue(kv.Value) as Exception;
                var transaction = TransactionContainer.Transactions?.Value;

                transaction.CaptureException(exception, "Failed outgoing HTTP request");
                //TODO: we don't know if exception is handled, currently reports handled = false
                break;

            case "System.Net.Http.HttpRequestOut.Start":     //TODO: look for consts
                if (TransactionContainer.Transactions == null || TransactionContainer.Transactions.Value == null)
                {
                    return;
                }

                transaction = TransactionContainer.Transactions.Value;

                var span = transaction.StartSpan($"{request?.Method} {request?.RequestUri?.Host?.ToString()}", Span.TYPE_EXTERNAL,
                                                 Span.SUBTYPE_HTTP);

                if (processingRequests.TryAdd(request, span))
                {
                    span.Context = new Span.ContextC
                    {
                        Http = new Http
                        {
                            Url    = request?.RequestUri?.ToString(),
                            Method = request?.Method?.Method,
                        }
                    };

                    var frames      = new System.Diagnostics.StackTrace().GetFrames();
                    var stackFrames = StacktraceHelper.GenerateApmStackTrace(frames, logger, span.Name);
                    span.Stacktrace = stackFrames;
                }
                break;

            case "System.Net.Http.HttpRequestOut.Stop":
                var response = kv.Value.GetType().GetTypeInfo().GetDeclaredProperty("Response").GetValue(kv.Value) as HttpResponseMessage;

                if (processingRequests.TryRemove(request, out ISpan mspan))
                {
                    //TODO: response can be null if for example the request Task is Faulted.
                    //E.g. writing this from an airplane without internet, and requestTaskStatus is "Faulted" and response is null
                    //How do we report this? There is no response code in that case.
                    if (response != null)
                    {
                        mspan.Context.Http.Status_code = (int)response.StatusCode;
                    }

                    mspan.End();
                }
                else
                {
                    logger.LogWarning($"Failed capturing request"
                                      + (!String.IsNullOrEmpty(request?.RequestUri?.AbsoluteUri) && !String.IsNullOrEmpty(request?.Method?.ToString()) ? $" '{request?.Method.ToString()} " : " ")
                                      + (String.IsNullOrEmpty(request?.RequestUri?.AbsoluteUri) ? "" : $"{request?.RequestUri.AbsoluteUri}' ")
                                      + "in System.Net.Http.HttpRequestOut.Stop. This Span will be skipped in case it wasn't captured before.");
                }
                break;
            }
        }