C# 簡易非同步日誌類 [ C# | Log | TextWriterTraceListener ]
前言
即使是小型專案,也需要日誌的功能,這裡就不討論Log4Net之類的框架,提供一個非同步的、控制檯輸出+日誌檔案輸出的簡易日誌幫助類。
正文
一、幫助類檔案Logger.cs
//=========================================
//
// 作 者:農民伯伯
// 郵 箱:[email protected]
// 博 客:http://over140.cnblogs.com/
// 時 間:2009-7-16
// 描 述:日誌類,注意需要在日誌記錄的目錄給使用者寫入許可權!
//
//=========================================
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Runtime.CompilerServices;
using System.Web;
namespace CentralHousesTest
{
/// <summary>
/// 日誌類
/// </summary>
public sealed class Logger
{
#region Member Variables
/// <summary>
/// 用於Trace的組織輸出的類別名稱
/// </summary>
private const string trace_sql = "\r\n***********************TRACE_SQL {0}*****************************\r\nTRACE_SQL";
/// <summary>
/// 用於Trace的組織輸出的類別名稱
/// </summary>
private const string trace_exception = "\r\n***********************TRACE_EXCEPTION {0}***********************";
/// <summary>
/// 當前日誌的日期
/// </summary>
private static DateTime CurrentLogFileDate = DateTime.Now;
/// <summary>
/// 日誌物件
/// </summary>
private static TextWriterTraceListener twtl;
/// <summary>
/// 日誌根目錄
/// </summary>
private const string log_root_directory = @"D:\log";
/// <summary>
/// 日誌子目錄
/// </summary>
private static string log_subdir;
/// <summary>
/// " {0} = {1}"
/// </summary>
private const string FORMAT_TRACE_PARAM = " {0} = {1}";
/// <summary>
/// 1 僅控制檯輸出
/// 2 僅日誌輸出
/// 3 控制檯+日誌輸出
/// </summary>
private static readonly int flag = 2; //可以修改成從配置檔案讀取
#endregion
#region Constructor
static Logger()
{
System.Diagnostics.Trace.AutoFlush = true;
switch (flag)
{
case 1:
System.Diagnostics.Trace.Listeners.Add(new ConsoleTraceListener());
break;
case 2:
System.Diagnostics.Trace.Listeners.Add(TWTL);
break;
case 3:
System.Diagnostics.Trace.Listeners.Add(new ConsoleTraceListener());
System.Diagnostics.Trace.Listeners.Add(TWTL);
break;
}
}
#endregion
#region Method
#region trace
/// <summary>
/// 非同步錯誤日誌
/// </summary>
/// <param name="value"></param>
public static void Trace(Exception ex)
{
new AsyncLogException(BeginTraceError).BeginInvoke(ex, null, null);
}
/// <summary>
/// 非同步SQL日誌
/// </summary>
/// <param name="cmd"></param>
public static void Trace(SqlCommand cmd)
{
new AsyncLogSqlCommand(BeginTraceSqlCommand).BeginInvoke(cmd, null, null);
}
/// <summary>
/// 非同步SQL日誌
/// </summary>
/// <param name="sql"></param>
/// <param name="parameter"></param>
public static void Trace(string sql, params SqlParameter[] parameter)
{
new AsyncLogSql(BeginTraceSql).BeginInvoke(sql, parameter, null, null);
}
#endregion
#region delegate
private delegate void AsyncLogException(Exception ex);
private delegate void AsyncLogSqlCommand(SqlCommand cmd);
private delegate void AsyncLogSql(string sql, params SqlParameter[] parameter);
private static void BeginTraceError(Exception ex)
{
if (null != ex)
{
//檢測日誌日期
StrategyLog();
//輸出日誌頭
System.Diagnostics.Trace.WriteLine(string.Format(trace_exception, DateTime.Now));
while (null != ex)
{
System.Diagnostics.Trace.WriteLine(string.Format("{0} {1}\r\n{2}\r\nSource:{3}", ex.GetType().Name, ex.Message, ex.StackTrace, ex.Source));
ex = ex.InnerException;
}
}
}
private static void BeginTraceSqlCommand(SqlCommand cmd)
{
if (null != cmd)
{
SqlParameter[] parameter = new SqlParameter[cmd.Parameters.Count];
cmd.Parameters.CopyTo(parameter, 0);
BeginTraceSql(cmd.CommandText, parameter);
}
}
private static void BeginTraceSql(string sql, params SqlParameter[] parameter)
{
if (!string.IsNullOrEmpty(sql))
{
//檢測日誌日期
StrategyLog();
System.Diagnostics.Trace.WriteLine(sql, string.Format(trace_sql, DateTime.Now));
if (parameter != null)
{
foreach (SqlParameter param in parameter)
{
System.Diagnostics.Trace.WriteLine(string.Format(FORMAT_TRACE_PARAM, param.ParameterName, param.Value));
}
}
}
}
#endregion
#region helper
/// <summary>
/// 根據日誌策略生成日誌
/// </summary>
private static void StrategyLog()
{
//判斷日誌日期
if (DateTime.Compare(DateTime.Now.Date, CurrentLogFileDate.Date) != 0)
{
DateTime currentDate = DateTime.Now.Date;
//生成子目錄
BuiderDir(currentDate);
//更新當前日誌日期
CurrentLogFileDate = currentDate;
System.Diagnostics.Trace.Flush();
//更改輸出
if (twtl != null)
System.Diagnostics.Trace.Listeners.Remove(twtl);
System.Diagnostics.Trace.Listeners.Add(TWTL);
}
}
/// <summary>
/// 根據年月生成子目錄
/// </summary>
/// <param name="currentDate"></param>
private static void BuiderDir(DateTime currentDate)
{
int year = currentDate.Year;
int month = currentDate.Month;
//年/月
string subdir = string.Concat(year, '\\', month);
string path = Path.Combine(log_root_directory, subdir);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
log_subdir = subdir;
}
#endregion
#endregion
#region Properties
/// <summary>
/// 日誌檔案路徑
/// </summary>
/// <returns></returns>
private static string GetLogFullPath
{
get
{
return string.Concat(log_root_directory, '\\', string.Concat(log_subdir, @"\log", CurrentLogFileDate.ToShortDateString(), ".txt"));
}
}
/// <summary>
/// 跟蹤輸出日誌檔案
/// </summary>
private static TextWriterTraceListener TWTL
{
get
{
if (twtl == null)
{
if (string.IsNullOrEmpty(log_subdir))
BuiderDir(DateTime.Now);
else
{
string logPath = GetLogFullPath;
if (!Directory.Exists(Path.GetDirectoryName(logPath)))
BuiderDir(DateTime.Now);
}
twtl = new TextWriterTraceListener(GetLogFullPath);
}
return twtl;
}
}
#endregion
}
}
二、示例
2.1 錯誤記錄
static void Main(string[] args)
{
IList<string> list = null;
try
{
Console.WriteLine(list.Count);
}
catch (Exception ex)
{
Logger.Trace(ex);
}
Console.ReadLine();
}
程式碼說明:錯誤很明顯,未例項化就使用Count屬性。
日誌截圖:
2.2 資料庫記錄
using (SqlConnection con = new SqlConnection("Data Source=OVERA;Initial Catalog=Northwind;User ID=sa;Password=sa;"))
{
con.Open();
SqlCommand sqlCmd = new SqlCommand("SELECT * FROM Customers WHERE CompanyName = @CompanyName", con);
sqlCmd.Parameters.Add(new SqlParameter("@CompanyName", "Alfreds Futterkiste"));
Logger.Trace(sqlCmd);
SqlDataReader sdr = sqlCmd.ExecuteReader();
while (sdr.Read())
{
Console.Write(sdr.GetName(1));
Console.Write(" : ");
Console.WriteLine( sdr[1]);
}
sdr.Close();
}
Console.ReadLine();
}
日誌截圖:
需要注意這裡日誌策略改為了僅日誌檔案輸出。
三、注意事項
3.1 進行日誌檔案輸出的時候需要有寫的許可權。
3.2 實際使用中可能還會有一個變數控制是否進行日誌記錄。
3.3 錯誤記錄一般會在Global.asax的Application_Error裡面加上。
結束語
對於一般的小專案這個日誌類是夠用的了,日誌的策略還可以根據自己的需求改變一下,合適就行: )