1. 程式人生 > >C# 簡易非同步日誌類 [ C# | Log | TextWriterTraceListener ]

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裡面加上。

結束語  

  對於一般的小專案這個日誌類是夠用的了,日誌的策略還可以根據自己的需求改變一下,合適就行: )