前言
原来使用的是SQLServer数据库,使用Nlog很流畅,没有什么问题。现在有个新项目需要使用麒麟操作系统和达梦数据库,业务流程开发完成之后发现Nlog配置文件中把数据库连接内容修改之后不能执行插入操作。
原Nlog.config配置
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel=" Debug" internalLogFile="path_to_your_internal_log_file.txt">
throwExceptions="false">
<targets>
<!--<target name="file" xsi:type="AsyncWrapper" queueLimit="5000" maxArchiveDays="7" maxArchiveFiles="7" overflowAction="Discard">
<target xsi:type="File" fileName="${basedir}/Nlogs/${shortdate}.log" layout="${longdate} ${level:uppercase=true} ${event-context:item=Action} ${message} ${event-context:item=Amount} ${stacktrace}" />
</target>-->
<target name="file" xsi:type="AsyncWrapper" queueLimit="5000" overflowAction="Discard" >
<target xsi:type="File"
fileName="${basedir}/Nlogs/${shortdate}.log"
archiveFileName="${basedir}/Archive/${shortdate}.txt"
layout="***${newline} | 日志时间:${longdate} | 级别:${level:uppercase=true}${newline} | 消息:${message}${newline} | 开始时间: ${mdlc:item=log-start-time} | 开始线程: ${mdlc:item=log-start-thread} | 方法:${callsite:className=true:fileName=true:includeSourcePath=false:methodName=true}${newline}***"
archiveAboveSize="102400000"
maxArchiveFiles="30"
archiveEvery="Day"
/>
</target>
<target xsi:type="Database"
name="dbTarget"
dbProvider="Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient"
connectionString="TrustServerCertificate=True;Data Source=x.x.x.x;Initial Catalog=Logs;Persist Security Info=True;User ID=sa;Password=123456;"
commandText="INSERT INTO [dbo].[Logs] ([LongDate] ,[Level] ,[MachineName], [ProcessName], [ProcessId] , [ThreadId] ,[Logger] ,[Message] ,[Exception], [UserName], [IP], LogStartTicks, LogStartThread, PollFlag) VALUES (@LongDate ,@Level ,@MachineName, @ProcessName, @ProcessId, @ThreadId ,@Logger ,@Message ,@Exception, @UserName, @IP, @LogStartTicks, @LogStartThread, @PollFlag)">
<parameter name="@LongDate" layout="${date:format=yyyy-MM-dd HH\:mm\:ss.fff}" />
<parameter name="@Level" layout="${level}" />
<parameter name="@MachineName" layout="${machineName}" />
<parameter name="@ProcessName" layout="${processname}" />
<parameter name="@ProcessId" layout="${processid}" />
<parameter name="@ThreadId" layout="${threadid}" />
<parameter name="@Logger" layout="${logger}" />
<parameter name="@Message" layout="${message}" />
<parameter name="@Exception" layout="${exception:format=tostring}" />
<parameter name="@UserName" layout="${aspnet-user-identity}" />
<parameter name="@IP" layout="${aspnet-request:serverVariable=REMOTE_ADDR}" />
<parameter name="@LogStartTicks" layout="${mdlc:item=log-start-time}" />
<parameter name="@LogStartThread" layout="${mdlc:item=log-start-thread}" />
<parameter name="@PollFlag" layout="${mdlc:item=log-poll-flag}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="file" />
<logger name="*" minlevel="Info" writeTo="file,dbTarget" />
<logger name="*" minlevel="ERROR" writeTo="file,dbTarget,SendMail" />
</rules>
</nlog>
将上面的数据库内容修改成达梦数据库的,不执行插入。查了较多资料也没查出来原因。(包都装完了,和包没关系)
实现
Nlog配置文件
<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Debug"
internalLogFile="B:\logs\nlog-internal.log"
throwExceptions="false">
<!-- 必须添加:注册包含DmDatabaseTarget的程序集 -->
<extensions>
<add assembly="GeneralWms.Application"/>
</extensions>
<targets>
<!-- 文件日志 -->
<target name="file" xsi:type="File"
fileName="${basedir}/Nlogs/${shortdate}.log"
archiveFileName="${basedir}/Archive/${shortdate}.txt"
layout="***${newline} | 日志时间:${longdate} | 级别:${level:uppercase=true}${newline} | 消息:${message}${newline} | 开始时间: ${mdlc:item=log-start-time} | 开始线程: ${mdlc:item=log-start-thread} | 方法:${callsite:className=true:fileName=true:includeSourcePath=false:methodName=true}${newline}***"
archiveAboveSize="102400000"
maxArchiveFiles="30"
archiveEvery="Day"/>
<!-- 同步写入数据库 -->
<target name="dmDb"
type="~.DmDatabaseTarget, GeneralWms.Application"
ConnectionString="Server=x.x.x.x;Database=DAMENG;User=SYSDBA;Password=123456;"
includeCallSite="true"/>
</targets>
<rules>
<!--只写入文件目标-->
<logger name="*" minlevel="Debug" writeTo="file" />
<!--同时写入文件和数据库-->
<logger name="*" minlevel="Info" writeTo="file,dmDb" />
<!--错误及以上级别同时写入文件和数据库-->
<logger name="*" minlevel="Error" writeTo="file,dmDb" />
</rules>
<targets>
<target name="debug" type="Debug" layout="${longdate} ${level} ${message} ${exception}" />
</targets>
</nlog>
新增增加类
[Target("DmDatabaseTarget")]
public class DmDatabaseTarget : NLog.Targets.TargetWithContext
{
// 数据库连接字符串属性(可在NLog.config中配置)
public string ConnectionString = "";//在这写死
// 构造函数
public DmDatabaseTarget()
{
// 默认包含调用站点信息
this.IncludeCallSite = true;
this.IncludeCallSiteStackTrace = true;
}
// 异步写入日志到数据库
protected override void Write(LogEventInfo logEvent)
{
//var logEvent = new LogEventInfo(LogLevel.Info, loggerName, "Your message here");
//logEvent.Properties["LogStartTicks"] = someValue; // 设置 LogStartTicks 的值
//logEvent.Properties["LogStartThread"] = Thread.CurrentThread.ManagedThreadId; // 设置 LogStartThread 的值
if (string.IsNullOrEmpty(ConnectionString))
{
throw new InvalidOperationException("Connection string is null or empty.");
}
try
{
using (var conn = new DmConnection(ConnectionString))
{
conn.Open(); // 打开数据库连接
using (var cmd = new DmCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"
INSERT INTO LOGS (ID,
LONGDATE, LEVEL, MACHINENAME, PROCESSNAME,
PROCESSID, THREADID, LOGGER, MESSAGE,
EXCEPTION, USERNAME, IP, LOGSTARTTICKS,
LOGSTARTTHREAD, POLLFLAG
) VALUES (:ID,
:LONGDATE, :LEVEL, :MACHINENAME, :PROCESSNAME,
:PROCESSID, :THREADID, :LOGGER, :MESSAGE,
:EXCEPTION, :USERNAME, :IP, :LOGSTARTTICKS,
:LOGSTARTTHREAD, :POLLFLAG
)";
var generator = new SnowflakeIdGenerator(0);
// 使用实例调用 GenerateId 方法
var snowflakeId = generator.GenerateId();
// 设置日志开始时间和当前线程ID到 MDC
long logStartTicks = DateTime.UtcNow.Ticks;
int logStartThread = Thread.CurrentThread.ManagedThreadId;
// 将这些值手动添加到 logEvent.Properties
logEvent.Properties["LogStartTicks"] = logStartTicks;
logEvent.Properties["LogStartThread"] = logStartThread;
// 添加参数(达梦数据库使用:作为参数前缀)
cmd.Parameters.Add(new DmParameter(":ID", snowflakeId));
cmd.Parameters.Add(new DmParameter(":LONGDATE", logEvent.TimeStamp));
cmd.Parameters.Add(new DmParameter(":LEVEL", logEvent.Level.ToString()));
cmd.Parameters.Add(new DmParameter(":MACHINENAME", Environment.MachineName));
cmd.Parameters.Add(new DmParameter(":PROCESSNAME", GetCurrentProcessName()));
cmd.Parameters.Add(new DmParameter(":PROCESSID", GetCurrentProcessId()));
cmd.Parameters.Add(new DmParameter(":THREADID", System.Threading.Thread.CurrentThread.ManagedThreadId));
cmd.Parameters.Add(new DmParameter(":LOGGER", logEvent.LoggerName));
cmd.Parameters.Add(new DmParameter(":MESSAGE", logEvent.FormattedMessage));
cmd.Parameters.Add(new DmParameter(":EXCEPTION", logEvent.Exception?.ToString() ?? string.Empty));
// 设置日志开始时间和当前线程ID到 MDC
// 在开始记录日志之前,将值添加到 MDC
//MappedDiagnosticsContext.Set("LogStartTicks", DateTime.UtcNow.Ticks.ToString());
//MappedDiagnosticsContext.Set("LogStartThread", Thread.CurrentThread.ManagedThreadId.ToString());
// 从日志属性中获取自定义字段
logEvent.Properties.TryGetValue("UserName", out var userName);
logEvent.Properties.TryGetValue("IP", out var ip);
logEvent.Properties.TryGetValue("LogStartTicks", out var logStartTicksProperty);
logEvent.Properties.TryGetValue("LogStartThread", out var logStartThreadProperty);
logEvent.Properties.TryGetValue("PollFlag", out var pollFlag);
cmd.Parameters.Add(new DmParameter(":USERNAME", userName ?? DBNull.Value));
cmd.Parameters.Add(new DmParameter(":IP", ip ?? DBNull.Value));
cmd.Parameters.Add(new DmParameter(":LOGSTARTTICKS", logStartTicksProperty ?? logStartTicks));
cmd.Parameters.Add(new DmParameter(":LOGSTARTTHREAD", logStartThreadProperty ?? logStartThread));
cmd.Parameters.Add(new DmParameter(":POLLFLAG", pollFlag ?? false));
// 执行SQL命令
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
// 捕获异常并记录日志
LogManager.GetCurrentClassLogger().Error(ex, $"写入数据库日志失败,日志内容:{logEvent.FormattedMessage}");
}
}
// 辅助方法:获取当前进程名
private static string GetCurrentProcessName()
{
try
{
return System.Diagnostics.Process.GetCurrentProcess().ProcessName;
}
catch
{
return "Unknown";
}
}
// 辅助方法:获取当前进程ID
private static int GetCurrentProcessId()
{
try
{
return System.Diagnostics.Process.GetCurrentProcess().Id;
}
catch
{
return -1;
}
}
}
Program类
在Main方法中写
// 类型加载测试
try
{
var type = Type.GetType("~.DmDatabaseTarget, GeneralWms.Application");
if (type == null)
{
throw new Exception("类型加载失败!可能原因:\n" +
"1. 命名空间不匹配\n" +
"2. 类不是 public\n" +
"3. 程序集未正确引用");
}
Console.WriteLine("类型加载成功!");
// 实例化测试
var target = Activator.CreateInstance(type) as NLog.Targets.Target;
Console.WriteLine(target != null ? "Target 实例化成功" : "Target 实例化失败");
}
catch (Exception ex)
{
Console.WriteLine($"测试失败: {ex}");
}
//
// 配置 NLog
var config = new LoggingConfiguration();
// 使用反射加载自定义目标
Type targetType = Type.GetType("~.DmDatabaseTarget, GeneralWms.Application");
if (targetType != null)
{
// 创建目标对象
var target = (Target)Activator.CreateInstance(targetType);
target.Name = "dmDb"; // 设置目标名称
// 添加目标到配置中
config.AddTarget(target.Name, target);
// 创建日志规则
var rule = new LoggingRule("*", LogLevel.Info, target); // 从 Info 级别开始记录
config.LoggingRules.Add(rule);
// 应用配置
LogManager.Configuration = config;
}
else
{
Console.WriteLine("无法找到指定的目标类型.");
}
// 测试日志
//var logger = LogManager.GetCurrentClassLogger();
//logger.Debug("拐子大王来咯!");
先记录一下,后面有项目用到,直接拿着用吧。