没有日志,问题单难收敛,开发兄弟天天擦屁股还擦不干净...
超短期把这套东西补全委实困难,此处基于log4net,简单封装了日志框架。一切本着易用原则。
log4net系apache 2.0协议,无开源风险。
【演示】
程序运行目录下生成log.log文件及其备份
日志效果图
【使用步骤】
1 把log4net.dll加入到程序目录下,并引用它
2.把日志配置文件放到程序运行目录下
主要配置——文件按大小滚动备份
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="./Log/log.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="20" />
<maximumFileSize value="10M" />
<staticLogFileName value="true" />
<!--<datePattern value="yyyy-MM-dd".log""/> -->
<layout type="log4net.Layout.PatternLayout">
<!--<footer value="" />-->
<!--输出格式-->
<!--%date [%t] %-5p [%c] - %m%n-->
<!--样例:2008-03-26 13:42:32,111 [10] INFO Log4NetDemo.MainClass [(null)] - info-->
<conversionPattern value="%date [%t] %-5p - %m%n" />
</layout>
</appender>
3. 封装一个日志单态类,封装了log4net基本输出日志方法,对外暴露日志输出函数DebugLog,FuncEntryLog(用以标记函数入口)等
amespace agri.logger
{
public class Log
{
#region 私有内容
private static log4net.ILog _logger = null;
private static object _locker = new object();
static Log()
{
string path = string.Format("{0}log.config", AppDomain.CurrentDomain.BaseDirectory);
if (File.Exists(path))
{
log4net.Config.XmlConfigurator.Configure(new FileInfo(path));
}
else
{
RollingFileAppender appender = new RollingFileAppender();
appender.Name = "LogFileAppender";
appender.File = "./Log/log.log";
appender.AppendToFile = true;
appender.RollingStyle = RollingFileAppender.RollingMode.Size;
appender.MaximumFileSize = "10M";
appender.MaxSizeRollBackups = 20;
log4net.Layout.PatternLayout layout =
new log4net.Layout.PatternLayout("%date [%t] %-5p - %m%n");
appender.Layout = layout;
BasicConfigurator.Configure(appender);
appender.ActivateOptions();
}
}
private static log4net.ILog instance
{
get
{
if (null == _logger)
{
lock (_locker)
{
if (null == _logger)
{
_logger = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
}
}
}
return _logger;
}
}
private static void log_by_level(Level level, int hierarchy, string format, params object[] args)
{
hierarchy += 1;
MethodBase method = get_method_from_stack(hierarchy);
string strHeadFmt = string.Format("[{0}][{1}] ", method.DeclaringType, method.Name);
strHeadFmt += format;
if (level.Equals(Level.Debug))
{
Log.instance.DebugFormat(strHeadFmt, args);
}
else if (level.Equals(Level.Info))
{
Log.instance.InfoFormat(strHeadFmt, args);
}
else if (level.Equals(Level.Warn))
{
Log.instance.WarnFormat(strHeadFmt, args);
}
else if (level.Equals(Level.Error))
{
Log.instance.ErrorFormat(strHeadFmt, args);
}
else if (level.Equals(Level.Fatal))
{
Log.instance.FatalFormat(strHeadFmt, args);
}
else
{
Log.instance.InfoFormat(strHeadFmt, args);
}
}
private static MethodBase get_method_from_stack(int hierarchy)
{
hierarchy += 1; // 这个函数本身也算一层
StackTrace strace = new StackTrace(true);
int nFrameIndex = strace.FrameCount > hierarchy ? hierarchy : 0; // hierachy从0开始计数
MethodBase method = strace.GetFrame(nFrameIndex).GetMethod();
return method;
}
#endregion 私有内容
#region 对外暴露的方法
public static void DebugLog(string format, params object[] args)
{
log_by_level(Level.Debug, 1, format, args);
}
public static void InfoLog(string format, params object[] args)
{
log_by_level(Level.Info, 1, format, args);
}
public static void WarnLog(string format, params object[] args)
{
log_by_level(Level.Warn, 1, format, args);
}
public static void ErrorLog(string format, params object[] args)
{
log_by_level(Level.Error, 1, format, args);
}
public static void FatalLog(string format, params object[] args)
{
log_by_level(Level.Fatal, 1, format, args);
}
/// <summary>
/// 函数入口日志,Info级别
/// </summary>
/// <param name="args"></param>
public static void FuncEntryLog(params object[] args)
{
// 不晓得怎么使用 profiling API.或反射机制实现获取调用函数的参数值,
//>因此这里只能笨笨的让用户程序把参数带过来了,待优化
StringBuilder sbMsg = new StringBuilder("Function Enterd...(");
for (int i = 0; i < args.Length; i++)
{
sbMsg.Append("<arg");
sbMsg.Append(i);
sbMsg.Append(":");
sbMsg.Append(args[i]);
sbMsg.Append(">");
if (i != args.Length - 1)
{
sbMsg.Append(", ");
}
}
sbMsg.Append(")");
log_by_level(Level.Info, 1, sbMsg.ToString());
// 知识点:C++写得话,可以在此处调用个宏,此宏创建个临时变量,该变量的析构函数调用FuncExit()函数。
//>外面的函数执行结束后,该变量生命周期结束时,就会调用其析构函数,进而调用FuncExit()。
//>因此,只要在函数入口调用FuncEntry()就能实现函数出口时的日志输出了。
}
public static void FuncExitLog()
{
log_by_level(Level.Info, 1, "Function Exited...");
}
// 待重载符号<<,以实现"DEBUG << MSG1 << MSG2 << ..."形式的调用
#endregion 对外暴露的方法
}
}
4. 测试程序
private void m_btnChangeWnd_Click(object sender, EventArgs e)
{
Log.FuncEntryLog(sender, e); // 函数入口日志,新代码要求每个函数入口必打日志
_Task.Stop();
Form subform = new SubForm();
Random random = new Random();
int rdNum = random.Next(10);
Log.DebugLog("生成一个随机数:{0},看看是不是{1}的整数倍", rdNum, 2);
if (0 == rdNum % 2)
{
subform.BackColor = Color.Black;
Log.ErrorLog("给你个黑框框");
}
subform.Show();
Log.FuncExitLog(); // 函数出口日志,不强制要求
}
转载请注明出处:http://blog.csdn.net/shineych/article/details/8134954