zoukankan      html  css  js  c++  java
  • 磨刀不误砍柴工——统一日志系统 Log4Net/ExceptionLess

    本文版权归博客园和作者吴双本人共同所有,转载和爬虫必须注明原文地址:www.cnblogs.com/tdws 。

    一.   写在前面

    本文Log4Net介绍了基础的方式,大数据量生产环境不能使用,中等日志量请日志单库。 希望爱技术的你不要错过exceptionless和ELK

    第四节开始简单配置大牛们推荐的了ExceptionLess, 一款开源分布式日志系统。

    日志系统对于任何项目都是必不可少的,无论对于测试阶段的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     }

    话说到这,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吗?如果用得上抓紧收藏吧。有疑问欢迎留言。
     如果我的点滴分享对你有点滴帮助,欢迎点击下方红色按钮关注,我将持续输出干货分享。也欢迎为我也为你自己点赞支持。

      2017.05.21补充  日志系统已升级为Mongo

                                                      --保持学习,谨记谦虚。不端不装,有趣有梦。

  • 相关阅读:
    Visifire正式版(v1.1)发布
    [转]PSP机能强大!已能模拟运行WINDOWS系统?
    在Silverlight+WCF中应用以角色为基础的安全模式(一)基础篇之角色为基础的安全模式简介 Virus
    C#的加密解密算法,包括Silverlight的MD5算法 Virus
    MMORPG programming in Silverlight Tutorial (10)Implement the sprite’s 2D animation (Part IV)
    Game Script: Rescue Bill Gates
    MMORPG programming in Silverlight Tutorial (9)KeyFrame Animation
    MMORPG programming in Silverlight Tutorial (5)Implement the sprite’s 2D animation (Part II)
    MMORPG programming in Silverlight Tutorial (7)Perfect animation
    MMORPG programming in Silverlight Tutorial (3)Animate the object (Part III)
  • 原文地址:https://www.cnblogs.com/tdws/p/6298124.html
Copyright © 2011-2022 走看看