zoukankan      html  css  js  c++  java
  • 统一日志系统 Log4Net/ExceptionLess

    一.   写在前面

    本文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吗?如果用得上抓紧收藏吧。有疑问欢迎留言。
  • 相关阅读:
    redis的数据类型 (一) 字符串
    php 与redis 结合 使用predis
    jquery.datetimepicker.js 当鼠标离开时,不选中当前时间,以达到清空的目的
    nginx 配置 查阅资料
    座机号码的正则表达式
    mysql 局域网同事之间直接用客户端访问
    《CSS揭秘》之毛玻璃效果知识点
    《JavaScript 设计模式与开发实战》第一部分(1、2、3章)笔记
    《JavaScript_DOM编程艺术第二版(中文)》整书笔记
    Node调用C++(dll)
  • 原文地址:https://www.cnblogs.com/MuNet/p/6688403.html
Copyright © 2011-2022 走看看