WPF 自定义日志记录
为了便于分析应用程序的运行情况和BUG诊断,一般都会搞个日志输出。
当初看过一点Log4net,觉得有些麻烦了,就动手写了个简单的。
本例子已经在 项目中使用过,拿出来分享一下,欢迎各位拍砖,呵呵。
本日志记录比较轻巧,在config文件里面配置后,把loghelper.cs 文件放到项目中即可使用。日志输出开关有四个:不输出、输出到DebugVIew、每次全新输出到文本、追加输出到文本。
输出的日志有特定的分隔符,可以放到excel里面,分割为每一列后继续进行筛选等分析。
本例子运行环境是DoNet 3.5 WPF C#,也可以扩展到别的环境使用.
效果预览
配置文件
当初想过使用ini文件或者xml文件存储配置信息,后来发现config文件用的比较多,而且config文件读取比较方便,索性就集成到Config文件里面。
在项目里面添加config文件即可。
1 <?xml version="1.0" encoding="utf-8" ?>
2 <!-- Author Shi Xiaokang
3 http://www.cnblogs.com/xiaokang088/
4 Version 1.00
5 Date: 2011-1-21-->
6 <configuration>
7 <runtime>
8 <generatePublisherEvidence enabled="false"/>
9 </runtime>
10 <appSettings>
11 <!--
12 ; 0 - No output
13 ; 1 - Debugger (Such as DbgView)
14 ; 2 - File (Overwrite the old file and doesn't close it until application exits)
15 ; 3 - File (Append the log at the end of file and close it after each log output)
16 ; 4 - Debugger&File (Append the log at the end of file and close it after each log output)
17 -->
18 <add key="logDestination" value="4"/>
19 <!--log file path. if value is null or string.empty, don't ouput log to file-->
20 <!--example:%LocalAppData%\log.txt-->
21 <add key="logPath" value="%LocalAppData%\log.txt"/>
22 </appSettings>
23 </configuration>
logDestination
- 0 没有任何日志输出
- 1 可以输出到DebugView
- 2 每次启动应用程序后,把上次写的日志清除掉,重新写一份日志
- 3 第一次启动应用程序后,创建日志文件,后面一直累加
logPath 文本日志的文件位置, 推荐使用 AppData里面的路径,这个是系统默认的应用程序 数据存储路径。
如果搞别的,也可以,但是要考虑只读、权限等问题,由于客户机环境复杂,所以还是放在AppData 比较省心。
使用方法
在需要打印日志的地方调用方法, 大致分为:简单打印、打印变量、打印方法执行结果、打印堆栈信息、打印异常。
下面只列举了一部分,
1 // UI|2011/12/20 14:57:39 630 | Test Write Method
2 LogHelper.Write("Test Write Method");
3
4 bool? testBool = false;
5 /// UI|2011/12/20 14:57:48 277 | False
6 LogHelper.Write(testBool);
7
8 Double testDouble = 0.2256;
9 /// UI|2011/12/20 14:57:48 279 | 0.2256
10 LogHelper.Write(testDouble);
11
12 /// UI|2011/12/20 15:01:28 801 | current ID:115001 Name:Jackon
13 LogHelper.Write("current ID:{0} Name:{1}", 115001, "Jackon");
14
15 bool? ret = DoTestIsOK();
16 /// UI|2011/12/20 15:17:04 732 | Method:DoTestIsOK --> Result:Null
17 LogHelper.WriteMethod("DoTestIsOK", ret);
18 int a = 1, b = 3;
19 int ret = DoTestAdd(a, b);
20
21 /// UI|2011/12/20 15:17:05 316 | Method:DoTestAdd --> Result:4 -- Parms:a:1 b:3
22 LogHelper.WriteMethod("DoTestAdd", ret, "a:{0} b:{1}", a, b);
23
原理
C# 的Trace可以打印到output里面,Realse后,也可以打印到DebugView中。
这里的实现是丰富了Trace的功能,并且将日志输出为 不同的格式,打印出来后方便分析。
通过 Trace.Listeners 可以控制输出的目的地。
话不多说,都在代码里,如下:
1 #region init
2 /// <summary>
3 /// init by configuration
4 /// </summary>
5 public static void Init()
6 {
7 #region define
8 string logPathKey = "logPath", logDestinationKey = "logDestination", autoFlushKey = "autoFlush";
9 string logPath = string.Empty, logDestination = string.Empty;
10 FileStream stream;
11 FileMode fileMode;
12 #endregion
13
14 #region get destination
15 //no destination,return
16 if (!ConfigurationManager.AppSettings.AllKeys.Contains(logDestinationKey)) return;
17 logDestination = ConfigurationManager.AppSettings[logDestinationKey].Trim();
18
19 int logType = 0;
20 Int32.TryParse(logDestination, out logType);
21 //if logType == 0 , don't record log
22 isLog = (logType != 0);
23 #endregion
24
25
26
27
28
29
30 #region switch log type
31 switch (logType)
32 {
33 case 0:
34 {
35 //0 - No output
36 Trace.Listeners.Clear();
37 return;
38 }
39 case 1:
40 {
41 //1 - Debugger (Such as DbgView)
42 //use default listener
43 return;
44 }
45 case 2:
46 {
47 //2 - File (Overwrite the old file and doesn't close it until application exits)
48 Trace.Listeners.Clear();
49 fileMode = FileMode.Create; break;
50 }
51 case 3:
52 {
53 //3 - File (Append the log at the end of file and close it after each log output)
54 Trace.Listeners.Clear();
55 fileMode = FileMode.Append; break;
56 }
57 case 4:
58 {
59 //4 - Debugger&File (Append the log at the end of file and close it after each log output)
60 fileMode = FileMode.Append; break;
61 }
62
63 default: return;
64 }
65 #endregion
66
67 #region check path
68 //path is null
69 logPath = ConfigurationManager.AppSettings[logPathKey].Trim();
70 if (string.IsNullOrEmpty(logPath)) return;
71
72 //path has invalid char
73 var pathCharArray = logPath.ToCharArray();
74 if (pathCharArray.Any(o => Path.GetInvalidPathChars().Contains(o)))
75 return;
76
77 //FileName has invalid char
78 //note : invalid file name chars count is 41,
79 //invalid path chars count is 36
80 //and , the top 36 of invalid file name chars are same as invalid path chars
81 //so,first check path invalid chars,second check filename,only filename
82 var filenameCharArray = Path.GetFileName(logPath).ToCharArray();
83 if (filenameCharArray.Any(o => Path.GetInvalidFileNameChars().Contains(o)))
84 return;
85
86 //EnvironmentVariables Path
87 if (logPath.Contains('%'))
88 logPath = Environment.ExpandEnvironmentVariables(logPath);
89
90 //cheng relative path to absolute path.
91 if (String.IsNullOrEmpty(Path.GetPathRoot(logPath)))
92 logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, logPath);
93 #endregion
94
95 #region file log
96 //risk:directory readonly;need administrator right to createfile;and so on
97 //use try-catch
98 try
99 {
100 if (!Directory.Exists(Path.GetDirectoryName(logPath)))
101 Directory.CreateDirectory(Path.GetDirectoryName(logPath));
102
103 stream = File.Open(logPath, fileMode, FileAccess.Write, FileShare.ReadWrite);
104 TextWriterTraceListener text = new TextWriterTraceListener(stream);
105 //text.TraceOutputOptions = TraceOptions.DateTime;
106 Trace.Listeners.Add(text);
107 }
108 catch (Exception ex)
109 {
110 Trace.Write(ex);
111 }
112 #endregion
113
114 }
115 #endregion
结束,欢迎拍砖。