如何解决WCF如何在WCF中传播DiagnosticSource.Activity上下文?
我尝试在使用WCF的应用程序中将OpenTelemetry与控制台导出器集成在一起。
问题是我当前的System.Diagnostics.DiagnosticSource.Activity
的跟踪ID不会传播到另一侧。
因此,另一方面,父信息丢失了,跟踪也被破坏了。
我没有打开 WCF中的ActivityPropagation,它在检查日志时可以正常工作。
问题是,我另一端的活动没有/错误的活动ID。
目前,我一无所知,我也可以通过WCF传播System.Diagnostics.DiagnosticSource.Activity.Id
- 如何正确设置OpenTelemetry或WCF以传播活动ID /上下文?
该代码基本上仅是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>
解决方法
您需要解析 HTTP 标头并自行创建 Activity
。您可以这样做:
(要求:System.Diagnostics.DiagnosticSource nuget 包。)
- 创建以下助手类:
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";
}
- 在启动时,连接行为:
ServiceHost host = ...;
ServiceEndpoint endpoint = host.AddServiceEndpoint(...);
endpoint.EndpointBehaviors.Add(new TracingEndpointBehavior());
- 确保有
ActivityListener
,否则Activity
创建的ActivitySource
将为空。例如:
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
,但IServiceBehavior
和IContractBehavior
也可以使用。 - 我选择了
headers[HeaderNames.SOAPAction]
作为活动名称,但它可以是任何名称。 - 我通过代码连接了该行为,但也可以通过 app.config 或 web.config 来实现。不过,您需要创建一个
BehaviorExtensionElement
。
注意事项
- 不要使用
IParameterInspector
,因为在失败时不会执行AfterCall()
。 - 我从未使用过 OpenTelemetry SDK,所以我不知道上面的内容是否会有所不同。
- 灵感:https://github.com/jbogard/NServiceBus.Extensions.Diagnostics/blob/b7ef20b5f579809c5ed69ac7518c9d1ddae89884/src/NServiceBus.Extensions.Diagnostics/IncomingPhysicalMessageDiagnostics.cs
- 我认为 WCF 活动与
System.Diagnostics.Acitivity
不同。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。