統一日誌系統 Log4Net/ExceptionLess

一.   寫在前面

本文Log4Net介紹了基礎的方式,大數據量生產環境不能使用,中等日誌量請日誌單庫。 但願愛技術的你不要錯過exceptionless和ELKhtml

第四節開始簡單配置大牛們推薦的了ExceptionLess, 一款開源分佈式日誌系統。git

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

個人LogSystem使用Log4net入庫的方式,網上特別多的分享,可是能完整運行下來的真是不多,因此如今須要和之後用得上的小夥伴抓緊收藏咯。web

 

二.  Log4Net自定義內容入庫

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

LogSystem數據庫結構,個人建議是一個項目一個表。數據庫

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

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,定義了日誌級別,和寫入方法less

 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 }
複製代碼

話說到這,Log4Net數據庫日誌系統已經完成。

四.  更好的方式—— ExceptionLess本地部署

 仍是先上個本地部署圖:

部署的過程當中,參考了官方文檔和一位園友的文章。

http://www.cnblogs.com/savorboard/p/exceptionless.html

http://www.cnblogs.com/uptothesky/p/5864863.html

https://github.com/exceptionless/Exceptionless/wiki/Self-Hosting

實際上參照着參考文檔的Production配置文檔,把Java環境配置好,而後裝好ES服務並啓動. 你的self hosting基本都不會有問題。

五.   寫在最後

     

 不許備給本身搭建一個LogSystem嗎?若是用得上抓緊收藏吧。有疑問歡迎留言。
相關文章
相關標籤/搜索