You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WCF服务自定义日志咨询:指定操作与请求体过滤方案实现

WCF自定义请求响应日志实现方案

一、核心扩展类代码实现

1. 自定义消息检查器(IDispatchMessageInspector)

这是实现日志拦截的核心类,负责捕获请求、判断日志条件并记录响应。必须使用消息缓冲副本,避免原消息流被消耗导致API调用失败:

using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Xml;

public class CustomMessageLogger : IDispatchMessageInspector
{
    // 需记录的目标操作名称
    private readonly string[] _targetOperations = { "GetRateLandedCost", "ShipParcel" };
    // 允许的AccountNumber过滤规则(可按需改为动态配置)
    private readonly string[] _allowedAccounts = { "ACC123", "ACC456" };

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        // 获取当前操作名称
        var operationName = OperationContext.Current.IncomingMessageHeaders.Action.Split('/').Last();
        
        // 判断是否为目标操作
        if (Array.Exists(_targetOperations, op => op.Equals(operationName, StringComparison.OrdinalIgnoreCase)))
        {
            // 创建消息缓冲副本,避免原消息被消耗
            var bufferedRequest = request.CreateBufferedCopy(int.MaxValue);
            request = bufferedRequest.CreateMessage();
            
            // 解析请求体中的AccountNumber
            string accountNumber = ExtractAccountNumber(bufferedRequest.CreateMessage());
            
            // 判断是否符合AccountNumber过滤条件
            if (Array.Exists(_allowedAccounts, acc => acc.Equals(accountNumber, StringComparison.OrdinalIgnoreCase)))
            {
                // 记录请求日志
                LogMessage("Request", operationName, bufferedRequest.CreateMessage());
                // 返回操作名称,供响应记录时使用
                return operationName;
            }
        }
        
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        // 仅处理标记过的目标操作响应
        var operationName = correlationState as string;
        if (!string.IsNullOrEmpty(operationName))
        {
            var bufferedReply = reply.CreateBufferedCopy(int.MaxValue);
            reply = bufferedReply.CreateMessage();
            LogMessage("Reply", operationName, bufferedReply.CreateMessage());
        }
    }

    // 解析请求体中的AccountNumber(需根据实际XML结构调整XPath)
    private string ExtractAccountNumber(Message message)
    {
        try
        {
            var doc = new XmlDocument();
            using (var ms = new MemoryStream())
            {
                message.WriteMessage(ms);
                ms.Position = 0;
                doc.Load(ms);
                
                // 假设AccountNumber在请求根节点下,需根据实际结构修改路径
                XmlNode accountNode = doc.SelectSingleNode("//AccountNumber");
                return accountNode?.InnerText ?? string.Empty;
            }
        }
        catch (Exception)
        {
            return string.Empty;
        }
    }

    // 日志写入逻辑(可替换为文件、数据库等存储方式)
    private void LogMessage(string messageType, string operationName, Message message)
    {
        var logPath = @"C:\Logs\WcfCustomLogs\";
        Directory.CreateDirectory(logPath);
        var logFileName = $"{DateTime.Now:yyyyMMddHHmmss}_{operationName}_{messageType}.xml";
        
        using (var writer = new StreamWriter(Path.Combine(logPath, logFileName)))
        {
            using (var xmlWriter = XmlWriter.Create(writer))
            {
                message.WriteMessage(xmlWriter);
            }
        }
    }
}

2. 端点行为实现(IEndpointBehavior)

将自定义日志检查器注入服务端消息拦截管道:

using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

public class CustomLoggingBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        // 无需处理
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        // 仅服务端生效,忽略客户端逻辑
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        // 给服务端分发运行时添加自定义消息检查器
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomMessageLogger());
    }

    public void Validate(ServiceEndpoint endpoint)
    {
        // 无需验证逻辑
    }
}

3. 配置扩展元素(BehaviorExtensionElement)

让自定义行为可通过web.config配置加载:

using System;
using System.ServiceModel.Configuration;

public class CustomLoggingBehaviorExtension : BehaviorExtensionElement
{
    public override Type BehaviorType => typeof(CustomLoggingBehavior);

    protected override object CreateBehavior()
    {
        return new CustomLoggingBehavior();
    }
}

二、web.config配置修改

1. 完整配置示例(保留原有基础日志)

<system.serviceModel>
  <!-- 原有diagnostics配置保留 -->
  <diagnostics wmiProviderEnabled="true">
    <messageLogging
        logEntireMessage="true"
        logMalformedMessages="true"
        logMessagesAtServiceLevel="true"
        logMessagesAtTransportLevel="false"
        maxMessagesToLog="5000"
        maxSizeOfMessageToLog="5000000" />
  </diagnostics>

  <!-- 注册自定义行为扩展 -->
  <extensions>
    <behaviorExtensions>
      <add name="customLogging" type="YourNamespace.CustomLoggingBehaviorExtension, YourAssemblyName"/>
    </behaviorExtensions>
  </extensions>

  <!-- 行为配置 -->
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <!-- 原有服务行为保留 -->
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
    <!-- 端点行为配置 -->
    <endpointBehaviors>
      <behavior name="CustomLoggingEndpointBehavior">
        <!-- 添加自定义日志行为 -->
        <customLogging />
      </behavior>
    </endpointBehaviors>
  </behaviors>

  <!-- 服务端点绑定自定义日志行为 -->
  <services>
    <service name="YourNamespace.YourService">
      <endpoint address="" binding="basicHttpBinding" contract="YourNamespace.IYourService" behaviorConfiguration="CustomLoggingEndpointBehavior"/>
      <!-- 其他端点配置按需保留 -->
    </service>
  </services>
</system.serviceModel>

<!-- 原有system.diagnostics配置保留 -->
<system.diagnostics>
  <sources>
      <source name="System.ServiceModel.MessageLogging">
          <listeners>
              <add name="messages"
              type="System.Diagnostics.XmlWriterTraceListener"
              initializeData="c:\logs\messages.svclog" />
          </listeners>
      </source>
  </sources>
</system.diagnostics>

注意:替换配置中的YourNamespace、YourAssemblyName、YourService、IYourService为项目实际的命名空间、程序集名称、服务类和契约接口名称。

三、关键注意事项

  • 消息缓冲处理:必须使用CreateBufferedCopy创建消息副本,否则原消息流会被读取后消耗,导致后续服务处理失败(这是之前API调用失效的常见原因)。
  • AccountNumber解析:ExtractAccountNumber方法中的XPath需根据实际请求XML结构调整,确保能正确获取目标值。
  • 日志路径权限:确保应用程序池身份对日志路径有读写权限,避免日志写入失败。
  • 规则灵活性:_targetOperations和_allowedAccounts可改为从配置文件读取,方便后续调整过滤规则。

内容的提问来源于stack exchange,提问作者Hans Snyder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 11:29:51