.NET Core 3.1中Windsor Castle集成NLog实现自定义日志写入数据库的问题求助
解决Windsor Castle集成NLog时自定义日志属性写入数据库的问题
看起来你遇到的核心问题是没有正确将自定义日志属性传递给NLog的event-properties,而是错误地用InfoFormat把参数嵌入到日志字符串里,导致数据库target无法获取到正确的属性值,进而引发类型转换和空值异常。下面一步步帮你解决:
1. 理解问题根源
你用_logger.InfoFormat(...)的方式,只是把参数拼接成了日志消息文本,并没有把这些值设置为NLog的event-properties。而你的NLog数据库配置里,是通过${event-properties:item=XXX}来读取属性值的——这意味着这些值根本没被传递到数据库target,所以参数要么是空字符串,要么是错误的类型,最终触发SQL异常。
2. 正确传递自定义日志属性
你需要用NLog的结构化日志或者**WithProperty方法**来传递自定义属性,而不是拼字符串。这里有两种常用方式:
方式一:使用匿名对象传递结构化数据
// 定义你的自定义日志数据 var customLog = new { CreateDate = DateTime.Now, UserProfileId = Guid.NewGuid(), // 确保这里是有效的Guid,匹配数据库列类型 IsError = false, Type = 1, Username = "当前登录用户", Message = "abcd", Description = "这是一条自定义业务日志", StackTrace = string.Empty, AddressIP = "192.168.1.100" }; // 用结构化日志方式传递,NLog会自动解析对象属性到event-properties _logger.Info("业务日志记录: {LogData}", customLog);
方式二:逐个添加属性(更灵活)
如果需要单独控制每个属性,可以用WithProperty链式调用:
_logger.WithProperty("CreateDate", DateTime.Now) .WithProperty("UserProfileId", Guid.NewGuid()) .WithProperty("IsError", false) .WithProperty("Type", 1) .WithProperty("Username", "当前登录用户") .WithProperty("Message", "abcd") .WithProperty("Description", "这是一条自定义业务日志") .WithProperty("StackTrace", string.Empty) .WithProperty("AddressIP", "192.168.1.100") .Info("业务日志记录");
注意:如果用Windsor Castle的抽象Logger
如果你的_logger是Windsor Castle提供的ILogger抽象,可能需要先获取底层的NLog Logger实例才能使用WithProperty:
// 转换为底层NLog Logger var nlogLogger = (NLog.Logger)_logger.Logger; nlogLogger.WithProperty("CreateDate", DateTime.Now) // 其他属性... .Info("业务日志记录");
3. 修正NLog配置(确保参数匹配)
检查你的nlog.config,确保:
- 数据库连接字符串
connectionString是有效的(替换成你的实际连接字符串) - 所有参数名和
event-properties的key完全一致(比如@UserProfileId对应UserProfileId属性) - 不需要额外的CASE转换,只要传递的属性类型和数据库列类型匹配(比如Guid对应uniqueidentifier,bool对应bit等)
修正后的核心配置部分(和你原来的一致,只要确保属性传递正确即可):
<target name="database" xsi:type="Database" connectionString="你的数据库连接字符串" commandText="INSERT INTO [dbo].[EventLog] ( [CreateDate], [UserProfileId], [IsError], [Type], [Username], [Message], [Description], [StackTrace], [AddressIP] ) VALUES ( @CreateDate, @UserProfileId, @IsError, @Type, @Username, @Message, @Description, @StackTrace, @AddressIP )"> <parameter name="@CreateDate" layout="${event-properties:item=CreateDate}" /> <parameter name="@UserProfileId" layout="${event-properties:item=UserProfileId}" /> <parameter name="@IsError" layout="${event-properties:item=IsError}" /> <parameter name="@Type" layout="${event-properties:item=Type}" /> <parameter name="@Username" layout="${event-properties:item=Username}" /> <parameter name="@Message" layout="${event-properties:item=Message}" /> <parameter name="@Description" layout="${event-properties:item=Description}" /> <parameter name="@StackTrace" layout="${event-properties:item=StackTrace}" /> <parameter name="@AddressIP" layout="${event-properties:item=AddressIP}" /> </target>
4. 调试验证
可以添加一个控制台target来查看event-properties是否正确传递,方便排查问题:
<targets> <!-- 你的文件和数据库target --> <target name="console" xsi:type="Console" layout="${longdate}|${level}|${message}|${all-event-properties}" /> </targets> <rules> <logger name="*" minlevel="Trace" writeTo="console" /> <!-- 你的其他规则 --> </rules>
运行程序后,控制台会输出所有传递的属性,你可以确认每个属性是否存在、值是否正确。
为什么之前的方法失败?
InfoFormat只是生成格式化的日志消息,并没有把参数绑定到event-properties,所以数据库target读取到的都是空值或错误的字符串。- 你添加的CASE语句试图处理空字符串,但因为属性根本没被传递,所以转换后得到NULL,而数据库的
UserProfileId(或Id)列不允许空值,因此触发新的异常。
内容的提问来源于stack exchange,提问作者axern
相关产品推荐
相关产品推荐

