代码配置NLog无法写入数据库且无异常,求原因排查
我来帮你一步步排查这个NLog数据库日志不写入的问题——毕竟这种无异常的静默失败确实头疼,咱们从最常见的配置点开始查:
一、先检查数据库目标的核心配置项
这些是数据库目标工作的基础,缺一个都可能导致静默失败:
连接字符串有效性:
确保dbTarget2.ConnectionString是完全正确的,包含正确的服务器地址、数据库名、认证方式(比如Windows集成认证还是SQL账号密码)。可以单独用SqlConnection测试连接,比如:using (var conn = new SqlConnection("你的连接字符串")) { try { conn.Open(); Console.WriteLine("连接成功"); } catch (Exception ex) { Console.WriteLine($"连接失败:{ex.Message}"); } }如果连接本身失败,NLog不会抛出异常,但会在内部日志里记录。
SQL命令文本正确性:
你的CommandText必须和数据库日志表的结构完全匹配,字段名、参数名不能错。举个标准的例子:dbTarget2.CommandText = "INSERT INTO Logs (LogLevel, LogMessage, CreatedTime, LoggerName) VALUES (@level, @message, @timestamp, @logger)";要确保表
Logs已经存在,且字段类型对应(比如CreatedTime是datetime2类型,LogMessage是nvarchar(max))。参数映射是否齐全:
每个SQL参数都要对应NLog的布局渲染器,不能漏:dbTarget2.Parameters.Add(new DatabaseParameterInfo("@level", "${level}")); dbTarget2.Parameters.Add(new DatabaseParameterInfo("@message", "${message}")); dbTarget2.Parameters.Add(new DatabaseParameterInfo("@timestamp", "${date:format=yyyy-MM-dd HH:mm:ss.fff}")); dbTarget2.Parameters.Add(new DatabaseParameterInfo("@logger", "${logger}"));参数名要和
CommandText里的完全一致,大小写敏感(取决于数据库配置)。
二、检查NLog核心配置是否生效
很多时候是配置没正确应用到NLog引擎:
是否将目标添加到配置并设置日志规则:
你必须把数据库目标添加到LoggingConfiguration,并且创建日志规则指定哪些日志要写入这个目标:logConfig.AddTarget("dbTarget", dbTarget2); // 规则:所有日志记录器,Info及以上级别写入数据库目标 logConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, dbTarget2));如果日志级别设得太高(比如
LogLevel.Error),而你只输出了Info级别的日志,自然不会写入。是否将配置应用到LogManager:
最后一定要把配置赋值给LogManager.Configuration,否则NLog会用默认配置,完全忽略你的代码配置:LogManager.Configuration = logConfig;是否正确使用Logger:
要确保用LogManager.GetCurrentClassLogger()(或指定名称的记录器)获取Logger,并且输出的日志级别符合规则:var logger = LogManager.GetCurrentClassLogger(); logger.Info("测试数据库日志"); // 这个级别要匹配规则里的级别
三、排查“无异常”的关键:开启NLog内部日志
NLog默认不会抛出配置或执行错误的异常,而是把细节记录在内部日志里。这是排查静默失败的核心手段:
添加这段代码在配置NLog之前:
// 开启内部日志,输出到文件 InternalLogger.LogFile = "nlog-internal.log"; // 设置日志级别为Trace,获取最详细的信息 InternalLogger.LogLevel = LogLevel.Trace;
运行程序后,打开nlog-internal.log,里面会清晰显示:
- 数据库目标的初始化过程
- 数据库连接尝试的结果
- SQL语句的执行情况(比如语法错误、权限不足)
- 任何被静默处理的错误
四、其他常见坑点
- 数据库权限问题:确保应用程序使用的账号有日志表的
INSERT权限。比如用Windows认证时,运行程序的用户需要有对应权限;用SQL账号时,账号要被授予INSERT权限。 - 异步目标的刷新问题:如果用了
AsyncTargetWrapper包装数据库目标,控制台程序结束前要调用LogManager.Shutdown(),确保异步队列里的日志被写入数据库,否则可能丢失日志。 - 版本兼容性:确保
NLog和NLog.Targets.Database的NuGet包版本一致,不同版本可能有配置差异。
完整示例代码
你可以对比自己的代码,看看哪里漏了:
using NLog; using NLog.Config; using NLog.Targets; using System.Data.SqlClient; namespace Test.Logger { class Program { static void Main(string[] args) { // 开启内部日志排查问题 InternalLogger.LogFile = "nlog-internal.log"; InternalLogger.LogLevel = LogLevel.Trace; var logConfig = new LoggingConfiguration(); // 配置数据库目标 var dbTarget2 = new DatabaseTarget("dbTarget") { ConnectionString = "Server=.;Database=YourLogDB;Integrated Security=True;", // 替换为你的连接字符串 CommandText = "INSERT INTO Logs (LogLevel, LogMessage, CreatedTime, LoggerName) VALUES (@level, @message, @timestamp, @logger)" }; // 添加参数映射 dbTarget2.Parameters.Add(new DatabaseParameterInfo("@level", "${level}")); dbTarget2.Parameters.Add(new DatabaseParameterInfo("@message", "${message}")); dbTarget2.Parameters.Add(new DatabaseParameterInfo("@timestamp", "${date:format=yyyy-MM-dd HH:mm:ss.fff}")); dbTarget2.Parameters.Add(new DatabaseParameterInfo("@logger", "${logger}")); // 添加目标到配置 logConfig.AddTarget(dbTarget2); // 添加日志规则 logConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, dbTarget2)); // 应用配置 LogManager.Configuration = logConfig; var logger = LogManager.GetCurrentClassLogger(); logger.Info("这是一条测试数据库日志"); logger.Error("这是一条错误日志"); // 确保日志被刷新 LogManager.Shutdown(); } } }
内容的提问来源于stack exchange,提问作者coolswastik

