现在现成的日志组件实在是太多太多,为什么我还需要自己实现呢?????

需求来源于java的log4j,

[07-31 16:40:00:557:WARN : com.game.engine.thread.ServerThread:117] -> 全局排行榜同步执行器-->ServerThread[全局排行榜同步执行器]执行 执行时间过长:23

简单的一句日志信息,但是我却可以很清晰的定位日志输出的代码位置;com.game.engine.thread包下面的ServerThread这个类文件的第117行;

而log4net却不行,

也许是因为我米有找到正确的对应配置文件、可惜吧~!

于是我打算自己重组日志组件来实现清洗的日志记录。当然你也可以说,.net平台下面的exception抛错的话日志也很清晰。

但是有时候我们逻辑错误或者是参数错误,根本不会抛错,这种情况下我们没有得到预期结果的时候只能通过简单调试找出原因。

那么很明显费时,费力,所以我就想得到像log4j那样打印出清晰的日志。

可能你会问有这个必要嘛?很有必要哦,比如你程序上线一个版本,然后打印的信息和现在最新版本行号已经不符合了,数据流向和清晰度自然而然就不存在多大的意义~!

如果需要找到这样清晰的日志。那么就需要得到方法的调用堆栈信息。

查阅文档发现 System.Diagnostics.StackTrace 类是记录堆栈信息的

于是开始无尽的测试之行

 namespace Sz.StackTraceTest
 {
     class Program
     {
         static void Main(string[] args)
         {
             Test();
             Console.ReadLine();
         }

         static public void Test()
         {
             ShowStackTrace();
         }

         static public void ShowStackTrace()
         {

             StackTrace trace = new StackTrace();
             var frames = trace.GetFrames();
             foreach (var item in frames)
             {
                 Console.WriteLine(string.Format("[{0},文件{1},方法{2},行{3}]", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), item.GetFileName(), item.GetMethod(), item.GetFileLineNumber()));
             }
         }

     }
 }

运行后发现:

并未得到文件等信息,为啥会这样

        // 摘要:
        //     用调用方的帧初始化 System.Diagnostics.StackTrace 类的新实例。
        public StackTrace();
        //
        // 摘要:
        //     用调用方的帧初始化 System.Diagnostics.StackTrace 类的新实例,可以选择捕获源信息。
        //
        // 参数:
        //   fNeedFileInfo:
        //     如果为 true,则捕获文件名、行号和列号;否则为 false。
        public StackTrace(bool fNeedFileInfo);

查询了一下StackTrace类的重载得知,应该是默认构造函数并未捕获信息

那么重来一次

这次取到了。可是意外的发现,记录的文件居然 是全路径。,

显然这不是我想要的结果。

那么再试试看有么有其他路径。

于是想到,既然有函数,那么可以通过函数入手啊,函数,肯定有父节啊。

        //
        // 摘要:
        //     获取声明该成员的类。
        //
        // 返回结果:
        //     声明该成员的类的 Type 对象。
        public abstract Type DeclaringType { get; }

于是找到这个属性,声明该函数的对象。那么肯定会得到类型的完全限定名称了;

 namespace Sz.StackTraceTest
 {
     class Program
     {
         static void Main(string[] args)
         {
             Test();
             Console.ReadLine();
         }

         static public void Test()
         {
             ShowStackTrace();
         }

         static public void ShowStackTrace()
         {

             StackTrace trace = new StackTrace(true);
             var frames = trace.GetFrames();
             foreach (var item in frames)
             {
                 Console.WriteLine(string.Format("[{0},文件{1},方法{2},行{3}]", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), item.GetMethod().DeclaringType.FullName, item.GetMethod(), item.GetFileLineNumber()));
             }
         }

     }
 }

看看那运行结果

得到命名空间和类型名了。

但是图中我们看到,其实我只想Test()中输出打印的地方加入一个而已,

但是打印出了整个走向,这个时候我们是普通打印日志根本不需要整个的流程走向

再次查看StackTrace类还有一个重载方式

         //
         // 摘要:
         //     从调用方的帧初始化 System.Diagnostics.StackTrace 类的新实例,跳过指定的帧数并可以选择捕获源信息。
         //
         // 参数:
         //   skipFrames:
         //     堆栈中的帧数,将从其上开始跟踪。
         //
         //   fNeedFileInfo:
         //     如果为 true,则捕获文件名、行号和列号;否则为 false。
         //
         // 异常:
         //   System.ArgumentOutOfRangeException:
         //     skipFrames 参数为负数。
         public StackTrace(int skipFrames, bool fNeedFileInfo);

跳过指定帧;

 namespace Sz.StackTraceTest
 {
     class Program
     {
         static void Main(string[] args)
         {
             Test();
             Console.ReadLine();
         }

         static public void Test()
         {
             Log("test");
             Log("test");
             Log("test");
         }

         static public void Log(string msg)
         {
             StackTrace trace = , true);
             )
             {
                 );
                 Console.WriteLine(string.Format("[{0},文件{1},方法{2},行{3}]:{4}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), item.GetMethod().DeclaringType.FullName, item.GetMethod(), item.GetFileLineNumber(), msg));
             }
             else
             {
                 Console.WriteLine(string.Format("[{0},文件{1},方法{2},行{3}]:{4}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), null, null, null, msg));
             }
             //var frames = trace.GetFrames();

             //foreach (var item in frames)
             //{
             //    Console.WriteLine(string.Format("[{0},文件{1},方法{2},行{3}]", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), item.GetMethod().DeclaringType.FullName, item.GetMethod(), item.GetFileLineNumber()));
             //}
         }
     }
 }

再来看看

ok已完全符合我的需求了。是不是呢???这样检查逻辑错误的时候方便许多了。

附上我的全部日志组件源码

组件实现了简单的配置;

由于日志打印控制还是输出文本文件都是比较耗时的事情;所以加入线程模型

 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading;

 /**
  *
  * @author 失足程序员
  * @Blog http://www.cnblogs.com/ty408/
  * @mail 492794628@qq.com
  * @phone 13882122019
  *
  */
 namespace Sz
 {
     /// <summary>
     /// 日志辅助
     /// <para>AppSettings 设置 LogRootPath 为日志的根目录</para>
     /// </summary>
     public class Logger
     {
         static ThreadModel logConsoleThread = new ThreadModel("Console Log Thread");
         static ThreadModel logFileThread = new ThreadModel("File Log Thread");
         static string logInfoPath = "log/info/";
         static string logErrorPath = "log/error/";

         /// <summary>
         /// 设置日志的输出根目录
         /// </summary>
         /// <param name="path"></param>
         static public void SetLogRootPath(string path)
         {

             logInfoPath = path + logInfoPath;
             logErrorPath = path + logErrorPath;
             if (!Directory.Exists(logInfoPath)) { Directory.CreateDirectory(logInfoPath); }
             if (!Directory.Exists(logErrorPath)) { Directory.CreateDirectory(logErrorPath); }

         }

         static Logger()
         {

             if (System.Configuration.ConfigurationManager.AppSettings.AllKeys.Contains("LogRootPath"))
             {
                 string logPath = System.Configuration.ConfigurationManager.AppSettings["LogRootPath"].ToString();
                 if (!(logPath.EndsWith("\\") || logPath.EndsWith("/")))
                 {
                     logPath = "\\";
                 }
                 logInfoPath = logPath + logInfoPath;
                 logErrorPath = logPath + logErrorPath;
             }
             if (!Directory.Exists(logInfoPath)) { Directory.CreateDirectory(logInfoPath); }
             if (!Directory.Exists(logErrorPath)) { Directory.CreateDirectory(logErrorPath); }

         }

         #region 日子写入文件辅助任务 class LogTaskFile : TaskBase
         /// <summary>
         /// 日子写入文件辅助任务
         /// </summary>
         class LogTaskFile : TaskBase
         {
             string msg, mathed;
             Exception exce;
             StackTrace trace;
             static readonly StringBuilder sb = new StringBuilder();
             public LogTaskFile(StackTrace trace, string mathed, string msg, Exception exce)
                 : base("File Log Task")
             {
                 this.mathed = mathed;
                 this.trace = trace;
                 this.msg = msg;
                 this.exce = exce;
             }

             public override void TaskRun()
             {
                 );
                 DateTime dnow = DateTime.Now;
                 sb.Clear();
                 sb.Append("[")
                     .Append(dnow.NowString())
                     .Append(mathed.PadRight())
                     .Append(":")
                     .Append(frame.GetMethod().DeclaringType.FullName)
                     .Append(", ")
                     .Append(frame.GetMethod())
                     .Append(", ")
                     .Append(frame.GetFileLineNumber())
                     .Append("] ").Append(msg);

                 if (exce != null)
                 {
                     sb.AppendLine("")
                     .AppendLine("----------------------Exception--------------------------")
                     .Append(exce.GetType().FullName).Append(": ").AppendLine(exce.Message)
                     .AppendLine(exce.StackTrace)
                     .AppendLine("----------------------Exception--------------------------");
                 }
                 string logPath = string.Format("{0}info_{1}.log", logInfoPath, dnow.ToString("yyyyMMdd"));

                 System.IO.File.AppendAllText(logPath, sb.ToString(), UTF8Encoding.Default);
                 System.IO.File.AppendAllText(logPath, "\r\n", UTF8Encoding.Default);

                 logPath = string.Format("{0}error_{1}.log", logErrorPath, dnow.ToString("yyyyMMdd"));
                 System.IO.File.AppendAllText(logPath, sb.ToString(), UTF8Encoding.Default);
                 System.IO.File.AppendAllText(logPath, "\r\n", UTF8Encoding.Default);

             }
         }
         #endregion

         #region 日志写入控制台输出 class LogTaskConsole : TaskBase
         /// <summary>
         /// 日志写入控制台输出
         /// </summary>
         class LogTaskConsole : TaskBase
         {
             string msg, mathed;
             Exception exce;
             StackTrace trace;
             static readonly StringBuilder sb = new StringBuilder();

             public LogTaskConsole(StackTrace trace, string mathed, string msg, Exception exce)
                 : base("Console Log Task")
             {
                 this.mathed = mathed;
                 this.trace = trace;
                 this.msg = msg;
                 this.exce = exce;
             }

             public override void TaskRun()
             {
                 sb.Clear();
                 );
                 sb.Append("[")
                     .Append(DateTime.Now.NowString())
                     .Append(mathed.PadRight())
                     .Append(":")
                     .Append(frame.GetMethod().DeclaringType.FullName)
                     //.Append(", ")
                     //.Append(frame.GetMethod())
                     .Append(", ")
                     .Append(frame.GetFileLineNumber())
                     .Append("] ").Append(msg);

                 if (exce != null)
                 {
                     sb.AppendLine("")
                     .AppendLine("----------------------Exception--------------------------")
                     .Append(exce.GetType().FullName).Append(": ").AppendLine(exce.Message)
                     .AppendLine(exce.StackTrace)
                     .AppendLine("----------------------Exception--------------------------");
                 }
                 Console.WriteLine(sb.ToString());
             }
         }
         #endregion

         string name;
         /// <summary>
         ///
         /// </summary>
         /// <param name="name"></param>
         public Logger(string name)
         {
             this.name = name;
         }

         /// <summary>
         /// 输出到控制台
         /// </summary>
         /// <param name="msg"></param>
         static public void Debug(string msg)
         {
             StackTrace trace = , true);
             LogTaskConsole logConsole = new LogTaskConsole(trace, "Debug", msg, null);
             logConsoleThread.AddTask(logConsole);
         }

         /// <summary>
         /// 控制台和文本文件
         /// </summary>
         /// <param name="msg"></param>
         static public void Info(string msg)
         {
             AddLog("Info", msg, null);
         }
         /// <summary>
         /// 控制台和文本文件
         /// </summary>
         static public void Error(string msg)
         {
             AddLog("Error", msg, null);
         }
         /// <summary>
         /// 控制台和文本文件
         /// </summary>
         static public void Error(string msg, Exception exception)
         {
             AddLog("Error", msg, exception);
         }

         static void AddLog(string mathed, string msg, Exception exception)
         {
             StackTrace trace = , true);
             LogTaskConsole logConsole = new LogTaskConsole(trace, mathed, msg, exception);
             logConsoleThread.AddTask(logConsole);
             LogTaskFile logFile = new LogTaskFile(trace, mathed, msg, exception);
             logFileThread.AddTask(logFile);
         }
     }
 }

线程模型的任务类型

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;

 /**
  *
  * @author 失足程序员
  * @Blog http://www.cnblogs.com/ty408/
  * @mail 492794628@qq.com
  * @phone 13882122019
  *
  */
 namespace Sz
 {
     internal abstract class TaskBase
     {

         public string Name { get; private set; }

         public TaskBase(string name)
         {
             this.Name = name;
             TempAttribute = new ObjectAttribute();
         }
         public ObjectAttribute TempAttribute { get; set; }

         public abstract void TaskRun();
     }
 }

线程模型

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;

 /**
  *
  * @author 失足程序员
  * @Blog http://www.cnblogs.com/ty408/
  * @mail 492794628@qq.com
  * @phone 13882122019
  *
  */
 namespace Sz
 {
     /// <summary>
     /// 线程模型
     /// </summary>
     internal class ThreadModel
     {
         public bool IsStop = false;
         /// <summary>
         /// ID
         /// </summary>
         public int ID;

         ;

         public ThreadModel(string name)
         {
             lock (typeof(ThreadModel))
             {
                 StaticID++;
             }
             ID = StaticID;
             System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(Run));
             thread.Name = name;
             thread.IsBackground = true;
             thread.Start();
         }

         /// <summary>
         /// 任务队列
         /// </summary>
         protected System.Collections.Concurrent.ConcurrentQueue<TaskBase> taskQueue = new System.Collections.Concurrent.ConcurrentQueue<TaskBase>();

         /// <summary>
         /// 加入任务
         /// </summary>
         /// <param name="t"></param>
         public virtual void AddTask(TaskBase t)
         {
             taskQueue.Enqueue(t);
             //防止线程正在阻塞时添加进入了新任务
             are.Set();
         }

         //通知一个或多个正在等待的线程已发生事件
         protected ManualResetEvent are = new ManualResetEvent(false);

         protected virtual void Run()
         {
             while (true)
             {
                 while (!taskQueue.IsEmpty)
                 {
                     TaskBase t = null;
                     if (!taskQueue.IsEmpty && taskQueue.TryDequeue(out t))
                     {
                         try
                         {
                             t.TaskRun();//执行任务
                             t = null;
                         }
                         catch (Exception ex)
                         {
                             Logger.Error("Thread:<" + Thread.CurrentThread.Name + "> TaskRun <" + t.Name + ">", ex);
                         }
                     }
                 }
                 are.Reset();
                 //队列为空等待200毫秒继续
                 are.WaitOne();
             }
         }
     }
 }

到此为止,日志组件完成。如果有需要的或者原因的可以自己加入,数据库,和mail处理的

你的日志组件记录够清晰嘛?--自己开发日志组件 Logger的更多相关文章

  1. SQL Server 数据库开启日志CDC记录,导致SQL Server 数据库日志异常增大

    这几天单位的SQL Server业务数据生产库出现数据库日志增长迅速,导致最终数据无法写入数据库,业务系统提示"数据库事务日志已满",经过多方咨询和请教,终于将日志异常的数据库处理 ...

  2. 【干货】.NET开发通用组件发布(四) 日志记录组件

    组件介绍和合作开发 http://www.cnblogs.com/MrHuo/p/MrHuoControls.html 日志记录组件功能介绍 通过基类Logger,实现了文本记录日志和数据库记录日志两 ...

  3. 点滴积累【C#】---使用log4net组件记录错误日志(以文本形式记录)

    效果: 描述: 利用log4net组件进行错误日志的记录,log4net记录错误的方式我所了解的有4种,No.1 文本形式记录日志,No.2存储到数据库形式记录日志,No.3控制台控制显示日志,No. ...

  4. 大叔也说Xamarin~Android篇~日志的记录

    回到目录 无论哪个平台,开始哪种应用程序,日志总是少不了的,大家在Lind.DDD里也可以看到大叔的日志组件,而在xamarin进行移动开发时,为了更好的调试,记录运行的情况,日志也是必须的,这讲主要 ...

  5. ASP.NET Core 实战:使用 NLog 将日志信息记录到 MongoDB

    一.前言 在项目开发中,日志系统是系统的一个重要组成模块,通过在程序中记录运行日志.错误日志,可以让我们对于系统的运行情况做到很好的掌控.同时,收集日志不仅仅可以用于诊断排查错误,由于日志同样也是大量 ...

  6. python记录_day019 类的约束 异常处理 日志

    一 .约束 python中约束有两种 第一种,通过抛异常进行约束,这种是子类不按我要求的来,我就给你抛异常(推荐) 操作:提取一个父类. 在父类中给出一个方法.但在方法中不给出任何代码,直接抛异常 # ...

  7. NET Core 实战:使用 NLog 将日志信息记录到 MongoDB

    NET Core 实战:使用 NLog 将日志信息记录到 MongoDB https://www.cnblogs.com/danvic712/p/10226557.html ASP.NET Core ...

  8. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  9. 基于log4net的日志组件扩展封装,实现自动记录交互日志 XYH.Log4Net.Extend(微服务监控)

    背景: 随着公司的项目不断的完善,功能越来越复杂,服务也越来越多(微服务),公司迫切需要对整个系统的每一个程序的运行情况进行监控,并且能够实现对自动记录不同服务间的程序调用的交互日志,以及通一个服务或 ...

随机推荐

  1. android——fargment基础

    1.Fragment的产生与介绍 Android运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视.针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布局以适应 ...

  2. TestNG 与 Junit的比较

    转自 http://www.blogjava.net/fanscial/archive/2005/12/14/23780.html 1.         JDK 5 Annotations (JDK ...

  3. wamp环境 安装memcache 扩展

    这两天在研究tp的memcached缓存 总是遇到坑 在网上找了很多教程看终于弄出来了现在拿出来分享 首先安装memcached下载memcache压缩包 使用cmd以管理员命令去安装 E:\wamp ...

  4. 一图搞定【实战Java高并发程序设计】

    来了解下java并发的技术点吧.这里面包括了并发级别.算法.定律,还有开发包.在过去单核CPU时代,单任务在一个时间点只能执行单一程序,随着多核CPU的发展,并行程序开发就显得尤为重要.这本书主要介绍 ...

  5. dagger2 备注

    dagger 2是android下的IOC框架,类似java服务端的spring,但功能上远没有其强大.个人理解不管是APP还是服务端依赖注入的本质都是一样的,用于解耦某个服务的定义和实现.我自己给出 ...

  6. iOS开发系列--C语言之预处理

    概述 大家都知道一个C程序的运行包括编译和链接两个阶段,其实在编译之前预处理器首先要进行预处理操作,将处理完产生的一个新的源文件进行编译.由于预处理指令是在编译之前就进行了,因此很多时候它要比在程序运 ...

  7. 图片拾取器-PicPicker

    最近报名参加了360前端星计划,想当一名前端实习生,学习更多更流行的前端知识.然后需要完成一个作业,才能进培训,进了培训还得看运气才能留下,流程不少.书归正传,请看: 课后作业题目 请从下面两个题目中 ...

  8. 《CLR.via.C#第三版》第二部分第8,9章节读书笔记(四)

    三种类型的构造方法: 实例构造器(引用类型):实例构造器永远不能被继承(所以方法前没有修饰符):如果类的修饰符为static(sealed和abstract),编译器根本不会在类的定义中生成一个默认构 ...

  9. MySQL 权限

    create user创建用户 CREATE USER li@localhost IDENTIFIED BY 'li'; 授予用户li数据库person的所有权限,并允许用户li将数据库person的 ...

  10. PMO是什么?如何与其他部门协作配合提高项目成功率?

    许多公司里,有许多IT项目,特别是在软件公司里,许多开发团队并没有运用灵敏开发来进行项目办理.在某些状况下,尤其在一些公司里IT不是很受注重的,只能作为一个事务支撑部分,灵敏团队面对的首要疑问,是缺少 ...