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}'"); } }
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 }); }
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 }); }
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; } }
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; } }
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 }); }
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 */ }); }
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); }
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; } }
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; } }