1

我尝试在使用 WCF 的应用程序中将 OpenTelemetry 与控制台导出器集成。问题是,我当前的跟踪 idSystem.Diagnostics.DiagnosticSource.Activity没有传播到另一端。因此,另一方面,父信息丢失并且跟踪被破坏。

确实在检查日志时打开了 WCF 中的 ActivityPropagation。

问题是,我在另一边的活动没有/错误的活动 ID。


目前我不知道我还能做些什么来传播 System.Diagnostics.DiagnosticSource.Activity.IdWCF

  • 如何正确设置 OpenTelemetry 或 WCF 以传播 Activity Id/Context?




代码基本上只有 microsoft WCF Calculator Tutorial。我尝试将 OpenTelemetry 集成到其中。

这些是我用于 OpenTelemetry 的部分

客户:

public class Program
{
    private static readonly ActivitySource MyActivitySource = new ActivitySource("MyCompany.MyProduct.MyClient");

    public static void Main(string[] args)
    {
        var tracerProvider = Sdk.CreateTracerProviderBuilder()
            .AddSource("MyCompany.MyProduct.MyClient")
            .AddConsoleExporter()
            .Build();

        using (var activity_wcf_session = MyActivitySource.StartActivity("WCF_Session"))
        {
            activity_wcf_session?.SetTag("index", 0);

            //Step 1: Create an instance of the WCF proxy.
            CalculatorClient client = new CalculatorClient();

            // Step 2: Call the service operations.
            // Call the Add service operation.
            using (var activity = MyActivitySource.StartActivity("Client_call_add()"))
            {
                activity?.SetTag("msg", "foo");
                double result = client.Add(100.00D, 15.99D);
            }
            
            // Step 3: Close the client to gracefully close the connection and clean up resources.
            Console.WriteLine("\nPress <Enter> to terminate the wcf client.");
            Console.ReadLine();
            client.Close();
        }
    }
}

服务:

public class CalculatorService : ICalculator
    {
        private static readonly ActivitySource MyActivitySource = new ActivitySource(
            "MyCompany.MyProduct.CalculatorService");

        private TracerProvider tracerProvider;

        public CalculatorService()
        {
            tracerProvider = Sdk.CreateTracerProviderBuilder()
            .AddSource("MyCompany.MyProduct.CalculatorService")
            .AddConsoleExporter()
            .Build();
        }


        public double Add(double n1, double n2)
        {
            Console.WriteLine("Activity.Current is null: " + (Activity.Current == null)); // is always null

            using (var activity = MyActivitySource.StartActivity("CalculatorService_add()", ActivityKind.Server))
            {
                // activity.parent is not set

                double result = n1 + n2;
                return result;
            }
        }
        
        // ...
    }
}

这就是我在项目中激活活动传播的方式(主机相同)

<system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information,ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add initializeData="C:\logs\GettingStarted_Client.svclog" type="System.Diagnostics.XmlWriterTraceListener" name="xml" />
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>
4

1 回答 1

0

您需要解析 HTTP 标头并Activity自行创建。你可以这样做:

(要求:System.Diagnostics.DiagnosticSource nuget 包。)

  1. 创建以下帮助类:
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http.Headers;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;

internal sealed class TracingEndpointBehavior : IEndpointBehavior
{
    private readonly TracingMessageInspector messageInspector = new TracingMessageInspector();

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        // No-op
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        // No-op
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(messageInspector);
    }

    public void Validate(ServiceEndpoint endpoint)
    {
        // No-op
    }
}

internal sealed class TracingMessageInspector : IDispatchMessageInspector
{
    private readonly ActivitySource activitySource = new ActivitySource(nameof(TracingMessageInspector));

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        return StartActivity(WebOperationContext.Current?.IncomingRequest?.Headers);
    }

    private Activity StartActivity(WebHeaderCollection headers)
    {
        if (headers == null)
        {
            return activitySource.StartActivity("<UnknownAction>", ActivityKind.Server);
        }

        Activity activity = activitySource.StartActivity(
            name: headers[HeaderNames.SOAPAction] ?? "<UnknownAction>",
            kind: ActivityKind.Server,
            parentId: headers[HeaderNames.TraceParent]);

        if (activity == null)
        {
            return null;
        }

        activity.TraceStateString = headers[HeaderNames.TraceState];

        string baggageString = headers[HeaderNames.Baggage] ?? headers[HeaderNames.CorrelationContext];
        if (baggageString != null)
        {
            foreach (var item in baggageString.Split(','))
            {
                if (NameValueHeaderValue.TryParse(item, out NameValueHeaderValue baggageItem))
                {
                    activity.AddBaggage(baggageItem.Name, WebUtility.UrlDecode(baggageItem.Value));
                }
            }
        }

        return activity;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        if (correlationState is Activity activity)
        {
            activity.Stop();
        }
    }
}

internal static class HeaderNames
{
    public const string SOAPAction = "SOAPAction";
    public const string TraceParent = "traceparent";
    public const string TraceState = "tracestate";
    public const string CorrelationContext = "Correlation-Context";
    public const string Baggage = "baggage";
}
  1. 在启动时,连接行为:
ServiceHost host = ...;
ServiceEndpoint endpoint = host.AddServiceEndpoint(...);
endpoint.EndpointBehaviors.Add(new TracingEndpointBehavior());
  1. 确保有ActivityListeners,否则Activitys 创建的ActivitySources 将为空。例如:
ActivitySource.AddActivityListener(new ActivityListener
{
    ActivityStarted = a => Console.WriteLine($"[{DateTime.UtcNow:o}] Started: {a.OperationName} ({a.Id})"),
    ActivityStopped = a => Console.WriteLine($"[{DateTime.UtcNow:o}] Stopped: {a.OperationName} ({a.Id}) took {a.Duration}"),
    ShouldListenTo = _ => true,
    Sample = (ref ActivityCreationOptions<ActivityContext> o) => ActivitySamplingResult.AllDataAndRecorded,
});

变化

  • 上面的代码使用了IEndpointBehavior,但IServiceBehaviorIContractBehavior可以工作。
  • 我选择headers[HeaderNames.SOAPAction]了活动名称,但它可以是任何名称。
  • 我通过代码连接了行为,但也可以通过app.configweb.config来实现。你需要创建一个BehaviorExtensionElement

笔记

于 2021-06-06T02:34:00.180 回答