private void OnMessageStart(KeyValuePair <string, object> kv, string action) { var currentSegment = ApmAgent.GetCurrentExecutionSegment(); if (currentSegment is null) { return; } if (!(kv.Value is Activity activity)) { Logger.Trace()?.Log("Value is not an activity - exiting"); return; } string queueName = null; foreach (var tag in activity.Tags) { switch (tag.Key) { case "message_bus.destination": queueName = tag.Value; break; default: continue; } } if (MatchesIgnoreMessageQueues(queueName)) { return; } var name = $"{ServiceBus.SegmentName} {action} message"; _onMessageCurrent = currentSegment switch { Span span => span.StartSpanInternal(name, ApiConstants.TypeMessaging, ServiceBus.SubType, action.ToLowerInvariant(), id: activity.SpanId.ToString()), Transaction transaction => transaction.StartSpanInternal(name, ApiConstants.TypeMessaging, ServiceBus.SubType, action.ToLowerInvariant(), id: activity.SpanId.ToString()), _ => _onMessageCurrent }; }
public void ErrorShouldContainTransactionData(bool isSampled, bool captureOnSpan, bool captureAsError) { var payloadSender = new MockPayloadSender(); var expectedErrorContext = new Context(); expectedErrorContext.Labels["one"] = "1"; expectedErrorContext.Labels["twenty two"] = "22"; ITransaction capturedTransaction = null; IExecutionSegment errorCapturingExecutionSegment = null; var mockConfig = new MockConfigSnapshot(transactionSampleRate: isSampled ? "1" : "0"); using (var agent = new ApmAgent(new TestAgentComponents(config: mockConfig, payloadSender: payloadSender))) { agent.Tracer.CaptureTransaction(TestTransaction, CustomTransactionTypeForTests, transaction => { capturedTransaction = transaction; foreach (var keyValue in expectedErrorContext.Labels) transaction.Context.Labels[keyValue.Key] = keyValue.Value; ISpan span = null; if (captureOnSpan) { span = transaction.StartSpan(TestSpan1, ApiConstants.TypeExternal); errorCapturingExecutionSegment = span; } else errorCapturingExecutionSegment = transaction; if (captureAsError) errorCapturingExecutionSegment.CaptureError("Test error message", "Test error culprit", new StackTrace(true).GetFrames()); else errorCapturingExecutionSegment.CaptureException(new TestException("test exception")); // Immutable snapshot of the context should be captured instead of reference to a mutable object // transaction.Context.Labels["three hundred thirty three"] = "333"; span?.End(); }); } payloadSender.Errors.Count.Should().Be(1); payloadSender.FirstError.Transaction.IsSampled.Should().Be(isSampled); payloadSender.FirstError.Transaction.Type.Should().Be(CustomTransactionTypeForTests); payloadSender.FirstError.TransactionId.Should().Be(capturedTransaction.Id); payloadSender.FirstError.TraceId.Should().Be(capturedTransaction.TraceId); payloadSender.FirstError.ParentId.Should().Be(errorCapturingExecutionSegment.Id); payloadSender.FirstError.Context.Should().BeEquivalentTo(expectedErrorContext); }
private static ElasticDocument DeserializeAndMapData(IExecutionSegment transaction, EventData message) { var span = transaction.StartSpan("Deserialize and Map Data in Message", "Data Transformation"); try { var readData = JsonConvert.DeserializeObject <ElasticDocument>(Encoding.UTF8.GetString(message.Body)); return(readData); } catch (Exception e) { span.CaptureException(e, "DeserializeAndMapData"); return(null); } finally { span.End(); } }
private void PublishMessage(IMessage message, string destination, bool forceDestination, Action <IMessage, string, bool> publish, IExecutionSegment transaction, string name) { var span = transaction.StartSpan(name, SpanType); try { var traceParent = $"00-{transaction.TraceId}-{span.Id}-01"; var traceAsyncTransaction = _config.GetValue <bool>(EnvironmentConstants.ApmTraceAsyncTransaction); message.Headers[ApmConstants.TraceParent] = traceParent; message.Headers[ApmConstants.ApmTraceAsyncTransaction] = traceAsyncTransaction; publish(message, destination, forceDestination); } catch (Exception ex) { span.CaptureException(ex); throw; } finally { span.End(); } }
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; } }
private void StartSpan <TState>(IExecutionSegment currentExecutionSegment, TState state) { if (state is string) { currentExecutionSegment.StartSpan(state.ToString(), ApmOptions.DefaultSpanType); } else if (state is IEnumerable <KeyValuePair <string, object> > Properties) { var propDic = new Dictionary <string, object>(); foreach (var item in Properties) { if (!propDic.ContainsKey(item.Key)) { propDic.Add(item.Key, item.Value); } } string apmSpanType = propDic.ContainsKey(nameof(apmSpanType)) ? propDic[nameof(apmSpanType)].ToString() : ApmOptions.DefaultSpanType; string apmSpanName = propDic.ContainsKey(nameof(apmSpanName)) ? propDic[nameof(apmSpanName)].ToString() : ApmOptions.DefaultSpanName; var span = currentExecutionSegment.StartSpan(apmSpanName, apmSpanType); AddMetadata(span, propDic); } }
private static async Task <T> PublishMessageAndWait <T>(IMessage message, string destination, bool forceDestination, int milliSecondsTimeout, Func <IMessage, string, bool, int, Task <T> > publishAndWait, IExecutionSegment transaction, string name) where T : IMessage { var span = transaction.StartSpan(name, SpanType); try { var traceParent = $"00-{transaction.TraceId}-{span.Id}-01"; message.Headers[ApmConstants.TraceParent] = traceParent; message.Headers[ApmConstants.ApmTraceAsyncTransaction] = true; return(await publishAndWait(message, destination, forceDestination, milliSecondsTimeout)); } catch (Exception ex) { span.CaptureException(ex); throw; } finally { span.End(); } }
public ElasticSpan(IExecutionSegment segment) { this._segment = segment; }
/// <summary> /// Try to inject the trace context into the Kafka message headers /// </summary> /// <param name="agent">The agent</param> /// <param name="segment">The outgoing distributed tracing data to propagate</param> /// <param name="message">The duck-typed Kafka Message object</param> /// <typeparam name="TTopicPartitionMarker">The TopicPartition type (used optimisation purposes)</typeparam> /// <typeparam name="TMessage">The type of the duck-type proxy</typeparam> internal static void TryInjectHeaders <TTopicPartitionMarker, TMessage>(IApmAgent agent, IExecutionSegment segment, TMessage message) where TMessage : IMessage { if (!HeadersInjectionEnabled) { return; } try { message.Headers ??= CachedMessageHeadersHelper <TTopicPartitionMarker> .CreateHeaders(); var adapter = new KafkaHeadersCollection(message.Headers, agent.Logger); var distributedTracingData = segment.OutgoingDistributedTracingData; adapter.Set(TraceContext.TraceParentBinaryHeaderName, distributedTracingData.SerializeToString()); adapter.Set(TraceContext.TraceStateHeaderName, distributedTracingData.TraceState.ToTextHeader()); } catch (Exception ex) { // don't keep trying if we run into problems HeadersInjectionEnabled = false; agent.Logger.Warning()?.LogException(ex, "There was a problem injecting headers into the Kafka record. Disabling headers injection"); } }