你的日志组件记录够清晰嘛?--自己开发日志组件 Logger
现在现成的日志组件实在是太多太多,为什么我还需要自己实现呢?????
需求来源于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的更多相关文章
- SQL Server 数据库开启日志CDC记录,导致SQL Server 数据库日志异常增大
这几天单位的SQL Server业务数据生产库出现数据库日志增长迅速,导致最终数据无法写入数据库,业务系统提示"数据库事务日志已满",经过多方咨询和请教,终于将日志异常的数据库处理 ...
- 【干货】.NET开发通用组件发布(四) 日志记录组件
组件介绍和合作开发 http://www.cnblogs.com/MrHuo/p/MrHuoControls.html 日志记录组件功能介绍 通过基类Logger,实现了文本记录日志和数据库记录日志两 ...
- 点滴积累【C#】---使用log4net组件记录错误日志(以文本形式记录)
效果: 描述: 利用log4net组件进行错误日志的记录,log4net记录错误的方式我所了解的有4种,No.1 文本形式记录日志,No.2存储到数据库形式记录日志,No.3控制台控制显示日志,No. ...
- 大叔也说Xamarin~Android篇~日志的记录
回到目录 无论哪个平台,开始哪种应用程序,日志总是少不了的,大家在Lind.DDD里也可以看到大叔的日志组件,而在xamarin进行移动开发时,为了更好的调试,记录运行的情况,日志也是必须的,这讲主要 ...
- ASP.NET Core 实战:使用 NLog 将日志信息记录到 MongoDB
一.前言 在项目开发中,日志系统是系统的一个重要组成模块,通过在程序中记录运行日志.错误日志,可以让我们对于系统的运行情况做到很好的掌控.同时,收集日志不仅仅可以用于诊断排查错误,由于日志同样也是大量 ...
- python记录_day019 类的约束 异常处理 日志
一 .约束 python中约束有两种 第一种,通过抛异常进行约束,这种是子类不按我要求的来,我就给你抛异常(推荐) 操作:提取一个父类. 在父类中给出一个方法.但在方法中不给出任何代码,直接抛异常 # ...
- NET Core 实战:使用 NLog 将日志信息记录到 MongoDB
NET Core 实战:使用 NLog 将日志信息记录到 MongoDB https://www.cnblogs.com/danvic712/p/10226557.html ASP.NET Core ...
- 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 重点: 实现多级子目录的压缩, ...
- 基于log4net的日志组件扩展封装,实现自动记录交互日志 XYH.Log4Net.Extend(微服务监控)
背景: 随着公司的项目不断的完善,功能越来越复杂,服务也越来越多(微服务),公司迫切需要对整个系统的每一个程序的运行情况进行监控,并且能够实现对自动记录不同服务间的程序调用的交互日志,以及通一个服务或 ...
随机推荐
- 多线程中使用CheckForIllegalCrossThreadCalls = false访问窗口-转
在多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,如果需要访问窗口中的控件,可以在窗口构造函数中将CheckForIllegalCrossThreadCalls设置为 false publi ...
- .Net 跨平台可移植类库PCL可用于任何平台包括Mono
Microsoft 在 .NET Framework 4 中添加了一个名为可移植类库 (PCL) 的新功能. 利用 PCL,您可以有选择性地面向 .NET Framework.Silverlight ...
- ViewBag 找不到编译动态表达式所需的一种或多种类型,是否缺少引用?
症状: 类似上面的警告提示,运行程序不会有任何错误,但若干地方都提示警告,并且明明dll的引用都是正确的. 解决方案: 删除:C:\Users\{your computer name}\AppData ...
- 浅谈Excel开发:十一 针对64位Excel的插件的开发和部署
自Office 2010版本开始有了32位和64位之分,对Excel来说,32位的Excel和64位的Excel在性能上的主要区别是64位的Excel能够处理2G及2G以上的大数据集. 随着64位操作 ...
- Mac下配置Apache服务
这篇文章主要是针对Mac用户,第一次搭建本地开发环境的同学,已经搭建过的同学可以忽略. Mac自带的Apache还是XAMPP? That is a question. 其实自带的apache也够用了 ...
- RAID 概述
原创地址:http://www.cnblogs.com/jfzhu/p/3999283.html 转载请注明出处 独立硬盘冗余阵列(RAID, Redundant Array of Indep ...
- Christmas Trees, Promises和Event Emitters
今天有同事问我下面这段代码是什么意思: var MyClass = function() { events.EventEmitter.call(this); // 这行是什么意思? }; util.i ...
- LinuxThreads 和 NPTL
http://www.ibm.com/developerworks/cn/linux/l-threading.html Linux 线程模型的比较:LinuxThreads 和 NPTL 进行移植的开 ...
- hibernate学习笔记之三 持久化的三种状态
Hibernate持久化对象有3中状态,瞬时对象(transientObjects),持久化对象(persistentObjects),离线对象(detachedObjects) 下图显示持久化三种状 ...
- 我所理解的Cocos2d-x
我所理解的Cocos2d-x(完全基于Cocos2d-x3.0,深度剖析计算机图形学,OpenGL ES及游戏引擎架构,全面提升游戏开发相关知识) 秦春林 著 ISBN 978-7-121-246 ...