你的日志组件记录够清晰嘛?--自己开发日志组件 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(微服务监控)
背景: 随着公司的项目不断的完善,功能越来越复杂,服务也越来越多(微服务),公司迫切需要对整个系统的每一个程序的运行情况进行监控,并且能够实现对自动记录不同服务间的程序调用的交互日志,以及通一个服务或 ...
随机推荐
- USACO翻译:USACO 2014 DEC Silver三题
USACO 2014 DEC SILVER 一.题目概览 中文题目名称 回程 马拉松 奶牛慢跑 英文题目名称 piggyback marathon cowjog 可执行文件名 piggyback ma ...
- 更加精确的定时器:dispatch_source_t
在使用定时器时,我们经常使用NSTimer,但是由于NSTimer会受RunLoop影响,当RunLoop处理的任务很多时,就会导致NSTimer的精度降低,所以在一些对定时器精度要求很高的情况下,应 ...
- 封装自己的DB类(PHP)
封装一个DB类,用来专门操作数据库,以后凡是对数据库的操作,都由DB类的对象来实现.这样有了自己的DB类,写项目时简单的sql语句就不用每次写了,直接调用就行,很方便! 1.封装一个DB类.一个类文件 ...
- 4.Powershell交互界面
Powershell提供两种接口:交互式和自动化脚本 先学下如何与Powershell Console和平共处,通过Powershell Console和机器学会对话. 通过以上一个简单测试,知道Po ...
- 嵌入式的重要平台 .NET Micro Framework
曾经辉煌的巨人PC界渐渐走向下坡路,而智能手机圈则没完没了般地争个你死我活.随着智能手机的广泛普及,不少商家为了不坐以待毙而纷纷开始涉足与穿戴式设备--智能手表(具体参见智能手表时代还有多远). 我们 ...
- MVC学习二:基础语法
目录 一:重载方法的调用 二:数据的传递 三:生成控件 四:显示加载视图 五:强类型视图 六:@Response.Write() 和 @Html.Raw()区别 七:视图中字符串的输入 八:模板页 一 ...
- [.net 面向对象程序设计进阶] (1) 开篇
[.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对 ...
- 将word文档A表格中的内容拷贝到word文档B表格中
Function IsFileExists(ByVal strFileName As String) As Boolean ) <> Empty Then IsFileExists = T ...
- ReactNative与NativeScript对比报告
综合这段时间对ReactNative(下称RN)和NativeScript(下称NS)的简单学习了解,分别从不同方面分析对比二者的优缺点. 页面结构 NS一个页面的目录结构: RN的一个页面一般就是一 ...
- Module Zero之Nuget包
返回<Module Zero学习目录> ABP module-zero已经发布在了nuget上了.这里是所有的包列表. Abp.Zero module zero的核心包. Abp.Zero ...