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
相关产品推荐
相关产品推荐

