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

需求来源于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. linux命令初识

    一.查看当前的目录文件 ls  demo   查看demo目录下的所有文件 ls  -l  demo/test.txt   --查看指定目录(test.txt)的详细内容 二.复制文件 cp   or ...

  2. canvas绘制坐标轴

    效果图如下, var canvas = document.getElementById("canvas"), context = canvas.getContext("2 ...

  3. SQL Server全时区转换

    SQL Server全时区转换 假如你的应用程序是跨国(例如跨国银行交易)使用的话,那么数据库的一些国际化特性支持可以说是非常重要 其中最常见的就是各国时区上的差异,由于SQL Server getd ...

  4. DevOps是云计算时代的开发与运营

    DevOps(英文Development和Operations的组合)是一组过程.方法与系统的统称,用于促进开发(应用程序/软件工程).技术运营和质量保障(QA)部门之间的沟通.协作与整合.[1] 它 ...

  5. A*寻路算法

    对于初学者而言,A*寻路已经是个比较复杂的算法了,为了便于理解,本文降低了A*算法的难度,规定只能横竖(四方向)寻路,而无法直接走对角线,使得整个算法更好理解. 简而言之,A*寻路就是计算从起点经过该 ...

  6. Alljoyn之管中窥豹

    Alljoyn之管中窥豹 一.历史: Alljoyn是高通2011年推出的近距离P2P通讯技术,它为分布式应用程序在不同设备中提供了运行环境,特别是移动性.安全性和动态配置,支持Microsoft W ...

  7. Android学习第二天-android常用命令

    上一篇文章中,我们重点讲解了adb的常用命令,下面我们一起来看看其它常用的命令 2 android 2.1 查看机器上所有已经安装的Android版本和AVD设备 2.1.1查看机器上已经安装的AVD ...

  8. ASP.NET MVC 5 - 创建连接字符串(Connection String)并使用SQL Server LocalDB

    您创建的MovieDBContext类负责处理连接到数据库,并将Movie对象映射到数据库记录的任务中.你可能会问一个问题,如何指定它将连接到数据库? 实际上,确实没有指定要使用的数据库,Entity ...

  9. JavaScript利用replace更改所有符合条件字符

    利用replace替换字符串时,在正常使用情况下默认只能更改匹配到的第一个字符 var a=new String("fffffddd"); console.log(a.replac ...

  10. MySQL数据库的安装与密码配置

                      MySQL是由MySQL AB公司开发,后由Oracle公司收购     MySQL是一个关系型数据库管理系统       分为社区版和企业版            ...