磨刀不誤砍柴工——統一日誌系統

来源:http://www.cnblogs.com/tdws/archive/2017/01/18/6298124.html
-Advertisement-
Play Games

一. 寫在前面 我這篇分享是很基礎的東西 我希望愛技術的你不要錯過exceptionless和ELK,當然你也可以用nosql+搜索技術比如es。 日誌系統對於任何項目都是必不可少的,無論對於測試階段的debug,性能測試,執行時間,操作記錄還是線上的問題排查,訪問記錄等,日誌系統都扮演著重要的角色 ...


 本文版權歸博客園和作者吳雙本人共同所有,轉載和爬蟲必須註明原文地址:www.cnblogs.com/tdws 。
本系統為自用系統。能得到評論區這麼多大神推薦其他技術,才知道自己的有多low。 回去學習一下

一.   寫在前面

   我這篇分享是很基礎的東西 我希望愛技術的你不要錯過exceptionless和ELK,當然你也可以用nosql+搜索技術比如es。

日誌系統對於任何項目都是必不可少的,無論對於測試階段的debug,性能測試,執行時間,操作記錄還是線上的問題排查,訪問記錄等,日誌系統都扮演著重要的角色。本篇分享的目的是能幫助需要的人快速搭建自己的LogSystem.,僅供參考。 先上個圖唄,自認為頁面還算清爽吧:

我的LogSystem使用Log4net入庫的方式,網上特別多的分享,但是能完整運行下來的真是很少,所以現在需要和以後用得上的小伙伴抓緊收藏咯。

 

二.  Log4Net自定義內容入庫

 Log4Net存日誌的方式,給人的感覺實在是不實用,IT行業不都求一個自動化嗎?廢話不說了,先上Log4net入庫系統的代碼。

LogSystem資料庫結構,我的建議是一個項目一個表。

在Log組件中,你需要這樣幾個類。下麵分別給出代碼:

LogContent.cs,這裡定義了Log實體,在實體化實體的時候,通過給構造函數傳參創建好這個對象。註釋很詳細了

 1 using System;
 2 
 3 namespace LogComponent
 4 {
 5     public class LogContent
 6     {
 7 
 8         public LogContent(string logLevel, string logMsg, string logModule, string description, string userName)
 9         {
10             LogLevel = logLevel;
11             UserName = userName;
12             Description = description;
13             LogMsg = logMsg;
14             LogModule = logModule;
15         }
16 
17         /// <summary>
18         /// 日誌級別
19         /// </summary>
20         public string LogLevel { get; set; }
21 
22         /// <summary>
23         /// 日誌消息
24         /// </summary>
25         public string LogMsg { get; set; }
26 
27         /// <summary>
28         /// 系統登陸用戶
29         /// </summary>
30         public string UserName { get; set; }
31 
32         /// <summary>
33         /// 日誌描述信息
34         /// </summary>
35         public string Description { get; set; }
36 
37         /// <summary>
38         /// 記錄時間
39         /// </summary>
40         public DateTime LogDate { get; set; }
41 
42         /// <summary>
43         /// 模塊名稱
44         /// </summary>
45         public string LogModule { get; set; }
46     }
47 }
View Code

LogHelper.cs,定義了日誌級別,和寫入方法

 1 [assembly: log4net.Config.XmlConfigurator(Watch = true,ConfigFile = "log4net.config")]
 2 namespace LogComponent
 3 {
 4     public class LogHelper
 5     {
 6         static log4net.ILog log = log4net.LogManager.GetLogger("myLogger");
 7 
 8         /// <summary>
 9         /// 異常日誌
10         /// </summary>
11         /// <param name="logMsg">日誌信息</param>
12         /// <param name="logModule">代碼模塊</param>
13         /// <param name="description">其他描述</param>
14         /// <param name="userName">用戶名</param>
15         public static void LogError(string logMsg, string logModule, string description = "", string userName = "")
16         {
17             log.Error(new LogContent("Error", SubLogString(logMsg), logModule, SubLogString(description), userName));
18         }
19 
20         public static void LogInfo(string logMsg, string logModule, string description = "", string userName = "")
21         {
22             log.Info(new LogContent("Info", SubLogString(logMsg), logModule, SubLogString(description), userName));
23         }
24 
25         public static void LogWarn(string logMsg, string logModule, string description = "", string userName = "")
26         {
27             log.Warn(new LogContent("Warn", SubLogString(logMsg), logModule, SubLogString(description), userName));
28         }
29 
30         public static void LogDebug(string logMsg, string logModule, string description = "", string userName = "")
31         {
32             log.Debug(new LogContent("Debug", SubLogString(logMsg), logModule, SubLogString(description), userName));
33         }
34 
35         private static string SubLogString(string str)
36         {
37             if (str.Length > 1500)
38             {
39                 return str.Substring(0, 1500);
40             }
41             return str;
42         }
43     }
44 }
View Code

MessagePartternConverter.cs

 1 using log4net.Core;
 2 using log4net.Layout.Pattern;
 3 using System.IO;
 4 using System.Reflection;
 5 namespace LogComponent
 6 {
 7     class MessagePatternConverter : PatternLayoutConverter
 8     {
 9         protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
10         {
11             if (Option != null)
12             {
13                 // Write the value for the specified key
14                 WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent));
15             }
16             else
17             {
18                 // Write all the key value pairs
19                 WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties());
20             }
21         }
22         /// <summary>
23         /// 通過反射獲取傳入的日誌對象的某個屬性的值
24         /// </summary>
25         /// <param name="property"></param>
26         /// <returns></returns>
27         private object LookupProperty(string property, log4net.Core.LoggingEvent loggingEvent)
28         {
29             object propertyValue = string.Empty;
30             PropertyInfo propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(property);
31             if (propertyInfo != null)
32                 propertyValue = propertyInfo.GetValue(loggingEvent.MessageObject, null);
33             return propertyValue;
34         }
35     }
36 }
View Code

MyLayout.cs

 1 using log4net.Layout;
 2 namespace LogComponent
 3 {
 4     class MyLayout : PatternLayout
 5     {
 6         public MyLayout()
 7         {
 8             this.AddConverter("property", typeof(MessagePatternConverter));
 9         }
10     }
11 }
View Code

其實看到這裡,最重要的並不是代碼了,核心部分Log4net都幫我們寫好了,關鍵在於你的配置,下麵是log4net.config的內容。拿到你的web項目里是一樣用的。但是不要忘了在你的項目中引用nuget:log4net喲。

log4net.config如下:在其中主要配置了log入庫的參數和sql語句,當然還有sql連接。註釋已經很詳細了

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3   <configSections>
 4     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
 5   </configSections>
 6   <log4net>
 7     <root >
 8       <level value="Debug"/>
 9       <appender-ref ref="ADONetAppender"/>
10     </root>
11     <logger name="myLogger">
12       <level value="Debug"/>
13       <appender-ref ref="ADONetAppender"/>
14     </logger>
15     <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender,log4net">
16       <!--BufferSize為緩衝區大小,只有日誌記錄超value條才會一塊寫入到資料庫-->
17       <bufferSize value="1"/>
18       <!--或寫為<param name="BufferSize" value="1" />-->
19       <!--引用-->
20       <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
21       <!--連接資料庫字元串-->
22       <connectionString value="Data Source=115.29.54.31;Initial Catalog=LogSystem;uid=sa;pwd=sa.;MultipleActiveResultSets=True"/>
23       <!--插入到表Log-->
24       <commandText value="INSERT INTO HdPubLog ([LogDate],[LogMsg],[UserName],[Description],[LogLevel],[LogModule]) VALUES (@log_date,@LogMsg,@UserName,@Description,@LogLevel,@LogModule)"/>
25       <parameter>
26         <parameterName value="@log_date"/>
27         <dbType value="DateTime"/>
28         <layout type="log4net.Layout.RawTimeStampLayout"/>
29         <!--獲取log4net中提供的日誌時間RawTimeStampLayout為預設的時間輸出格式-->
30       </parameter>
31       <parameter>
32         <parameterName value="@LogMsg"/>
33         <dbType value="String"/>
34         <size value="1510"/>
35         <layout type="LogComponent.MyLayout, LogComponent">
36           <param name="ConversionPattern" value="%property{LogMsg}"/>
37         </layout>
38       </parameter>
39       <parameter>
40         <parameterName value="@UserName"/>
41         <dbType value="String"/>
42         <size value="50"/>
43         <layout type="LogComponent.MyLayout, LogComponent">
44           <param name="ConversionPattern" value="%property{UserName}"/>
45         </layout>
46       </parameter>
47       <parameter>
48         <parameterName value="@Description"/>
49         <dbType value="String"/>
50         <size value="1510"/>
51         <layout type="LogComponent.MyLayout, LogComponent">
52           <param name="ConversionPattern" value="%property{Description}"/>
53         </layout>
54       </parameter>
55       <parameter>
56         <parameterName value="@LogLevel"/>
57         <dbType value="String"/>
58         <size value="50"/>
59         <layout type="LogComponent.MyLayout, LogComponent">
60           <param name="ConversionPattern" value="%property{LogLevel}"/>
61         </layout>
62       </parameter>
63       <parameter>
64         <parameterName value="@LogModule"/>
65         <dbType value="String"/>
66         <size value="50"/>
67         <layout type="LogComponent.MyLayout, LogComponent">
68           <param name="ConversionPattern" value="%property{LogModule}"/>
69         </layout>
70       </parameter>
71     </appender>
72   </log4net>
73 </configuration>
View Code

這樣一來,你的配置就完成了,你可以直接測試插入的情況:

三.   把Log信息可視化

 我的UI使用的是Datatables.js,彈出框是layer,日期組件好像是layDate,下拉框是修改樣式後的select2。UI代碼是我自己的一個框架里的,內容太多就不貼出來了,你只需要和以前一樣,把數據從庫里查出來,綁定給任意你喜歡的數據表格上。由於單頁面的日誌系統沒有什麼複雜操作,就用個sqlHelper查一下就算了,代碼和條件拼接如下

  1 public class xxxDal
  2     {
  3         private SqlHelper _sqlHelper = new SqlHelper();
  4 
  5         /// <summary>
  6         /// 獲取xxx的日誌
  7         /// </summary>
  8         /// <param name="model"></param>
  9         /// <returns></returns>
 10         public List<LogModel> GetxxxLog(SM_LogModel model)
 11         {
 12             StringBuilder sql = new StringBuilder();
 13             List<SqlParameter> sqlParameters = new List<SqlParameter>();
 14             StringBuilder sqlWhere = new StringBuilder();
 15             if (!string.IsNullOrWhiteSpace(model.LogStartTime))
 16             {
 17                 sqlParameters.Add(new SqlParameter("@LogStartTime", model.LogStartTime));
 18                 sqlWhere.Append(@" AND h.LogDate > @LogStartTime");
 19             }
 20             if (!string.IsNullOrWhiteSpace(model.LogEndTime))
 21             {
 22                 sqlParameters.Add(new SqlParameter("@LogEndTime", model.LogEndTime));
 23                 sqlWhere.Append(@"  AND h.LogDate < @LogEndTime");
 24             }
 25             if (!string.IsNullOrWhiteSpace(model.LogLevel))
 26             {
 27                 sqlParameters.Add(new SqlParameter("@LogLevel", model.LogLevel));
 28                 sqlWhere.Append(@" AND h.LogLevel = @LogLevel");
 29             }
 30             if (!string.IsNullOrWhiteSpace(model.LogModule))
 31             {
 32                 sqlParameters.Add(new SqlParameter("@LogModule", model.LogModule));
 33                 sqlWhere.Append(@" AND h.LogModule = @LogModule");
 34             }
 35             sql.AppendFormat(@"
 36                     WITH    t AS ( SELECT   ROW_NUMBER() OVER ( ORDER BY id DESC ) AS IndexNum ,
 37                         [Id] ,
 38                         CONVERT(VARCHAR, [LogDate], 21) AS [LogDate] ,
 39                         [UserName] ,
 40                         SUBSTRING([Description], 0, 150) AS [Description] ,
 41                         SUBSTRING([LogMsg], 0, 200) AS [LogMsg] ,
 42                         [LogLevel] ,
 43                         [LogModule]
 44                FROM     [LogSystem].[dbo].[xxxLog] h
 45                WHERE    1 = 1
 46                        {0}
 47              )
 48     SELECT  *
 49     FROM    t
 50     WHERE   IndexNum > @startIndex
 51             AND indexnum < @endIndex", sqlWhere);
 52             sqlParameters.Add(new SqlParameter("@startIndex", model.Start));
 53             sqlParameters.Add(new SqlParameter("@endIndex", model.Start + model.Length));
 54 
 55             DataTable dt = _sqlHelper.ExecuteDataTable(sql.ToString(), sqlParameters.ToArray());
 56             return DataTableTools<LogModel>.DataTableToList(dt);
 57         }
 58 
 59         public int GetxxxLogTotalCount(SM_LogModel model)
 60         {
 61             StringBuilder sql = new StringBuilder(); List<SqlParameter> sqlParameters = new List<SqlParameter>();
 62             sql.Append(@"
 63                     SELECT  COUNT(*)
 64                     FROM    [HdPubLog] h where 1=1 ");
 65             if (!string.IsNullOrWhiteSpace(model.LogStartTime))
 66             {
 67                 sqlParameters.Add(new SqlParameter("@LogStartTime", model.LogStartTime));
 68                 sql.Append(@" AND h.LogDate > @LogStartTime");
 69             }
 70             if (!string.IsNullOrWhiteSpace(model.LogEndTime))
 71             {
 72                 sqlParameters.Add(new SqlParameter("@LogEndTime", model.LogEndTime));
 73                 sql.Append(@" AND h.LogDate < @LogEndTime");
 74             }
 75             if (!string.IsNullOrWhiteSpace(model.LogLevel))
 76             {
 77                 sqlParameters.Add(new SqlParameter("@LogLevel", model.LogLevel));
 78                 sql.Append(@" AND h.LogLevel = @LogLevel");
 79             }
 80             if (!string.IsNullOrWhiteSpace(model.LogModule))
 81             {
 82                 sqlParameters.Add(new SqlParameter("@LogModule", model.LogModule));
 83                 sql.Append(@" AND h.LogModule = @LogModule");
 84             }
 85             return _sqlHelper.ExecuteScalar<int>(sql.ToString(), sqlParameters.ToArray());
 86         }
 87 
 88         [HttpPost]
 89         public LogModel GetxxxxSignelLog(int id)
 90         {
 91             string sql = @"
 92                     SELECT  [Id] ,
 93                             CONVERT(VARCHAR(30), [LogDate], 21) AS [LogDate] ,
 94                             [UserName] ,
 95                             [Description] ,
 96                             [LogMsg] ,
 97                             [LogLevel] ,
 98                             [LogModule] ,
 99                             [Id] IndexNum 
100                     FROM    [LogSystem].[dbo].[xxxxLog] h
101                     WHERE   h.id = @Id";
102             var row = _sqlHelper.ExecuteDataRow(sql, new SqlParameter("@Id", id));
103             return DataTableTools<LogModel>.DataRowToModel(row);
104         }
105     }

四.   寫在最後

     

 不准備給自己搭建一個LogSystem嗎?如果用得上抓緊收藏吧。有疑問歡迎留言。
 如果我的點滴分享對你有點滴幫助,歡迎點擊下方紅色按鈕關註,我將持續輸出乾貨分享。也歡迎為我也為你自己點贊支持。

 

                                                  --保持學習,謹記謙虛。不端不裝,有趣有夢。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近遇到的一個項目中用到了標題所說的方法,用Spring.Net將業務類封閉成WebService供其它地方調用使用,感覺還是蠻新鮮的,於是在園子中搜了一篇園友寫的文章(這裡)自己也嘗試著搭了一個環境,最後是順利跑了起來,但是中間遇到了幾個問題,這裡記錄一下。 具體的搭建方法不再贅述,上面的文章寫的 ...
  • ...
  • 前言 EF通過linq和各種擴展方法,再加上實體模型,編寫資料庫的訪問代碼確實是優美、舒服,但是生成的sql不盡如意、性能低下,尤其是複雜些的邏輯關係,最終大家還是會回歸自然,選擇能夠友好執行sql語句的ORM,認認真真的編寫sql;問題是:EF是否也能夠很友好的執行sql語句?EF提供直接執行sq ...
  • 背水一戰 Windows 10 之 控制項(導航類): Frame ...
  • 在ASP.NET MVC中,Ajax.BeginForm扮演著非同步提交的重要角色。其中就有五個重載方法,但是在實際應用中,你未必使用的得心應手,今天我們就從主要的參數來一探究竟。 一、actionName 用於指定請求地址的Action名稱。 二、controllerName 用於指定請求地址的Co ...
  • 任何一個和數據相關的系統里,數據持久化都是一個不容忽視的問題。 一直以來,Java 平臺出了很多 NB 的 ORM 框架,Hibernate、MyBatis等等。.NET 平臺上,ORM 框架這一塊一直沒有一個能吊到讓幾乎所有開發人員改掉以拼寫 SQL 語句訪問資料庫的習慣。 實際上,在 .NET ...
  • 本文的控制台項目是根據SuperSocket官方Telnet示例代碼進行調試的,官方示例代碼:Telnet示例。 開始我的第一個Telnet控制台項目之旅: 創建控制台項目:打開vs程式,文件=》新建=》項目 一、下載SuperSocket的動態庫,主要有兩種方式獲取SuperSocket的動態庫。 ...
  • 通過IIS直接瀏覽頁面時報錯: HTTP 錯誤 404.3 - Not Found 由於擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載文件,請添加 MIME 映射。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...