如何在NLog ≥ v5.0中覆盖内置JSON转换器实现字段脱敏
NLog v5.0+ 敏感字段脱敏实现方案
一、全局JSON转换器的正确注册方式
NLog v5.0废弃了ConfigurationItemFactory.Default.JsonConverter,改用LogManager.Setup()来配置自定义JSON处理逻辑:
- 实现适配NLog v5的序列化接口,推荐用
NLog.Serialization.IJsonSerializer来覆盖全局序列化逻辑,若仅需字段级转换也可实现IJsonConverter - 在程序初始化阶段注册:
// 非DI场景 LogManager.Setup() .SetupSerialization(config => config.RegisterJsonSerializer(new YourCustomCensorSerializer())); // ASP.NET Core DI场景 builder.Host.UseNLog(options => { options.SetupBuilder = setup => setup.SetupSerialization(s => s.RegisterJsonSerializer(new YourCustomCensorSerializer())); });
若仅需注册特定转换器:
LogManager.Setup() .SetupSerialization(s => s.RegisterJsonConverter(new YourCensorConverter()));
二、基于CensorMeAttribute的替代脱敏方案
如果全局转换器注册不生效,推荐以下几种灵活的字段级处理方式:
方案1:自定义LayoutRenderer处理标记属性
实现一个LayoutRenderer,反射检查日志对象的属性是否带有CensorMeAttribute,执行脱敏后再序列化:
[LayoutRenderer("censor-marked")] public class CensoredPropertiesRenderer : LayoutRenderer { protected override void Append(StringBuilder builder, LogEventInfo logEvent) { var targetObj = logEvent.Properties["LogData"]; // 假设日志对象存在于LogData属性中 if (targetObj == null) return; var type = targetObj.GetType(); foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (prop.GetCustomAttribute<CensorMeAttribute>() != null) { // 替换为脱敏值,比如***或部分掩码 prop.SetValue(targetObj, "***"); } } // 用NLog内置的JSON序列化器处理 builder.Append(LogManager.LogFactory.ServiceProvider.GetRequiredService<IJsonSerializer>().Serialize(targetObj)); } }
在NLog配置文件中注册并使用:
<extensions> <add assembly="你的程序集名称" /> </extensions> <targets> <target name="jsonFile" xsi:type="File" fileName="logs/app.log"> <layout type="JsonLayout"> <attribute name="timestamp" layout="${longdate}" /> <attribute name="data" layout="${censor-marked}" /> </layout> </target> </targets>
方案2:结合第三方JSON序列化器实现脱敏
如果项目使用Newtonsoft.Json或System.Text.Json,可让NLog复用这些库的序列化逻辑,通过自定义契约解析器处理标记属性:
以Newtonsoft.Json为例:
- 安装
NLog.Extensions.NewtonsoftJson包 - 实现自定义
ContractResolver:
public class CensoredContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); if (member.GetCustomAttribute<CensorMeAttribute>() != null) { property.ValueProvider = new CensoredValueProvider(property.ValueProvider); } return property; } } public class CensoredValueProvider : IValueProvider { private readonly IValueProvider _originalProvider; public CensoredValueProvider(IValueProvider originalProvider) => _originalProvider = originalProvider; public void SetValue(object target, object value) => _originalProvider.SetValue(target, value); public object GetValue(object target) => "***"; // 自定义脱敏逻辑 }
- 配置NLog使用Newtonsoft.Json并指定契约解析器:
LogManager.Setup() .SetupSerialization(s => s.UseNewtonsoftJson(jsonSettings => jsonSettings.ContractResolver = new CensoredContractResolver()));
方案3:日志对象预处理
在调用NLog日志方法前,手动遍历对象属性,对带有CensorMeAttribute的字段进行脱敏,再传入日志。这种方式无需修改NLog配置,简单直接。
三、关键注意点
- NLog v5的序列化体系已重构,务必使用
SetupSerialization下的注册方法,避免依赖旧的ConfigurationItemFactory相关API - 若处理嵌套对象的敏感字段,需在自定义逻辑中添加递归遍历逻辑
- 测试时要覆盖不同日志级别、不同对象结构的场景,确保脱敏效果符合预期
内容的提问来源于stack exchange,提问作者Astrid E.
相关产品推荐
相关产品推荐

