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

news2025/5/14 3:22:22

前言

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2375076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s监控方案实践(三):部署与配置Grafana可视化平台

k8s监控方案实践&#xff08;三&#xff09;&#xff1a;部署与配置Grafana可视化平台 文章目录 k8s监控方案实践&#xff08;三&#xff09;&#xff1a;部署与配置Grafana可视化平台一、Grafana简介1. 什么是Grafana&#xff1f;2. Grafana与Prometheus的关系3. Grafana应用场…

嵌入式系统架构验证工具:AADL Inspector v1.10 全新升级

软件架构建模与早期验证是嵌入式应用的关键环节。架构分析与设计语言&#xff08;AADL&#xff09;是专为应用软件及执行平台架构模型设计的语言&#xff0c;兼具文本与图形化的双重特性。AADL Inspector是一款轻量级的独立工具&#xff1a; 核心处理能力包括 √ 支持处理AA…

STM32-模电

目录 一、MOS管 二、二极管 三、IGBT 四、运算放大器 五、推挽、开漏、上拉电阻 一、MOS管 1. MOS简介 这里以nmos管为例&#xff0c;注意箭头方向。G门极/栅极&#xff0c;D漏极&#xff0c;S源极。 当给G通高电平时&#xff0c;灯泡点亮&#xff0c;给G通低电平时&a…

华为云Flexus+DeepSeek征文|从开通到应用:华为云DeepSeek-V3/R1商用服务深度体验

前言 本文章主要讲述在华为云ModelArts Studio上 开通DeepSeek-V3/R1商用服务的流程&#xff0c;以及开通过程中的经验分享和使用感受帮我更多开发者&#xff0c;在华为云平台快速完成 DeepSeek-V3/R1商用服务的开通以及使用入门注意&#xff1a;避免测试过程中出现部署失败等问…

鸿蒙NEXT开发动画案例5

1.创建空白项目 2.Page文件夹下面新建Spin.ets文件&#xff0c;代码如下&#xff1a; /*** TODO SpinKit动画组件 - Pulse 脉冲动画* author: CSDN—鸿蒙布道师* since: 2024/05/09*/ ComponentV2 export struct SpinFive {// 参数定义Require Param spinSize: number 48;Re…

ctfshow——web入门351~356

SSRF没有出网的部分 web入门351 $ch curl_init($url); 作用&#xff1a;初始化一个 cURL 会话&#xff0c;并设置目标 URL。解释&#xff1a; curl_init($url) 创建一个新的 cURL 资源&#xff0c;并将其与 $url 关联。这里的 $url 是用户提供的&#xff0c;因此目标地址完全…

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】金融风控分析案例-10.1 风险数据清洗与特征工程

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 PostgreSQL金融风控分析案例&#xff1a;风险数据清洗与特征工程实战一、案例背景&#xff1a;金融风控数据处理需求二、风险数据清洗实战&#xff08;一&#xff09;缺失值…

美女热舞混剪视频批量剪辑生产技术实践:智能处理与原创性提升方案解析

一、引言&#xff1a;短视频工业化生产的技术转型 在美女类短视频内容运营中&#xff0c;通过标准化技术流程实现「高质量、规模化」产出成为核心需求。本文结合实战经验&#xff0c;解析如何通过智能素材重组、AI 语音合成、动态元素叠加等技术手段&#xff0c;构建自动化生产…

神经网络基础-从零开始搭建一个神经网络

一、什么是神经网络 人工神经网络(Articial Neural Network,简写为ANN)也称为神经网络(NN),是一种模仿生物神经网络和功能的计算模型,人脑可以看做是一个生物神经网络,由众多的神经元连接而成,各个神经元传递复杂的电信号,树突接收到输入信号,然后对信号进行处理,通…

#Redis黑马点评#(五)Redisson原理详解

目录 一 基于Redis的分布式锁优化 二 Redisson 1 实现步骤 2 Redisson可重入锁机制 3 Redisson可重试机制 4 Redisson超时释放机制 5 RedissonMultiLock解决主从一致性 三 trylock与lock两者有何区别 四 Redis优化秒杀 一 基于Redis的分布式锁优化 二 Redisson Redis…

23.(vue3.x+vite)引入组件并动态切换(component)

让多个组件使用同一个挂载点,并动态切换,这就是动态组件 效果截图 A组件代码: <template><div><div>{{message }}</</

VBA会被Python代替吗

VBA不会完全被Python取代、但Python在自动化、数据分析与跨平台开发等方面的优势使其越来越受欢迎、两者将长期并存且各具优势。 Python以其易于学习的语法、强大的开源生态系统和跨平台支持&#xff0c;逐渐成为自动化和数据分析领域的主流工具。然而&#xff0c;VBA依旧在Exc…

SEMI E40-0200 STANDARD FOR PROCESSING MANAGEMENT(加工管理标准)-(三)完结

10 消息服务详情 10.1 本章定义实现加工管理概念所需的消息服务。这些消息已在第8.1节中初步介绍。 协议无关性&#xff1a;这些服务独立于所使用的消息协议&#xff0c;可映射至SECS-II&#xff08;SEMI E5&#xff09;或其他类似协议。 10.1.1 消息服务定义内容包括&#…

MySQL数据库创建、删除、修改

一&#xff1a;建库建表 我们以学校体系进行建表。将数据库命名为school。 以下代码中的大写均可小写不影响。如CREATE DATABASE与create database相同 四个关键的实体分别是学院、老师、学生和课程&#xff0c;其中&#xff0c;学生跟学院是从属关系&#xff0c;这个关系从…

【氮化镓】GaN在不同电子能量损失的SHI辐射下的损伤

该文的主要发现和结论如下: GaN的再结晶特性 :GaN在离子撞击区域具有较高的再结晶倾向,这导致其形成永久损伤的阈值较高。在所有研究的电子能量损失 regime 下,GaN都表现出这种倾向,但在电子能量损失增加时,其效率会降低,尤其是在材料发生解离并形成N₂气泡时。 能量损失…

防火墙来回路径不一致导致的业务异常

案例拓扑&#xff1a; 拓扑描述&#xff1a; 服务器有2块网卡&#xff0c;内网网卡2.2.2.1/24 网关2.2.254 提供内网用户访问&#xff1b; 外网网卡1.1.1.1/24&#xff0c;外网网关1.1.1.254 80端口映射到公网 这个时候服务器有2条默认路由&#xff0c;分布是0.0.0.0 0.0.0.0 1…

WTK6900C-48L:离线语音芯片重构玩具DNA,从“按键操控”到“声控陪伴”的交互跃迁

一&#xff1a;开发背景 随着消费升级和AI技术进步&#xff0c;传统玩具的机械式互动已难以满足市场需求。语音控制芯片的引入使玩具实现了从被动玩耍到智能交互的跨越式发展。通过集成高性价比的语音识别芯片&#xff0c;现代智能玩具不仅能精准响应儿童指令&#xff0c;还能实…

Python 数据分析与可视化:开启数据洞察之旅(5/10)

一、Python 数据分析与可视化简介 在当今数字化时代&#xff0c;数据就像一座蕴藏无限价值的宝藏&#xff0c;等待着我们去挖掘和探索。而 Python&#xff0c;作为数据科学领域的明星语言&#xff0c;凭借其丰富的库和强大的功能&#xff0c;成为了开启这座宝藏的关键钥匙&…

gitkraken 使用教程

一、安装教程 安装6.5.3&#xff0c;之后是收费的&#xff0c;Windows版免安装 二、使用教程 0. 软件说明 gitkraken是一个git本地仓库管理软件&#xff0c;可以管理多个仓库&#xff0c;并且仓库可以属于多个网站多个账户。 1. 克隆仓库 选择要克隆到什么位置&#xff0…

【LeetCode 热题 100】二叉树 系列

&#x1f4c1; 104. 二叉树的最大深度 深度就是树的高度&#xff0c;即只要左右子树其中有一个不为空&#xff0c;就继续往下递归&#xff0c;知道节点为空&#xff0c;向上返回。 int maxDepth(TreeNode* root) {if(root nullptr)return 0;return max(maxDepth(root->lef…