Nlog适配达梦数据库进行日志插入

前言

原来使用的是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("拐子大王来咯!");

先记录一下,后面有项目用到,直接拿着用吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晚风偷吻云朵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值