C#利用log4net和sqlite数据库记载日志

打印 上一主题 下一主题

主题 897|帖子 897|积分 2691

1 安装包

两个包:

  • log4net
  • System.Data.SQLite
第二个包也可以利用Microsoft.Data.Sqlite,查到的资料显示假如情况利用的是 .NET Core 或 .NET 5+,则建议利用Microsoft.Data.Sqlite。但是我并没有测试第二个包,可能利用上有区别。
2 下载Sqlite

假如本地没有sqlite情况的话,需要先下载。官网下载链接
进去之后直接找各自情况对应的版本,假如是windows情况的话,直接下载下图中标记的tool,中间那个下载链接是下载sqlite3.dll,不过我并不清楚如何利用。

tool解压之后有如下几个文件,双击打开sqlite3.exe即可。

3 Sqlite常用命令

打开是一个命令行界面,可以利用.help查看常用的命令及解释。
  1. .help
复制代码

创建数据库文件利用.open xxx,这条语句,假如发现数据库文件存在,就会直接打开,假如不存在,就会先创建再打开。
  1. .open test.db
复制代码
在目录内可以看到创建的数据库文件。

.databases可以查询所有数据库文件

.tables可以查询所有表(我还未创建,所以现在还没有表)

sql语句的话可以查询相干资料。
查询的数据以标准格式显示。
  1. .header on
  2. .mode column
  3. SELECT * FROM COMPANY;
复制代码
当然sqlite也有可视化的软件,但是我现在没用到,所以没有下载安装,需要的话可以自行查询。
4 创建日志相干基本表

利用命令创建日志表,包含id(利用自增,当然可以换成uuid或者其它形式)、日期、线程号、级别(info、error这些)、记载者、具体记载的信息、异常信息。具体内容要对应log4net的设置。
  1. CREATE TABLE Log (
  2.   Id INTEGER PRIMARY KEY AUTOINCREMENT,
  3.   Date DATETIME,
  4.   Thread VARCHAR(255),
  5.   Level VARCHAR(50),
  6.   Logger VARCHAR(255),
  7.   Message TEXT,
  8.   Exception TEXT
  9. );
复制代码
5 log4net设置

更换数据库连接。sql语句的内容对应数据库基本表的字段。
  1. <configuration>
  2.   <configSections>
  3.     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  4.   </configSections>
  5.   <log4net>
  6.     <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
  7.       <bufferSize value="1" />
  8.       <connectionType value="System.Data.SQLite.SQLiteConnection, System.Data.SQLite" />
  9.       <connectionString value="Data Source=./database/logs.db;Version=3;" />
  10.       <commandText value="INSERT INTO Log (Date, Thread, Level, Logger, Message, Exception) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />
  11.       
  12.       <parameter>
  13.         <parameterName value="@log_date" />
  14.         <dbType value="DateTime" />
  15.         <layout type="log4net.Layout.RawTimeStampLayout" />
  16.       </parameter>
  17.       <parameter>
  18.         <parameterName value="@thread" />
  19.         <dbType value="String" />
  20.         <size value="255" />
  21.         <layout type="log4net.Layout.PatternLayout">
  22.           <conversionPattern value="%thread" />
  23.         </layout>
  24.       </parameter>
  25.       <parameter>
  26.         <parameterName value="@log_level" />
  27.         <dbType value="String" />
  28.         <size value="50" />
  29.         <layout type="log4net.Layout.PatternLayout">
  30.           <conversionPattern value="%level" />
  31.         </layout>
  32.       </parameter>
  33.       <parameter>
  34.         <parameterName value="@logger" />
  35.         <dbType value="String" />
  36.         <size value="255" />
  37.         <layout type="log4net.Layout.PatternLayout">
  38.           <conversionPattern value="%logger" />
  39.         </layout>
  40.       </parameter>
  41.       <parameter>
  42.         <parameterName value="@message" />
  43.         <dbType value="String" />
  44.         <size value="4000" />
  45.         <layout type="log4net.Layout.PatternLayout">
  46.           <conversionPattern value="%message" />
  47.         </layout>
  48.       </parameter>
  49.       <parameter>
  50.         <parameterName value="@exception" />
  51.         <dbType value="String" />
  52.         <size value="2000" />
  53.         <layout type="log4net.Layout.ExceptionLayout" />
  54.       </parameter>
  55.     </appender>
  56.     <root>
  57.       <level value="ALL" />
  58.       <appender-ref ref="AdoNetAppender" />
  59.     </root>
  60.   </log4net>
  61. </configuration>
复制代码
6 日志记载

把生成的db文件拷到步伐里,这个文件就是记载文件的数据库了,其它的都不告急。当然,假如想查看数据库的话,也可以把sqlite3.exe拷过来。
读取log4net的设置建议写在AssemblyInfo.cs,这样步伐启动时会默认加载设置文件。
  1. //Log4net配置
  2. [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
复制代码

具体步伐如下:
  1. private static readonly ILog log = LogManager.GetLogger(typeof(Form1));
  2. log.Info("这是一条info语句");
  3. log.Warn("这是一条warn语句");
  4. log.Error("这是一条错误语句", new Exception("测试异常"));
复制代码
查看日志是否正常写入数据库。

7 利用C#步伐查询sqlite

步伐如下:
  1. // 构建连接字符串
  2. string connectionString = $"Data Source=./database/SoftWareBaseLog.db;Version=3;";
  3. // 创建 SQLite 连接
  4. using (var connection = new SQLiteConnection(connectionString))
  5. {
  6.     connection.Open();
  7.     // 创建 SQL 命令
  8.     string sql = "SELECT Message FROM Log where Level='ERROR'";
  9.     using (var command = new SQLiteCommand(sql, connection))
  10.     {
  11.         // 执行命令并读取数据
  12.         using (var reader = command.ExecuteReader())
  13.         {
  14.             while (reader.Read())
  15.             {
  16.                 string msg = reader.GetString(0);
  17.                 Console.WriteLine(msg);
  18.             }
  19.         }
  20.     }
  21. }
复制代码
8 实时显示日志

现在所有日志都写到数据库里了,那要是还想实时显示到界面上,当然也有很多方式实现,不过我这里建议实时显示可以利用log4net的自有功能。
好比我想利用winform中的listbox来实时显示日志,可以建立一个Appender(附加),继承于log4net的AppenderSkeleton,这是一个抽象类,有一个抽象方法。
具体的,可以参考以下步伐,这里会显示所有的日志,假如需要过滤的话,可以在这个基础上改。另外,一定一定一定要给this.Layout赋值,这是日志在界面上的显示方式,假如没有写的话,就会劳绩一个报错:“A layout must be set”,去网上搜这条内容,不一定能找到办理方案。
  1. public class ListBoxAppender : AppenderSkeleton
  2. {
  3.     private ListBox _ListBox;
  4.     public ListBoxAppender(ListBox box)
  5.     {
  6.         _ListBox = box;
  7.         this.Layout = new PatternLayout("%date [%thread] %-5level %logger - %message%newline");
  8.     }
  9.     protected override void Append(LoggingEvent loggingEvent)
  10.     {
  11.         // 获取日志信息
  12.         string logMessage = RenderLoggingEvent(loggingEvent);
  13.         // 更新 UI 控件
  14.         if (_ListBox.InvokeRequired)
  15.         {
  16.             _ListBox.Invoke(new Action(() => AppendText(logMessage)));
  17.         }
  18.         else
  19.         {
  20.             AppendText(logMessage);
  21.         }
  22.     }
  23.     private void AppendText(string text)
  24.     {
  25.         _ListBox.Items.Add(text);
  26.     }
  27. }
复制代码
只需要给log4net设置一次就可以利用,这样,每次调用日志记载,界面上的lbxLog控件就可以一直显示最新的日志信息。
  1. // 添加自定义 Appender
  2. var listBoxAppender = new ListBoxAppender(lbxLog);
  3. log4net.Config.BasicConfigurator.Configure(listBoxAppender);
复制代码
但是,测试出现了标题,在多线程、短时间添加多条日志的情况下会导致界面卡死,猜测是多条日志抢一个listbox的索引出现的标题,更好的方案是添加缓存机制,生产者-消耗者模式。
生产者只记载到队列里即可,这里为了避免队列堆积,利用了定长的一个概念,太多的日志堆积会丢掉一部份数据。
  1. public class LogQueueAppender : AppenderSkeleton
  2. {
  3.     /// <summary>
  4.     /// 锁对象
  5.     /// </summary>
  6.     private static readonly object _lock = new object();
  7.     private static LogQueueAppender Instance { get; set; }
  8.     /// <summary>
  9.     /// 日志队列
  10.     /// 不再直接更新控件,在日志频繁时会出现控件卡死的情况
  11.     /// </summary>
  12.     private ConcurrentQueue<string> LogQueue { get; } = new ConcurrentQueue<string>();
  13.     private LogQueueAppender()
  14.     {
  15.         //Layout必须赋值,否则会报错
  16.         this.Layout = new PatternLayout("%date [%thread] %-5level %logger - %message%newline");
  17.     }
  18.     /// <summary>
  19.     /// 获取实体对象
  20.     /// </summary>
  21.     /// <returns></returns>
  22.     public static LogQueueAppender GetInstance()
  23.     {
  24.         if (Instance == null)
  25.         {
  26.             lock (_lock)
  27.             {
  28.                 if (Instance == null)
  29.                 {
  30.                     Instance = new LogQueueAppender();
  31.                 }
  32.             }
  33.         }
  34.         return Instance;
  35.     }
  36.     /// <summary>
  37.     /// 获取日志队列
  38.     /// </summary>
  39.     /// <returns></returns>
  40.     public ConcurrentQueue<string> GetLogQueue()
  41.     {
  42.         return LogQueue;
  43.     }
  44.     /// <summary>
  45.     /// 覆写Append方法
  46.     /// </summary>
  47.     /// <param name="loggingEvent"></param>
  48.     protected override void Append(LoggingEvent loggingEvent)
  49.     {
  50.         // 获取日志信息
  51.         string logMessage = RenderLoggingEvent(loggingEvent);
  52.         AppendText(logMessage);
  53.     }
  54.     /// <summary>
  55.     /// 添加日志信息
  56.     /// </summary>
  57.     /// <param name="text"></param>
  58.     private void AppendText(string text)
  59.     {
  60.         //最多存储200条日志,多余的会直接丢弃
  61.         if (LogQueue.Count >= 200)
  62.         {
  63.             LogQueue.TryDequeue(out string message);               
  64.         }
  65.         LogQueue.Enqueue(text);
  66.     }
复制代码
而消耗者负责将日志信息更新到界面上,这里每次只更新一条日志信息,我认为足够了,当然这可以改成一次更新多条信息,避免日志堆积。
  1. /// <summary>
  2. /// 读取日志信息
  3. /// </summary>
  4. private void LoadLog()
  5. {
  6.     LogThreadRunFlag = true;
  7.     Task.Factory.StartNew(() => {
  8.         while (LogThreadRunFlag == true)
  9.         {
  10.             bool flag = LogQueueAppender.GetInstance().GetLogQueue().TryDequeue(out string log);
  11.             if (flag)
  12.             {
  13.                 lvwLog.Invoke(new Action(() => AppendText(log)));
  14.             }
  15.             Thread.Sleep(50);
  16.         }
  17.     }, TaskCreationOptions.LongRunning);           
  18. }
  19. /// <summary>
  20. /// 添加到界面中
  21. /// </summary>
  22. /// <param name="text">日志信息</param>
  23. private void AppendText(string text)
  24. {
  25.     if (lvwLog.Items.Count == 100)
  26.     {
  27.         lvwLog.Items.Clear();
  28.     }
  29.     lvwLog.Items.Add(text);
  30.     if (text.Contains("ERROR"))
  31.     {
  32.         lvwLog.Items[lvwLog.Items.Count - 1].ForeColor = Color.Red;
  33.     }
  34. }
复制代码
另外,控件换成了listview,因为这个可以很方便地“高亮”错误日志。相干listview的设置如下:
  1. lvwLog.View = View.Details; // 设置为竖向排列
  2. lvwLog.Scrollable = true; // 确保滚动条可用
  3. lvwLog.Columns.Add("日志信息", -2);   //添加一列数据,并占满全行
  4. lvwLog.DoubleBuffered(true);    //使用双缓冲,避免闪烁
复制代码
结果如下:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大号在练葵花宝典

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表