统一日志系统 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 }

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 }

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 }

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 }

其实看到这里,最重要的并不是代码了,核心部分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>

这样一来,你的配置就完成了,你可以直接测试插入的情况:

三.   把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吗?如果用得上抓紧收藏吧。有疑问欢迎留言。

时间: 2024-10-06 08:05:33

统一日志系统 Log4Net/ExceptionLess的相关文章

基于Log4Net的日志系统

阅读目录 日志系统应具备的特性 Log4Net 配置文件:log4net.config 初始化 输出信息 对Log4Net的封装 log4net.config复杂配置 不管是Web应用程序还是WinForm应用程序,Visual Studio所带的调试功能都是足够强大,足以应付开发中的各种调试需求.但是,对于已经发布的应用,要记录错误.记载运行中的各种状态信息,就需要依靠日志系统了. 回到顶部 日志系统应具备的特性 一个好的日志系统,应该具备以下的特性: 1.运行稳定.因为日志的作用就是要在系统

log4net.redis+logstash+kibana+elasticsearch+redis 实现日志系统

前端时间写了个随笔 log4net.NoSql +ElasticSearch 实现日志记录 ,因项目原因需要把日志根java平台的同事集成采用logstash+kibana+elasticsearch+redis结构实现日志统计分析,所以需要一个将log4net日志输出到redis的组件.没有找到现成的,就自己动手了.参考了 log4net.NoSql 的代码. redis的C#客户端使用了 ServiceStackRedis,json序列化使用 RestSharp.代码结构如下: JsonLa

.NET 日志系统的搭建:log4net+kafka+elk

前言 公司的程序日志之前都是采用log4net记录文件日志的方式,但是随着后来我们团队越来越大,项目也越来越大,我们的用户量也越来越多. 慢慢系统就暴露了很多问题,这个时候我们的日志系统已经不能满足我们的要求. 其主要有下面几个问题: 随着我们访问量的增加,我们的日志文件急剧增加 多且乱的文件日志,难以让我们对程序进行排错 文件日志的记录耗用我们应用服务器的资源,导致我们的应用服务器的处理用户请求的能力下降 我们的日志分布在多台应用服务器上,当程序遇到问题时,我们的程序员都需要找运维人员要日志,

面试题:应用中很多jar包,比如spring、mybatis、redis等等,各自用的日志系统各异,怎么用slf4j统一输出?

一.问题概述 如题所说,后端应用(非spring boot项目)通常用到了很多jar包,比如spring系列.mybatis.hibernate.各类连接数据库的客户端的jar包.可能这个jar包用的是logback.那个用的是log4j.那个又是log4j2, 这时候,怎么才能保证各jar包的日志都能输出,且能以统一的格式输出呢? 为什么要强调非spring boot项目,可参考第四节. 二.几种日志框架的简单介绍 来源:https://juejin.im/post/5a7c5d5751882

.NET下日志系统的搭建——log4net+kafka+elk

原文:.NET下日志系统的搭建——log4net+kafka+elk .NET下日志系统的搭建——log4net+kafka+elk# 前言# 我们公司的程序日志之前都是采用log4net记录文件日志的方式(有关log4net的简单使用可以看我另一篇博客),但是随着后来我们团队越来越大,项目也越来越大,我们的用户量也越来越多.慢慢系统就暴露了很多问题,这个时候我们的日志系统已经不能满足我们的要求.其主要有下面几个问题: 随着我们访问量的增加,我们的日志文件急剧增加 多且乱的文件日志,难以让我们对

日志收集框架 Exceptionless

日志收集框架 Exceptionless 前言 从去年就答应过Eric(Exceptionless的作者之一),在中国会帮助给 Exceptionless 做推广,但是由于各种原因一直没有做这件事情,在此对Eric表示歉意.:) Exceptionless 简介 Exceptionless 是一个开源的实时的日志收集框架,它可以应用在基于 ASP.NET,ASP.NET Core,Web Api,Web Forms,WPF,Console,MVC 等技术栈的应用程序中,并且提供了Rest接口可以

DNS的视图功能以及日志系统

实验环境:RHEL5.8 32Bit DNS的视图功能以及日志系统详解 DNS的主配置文件中的allow-recursion参数用来定义能够和DNS服务器进行递归的客户端来源,allow-query参数用来定义允许到DNS服务器上面发起查询请求的客户端,allow-transfer参数用来定义允许和DNS服务器进行区域传送的客户端,区域传送主要有两种方式,axfr和ixfr,使用dig命令也可以模拟实现区域传送: 如果我们的DNS服务器允许进行递归.发起查询请求以及进行区域传送的客户端比较多的话

日志系统之基于Zookeeper的分布式协同设计

最近这段时间在设计和实现日志系统,在整个日志系统系统中Zookeeper的作用非常重要--它用于协调各个分布式组件并提供必要的配置信息和元数据.这篇文章主要分享一下Zookeeper的使用场景.这里主要涉及到Zookeeper在日志系统中的使用,但其实它在我们的消息总线和搜索模块中也同样非常重要. 日志元数据 日志的类型和日志的字段这里我们统称为日志的元数据.我们构建日志系统的目的最终主要是为了:日志搜索,日志分析.这两大块我们很大程度上依赖于--ElasticSearch(关于什么是Elast

大型开源日志系统比较

1. 背景介绍 许多公司的平台每天会产生大量的日志(一般为流式数据,如,搜索引擎的pv,查询等),处理这些日志需要特定的日志系统,一般而言,这些系统需要具有以下特征: (1) 构建应用系统和分析系统的桥梁,并将它们之间的关联解耦: (2) 支持近实时的在线分析系统和类似于Hadoop之类的离线分析系统: (3) 具有高可扩展性.即:当数据量增加时,可以通过增加节点进行水平扩展. 本文从设计架构,负载均衡,可扩展性和容错性等方面对比了当今开源的日志系统,包括facebook的scribe,apac