子曰:“工欲善其事,必先利其器
github地址:https://github.com/redAntCpp/CSharpTools
简介
日志,通常用于记录软件的运行情况。在问题排查中占用极其重要的作用。在c#的控制台程序中我们可以用控制台输出做到相同的效果。当然网上也有专门用于记录日志的工具,如log4net等。
通常日志会将日志内容输出到相应的文本中,以便开发者在不需要打开代码的情况下,查询到系统内部问题。因此,自己定义一个日志帮助类还是有必要的。
日志等级
通常我们会将日志划分为不同的等级,代表日志的详细情况,如:信息、追踪、调试、错误等。划分等级是为了在不同情形下输出不一样的日志。在程序开发中,我们通常需要信息等级输出某些变量的具体值,用来判断是否取错了某些值,用追踪等级查看程序在业务上走到了哪一步,使用调试看程序执行的步骤,使用错误来输出程序执行的异常。当程序测试完毕,上线运行的时候,一些日志的详细情况便可以忽略,只需要输出错误日志,这样可以减少日志占用的空间,同时提高运行效率。待需要排查时候,再将日志等级调高,便于查因,可以参考Config类,配置日志等级。
日志格式
格式通常为 时间 + 进程id +方法名/类名 + 要输出的文本内容。
此类文件一般放在与项目同等级的目录下,并且命名为Logs,文件后缀为log。
当然日志文件存放的位置通常也可以配置的,通常以文件夹的形式体现。通常存在根目录,也可以在系统中让用户选择日志存放的地方。如下图,navicat就让我们自己选择日志的存放地址:
日志等级
日志虽可以跟踪系统的运行情况,但是如果日志打的过于详细,会导致日志文件过大占用过多的内存(尤其是在循环中记录日志)。处理定期清理日志,还应该做好日志的等级分类。通常我们分为以下几个等级:
- 错误(error):即程序运行出错时记录错误信息。通常为程序逻辑错误或者catch到的异常。
- 信息(infor):即程序运行时产生的一些信息。
- 追踪(track):顾名思义,跟踪程序的详细运行情况,通常用于记录程序进入了哪个函数,执行了哪些方法,产生了哪些数据。
- 调试(debug):调试,常用于记录系统的运行路径。
日志等级可以根据实际需求进行调整。通常稳定的程序运行不需要具体的日志,但当系统出问题时,需要详细的日志提供信息时,就可以通过提高日志的等级来达到此目的。
一份简单版的日志示例如下:
日志系统基本原理
基本原理为:在某个指定的路径,创建一个文件夹(通常以LOG命名),在此路径中新建一个文本,用于存放日志内容(通常后缀为.log)。当程序调用日志方法的时候,往文本追加日志内容。
关键代码
主代码:
private static void AddTextToFile(string ProcedureName, string Text, LoggerType LogType)
{
if (!Directory.Exists(LogPath))
{
Directory.CreateDirectory(LogPath); //目录不存在就动态创建
//日志文本的路径跟名称
}
FLogFileName = LogPath + "\\" + LogName + DateTime.Now.ToString("yyyy-MM-dd") + ".log";
FLock = new Mutex();//新建一个互斥变量
int ProcessID;//无符号整型
string fText;
string fLocFileName;
string fSource;
try
{
ProcessID = Process.GetCurrentProcess().Id;
fSource = ProcedureName;
fLocFileName = FLogFileName;
//枚举类转int,可加int直接转
//下面的排列决定格式化输出的日志顺序
fText = string.Format(sAppLogFormat, System.DateTime.Now, ProcessID, STR_EVENT_TYPE[(int)LogType], fSource + " ", Text);
try
{
FLock.WaitOne();//进入临界区域
File.AppendAllText(fLocFileName, fText + "\r\n"); //追加文本,并换行,如果文本不存在则创建
}
finally
{
FLock.ReleaseMutex();//离开临界区
}
}
catch (Exception err)
{
File.AppendAllText(FLogFileName, "错误:写入日志时发生异常:" + err.Message + "\r\n");//可在日志中查看抛出的异常
throw;//抛出异常
}
}
而日志等级通常用来限制日志的输出,如下:
//按日志等级输出
public static void AddTrack(string ProcedureName, string content)
{
if (Level >= 4)
{
AddLog(content, ProcedureName, LoggerType.logTrack);
}
}
同时配置日志存放的路径以及文本名称:
//配置默认文件路径以及默认等级,这个是个字符串,可以从配置中读取,也可以自己写
static string LogPath = HOTConfig.GetConfig().GetLogPath() + "\\" + "Logs";
static string LogName = HOTConfig.GetConfig().GetLogName();
static int Level = HOTConfig.GetConfig().GetLogLevel();
在代码中可以直接通过命名空间引用,做到项目全局可以引用:
参考Config的代码:
private JObject ConfigJson;
public Config()
{
try
{
//读取json,这个是目前在服务器上配置的,用的相对路径,也可以是绝对路径
string HOTConfigPath = HostingEnvironment.MapPath(@"/HOTApiConfig.Json");
Log.AddTrack("读取配置文件的路径:HOTConfigPath:", HOTConfigPath);
using (StreamReader file = System.IO.File.OpenText(HOTConfigPath))
{
using (JsonTextReader reader = new JsonTextReader(file))
{
JObject o = (JObject)JToken.ReadFrom(reader);
Log.AddTrack("读取配置文件:HOTConfig:", o.ToString());
ConfigJson = o;
}
}
}
catch(Exception e)
{
throw e;
}
}