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

需求来源于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. USACO翻译:USACO 2014 FEB SILVER 三题

    USACO 2014 FEB SILVER 一.题目概览 中文题目名称 自动打字 路障 神秘代码 英文题目名称 auto rblock scode 可执行文件名 auto rblock scode 输 ...

  2. Android四大组件--事务详解(转)

    一.什么是事务 事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取.事务的正确执行使得数据库从一种状态转换成另一种状态.   事务必须服从ISO/IEC所制定的ACID原则. ...

  3. $Host.Runspace.ThreadOptions = “ReuseThread”有神马用?

    $Host.Runspace.ThreadOptions = “ReuseThread” 在很多PowerShell的脚本中你都会看到这个语句被用来开头,那它的作用是什么呢? 答:这个设置可以提高对内 ...

  4. 有Maple T.A.自有试题图so easy

    对于想完全控制试题库的用户而言,Maple T.A.是最好的选择.不论您是要利用现有的题库,还是要创建自己的题库,Maple T.A.都可以为您提供功能强大.操作便捷的工具创建数学内容. 1) Ste ...

  5. You are attempting to run the 32-bit installer on a 64-bit version of Window

    您正试图在64位版本的窗口中运行32位安装程序. 系统有32位操作系统和64位操作系统的分别,相同的软件的安装也需要区分操作操作系统的位数. 解决办法:查看自己系统类型,根据类型下载安装相应位数的软件 ...

  6. 体验 ASP.NET Core 1.1 中预编译 MVC Razor 视图

    这是从 ASP.NET Core 1.1 官方发布博文中学到的一招,可以在 dontet publish 时将 Razor 视图编译为 .dll 文件. 需要在 project.json 中添加如下配 ...

  7. 《HiWind企业快速开发框架实战》(1)框架的工作原理

    <HiWind企业快速开发框架实战>(1)框架的工作原理 1.HiWind架构 HiWind的基本架构如下: 持久层部分:同时为框架本身的业务服务,也为开发人员的自定义业务服务. 逻辑层: ...

  8. MySQL 分析服务器状态

    标签:MYSQL/数据库/性能优化/调优 概述 文章简单介绍了通过一些查询命令分析当前服务器的状态. 目录 概述 获取服务器整体的性能状态 SQL操作计数 总结 步骤 获取服务器整体的性能状态 首先对 ...

  9. C#设计模式之外观

    IronMan之外观 接着上篇观察者内容的“剧情”,没看过的朋友也没关系,篇幅之间有衔接的关系但是影响不大. 需求: 为"兵工厂"提供各种支持,生产了各式各样的"Iron ...

  10. 老司机学新平台 - Xamarin Forms开发框架之MvvmCross插件精选

    在前两篇老司机学Xamarin系列中,简单介绍了Xamarin开发环境的搭建以及Prism和MvvmCross这两个开发框架.不同的框架,往往不仅仅使用不同的架构风格,同时社区活跃度不同,各种功能模块 ...