Trace、Debug和TraceSource与调试及日志设计

1:Trace与Debug类的特性:

 用法基本相同,只是Debug是在Debug本版本下使用,在Release版本下不会生成代码,而Trace在默认情况下是打开的,也就是说在Debug版本和Release版本下都会生成代码。


2:什么是Listeners

      Listenters属性,它是TraceListenerCollection类型(TraceSource类和TraceListener类),给类 属性控制跟踪信息输出的方向,可以是控制台(add(TextWriterTraceListener(new Console.Out))),文件(add(TextWriterTraceListener(new IO.File.CreateText(“output.txt”))等。Listenters集合中的成员包括 TextWriterTraceListener,DefaultTraceListener,EventLogTraceListener,WebPageTraceListener 等。而TextWriterTraceListener的子类又有ConsoleTraceListener, DelimitedListTraceListener,XmlWriterTraceListener,EventSchemaTraceListener。

  Example:

 TextWriterTraceListener tr1 = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(tr1);
TextWriterTraceListener tr2 = new TextWriterTraceListener(System.IO.File.CreateText("output.txt"));
Debug.Listeners.Add(tr2);
EventLogTraceListener tr3 = new EventLogTraceListener();
Debug.Listeners.Add(tr3);
TextWriter   m_tracer   =   File.CreateText(@ "D:\trace.txt ");
System.Diagnostics.TextWriterTraceListener   objTraceListener   =   new   TextWriterTraceListener(m_tracer);
Trace.Listeners.Add   (objTraceListener);

 

3:中断模式的调试


4:跟踪开关

      除了指定Listener外,要控制消息是否被输出,还需要指定跟踪开关。跟踪开关用于启用、禁用和筛选跟踪输出。

      Framework 中提供了三种类型的跟踪开关:BooleanSwitch 类、TraceSwitch 类和 SourceSwitch 类。BooleanSwitch是最简单的跟踪开关,可以指定是否输出消息。TraceSwitchSourceSwitch 类用于为特定的跟踪级别启用跟踪开关,以便显示为该级别及其下的所有级别指定的 TraceTraceSource 消息。

4.1:使用BooleanSwitch开关

  Example:

bool someBizCondition = true;
BooleanSwitch bs = new BooleanSwitch("DataMessageSwitch", "DataMessageSwitch des");
bs.Enabled = true;
Debug.WriteLineIf(someBizCondition, "log....");
Debug.Flush();

      bs.Enabled设置为true或者false,并不会使程序自动决定是否输出信息。

      如果不使用代码方式,而是使用配置文件的方式,是在 <configuration> 标记之后,但在 </configuration> 标记之前添加相应的 XML 来配置您的开关。如下:

<system.diagnostics>
<switches>
<add name="DataMessagesSwitch" value="1" />
</switches>
</system.diagnostics>
 注意其中的:DataMessagesSwitch

4.2:使用TraceSwitch开关

      TraceSwitch类可以通过使用跟踪开关来筛选消息,通过Level属性来获取或设置开关级别。0、1、2、3 和 4 分别对应于 OffErrorWarningInfoVerbose。任何大于 4 的数字都会被当作 Verbose,任何小于零的数字都会被当作 Off

Example:

 TextWriterTraceListener tr = new TextWriterTraceListener(System.IO.File.CreateText("output.txt"));
Debug.Listeners.Add(tr);
bool someBizCondition = true;
TraceSwitch ts = new TraceSwitch("mySwitch", "in the Config file");
ts.Level = TraceLevel.Verbose;
Debug.WriteLineIf(ts.TraceError && someBizCondition, "Error!!!");
Debug.WriteLineIf(ts.TraceWarning && someBizCondition, "Warning!!!");
Debug.WriteLineIf(ts.TraceInfo && someBizCondition, "Info!!!");
Debug.WriteLineIf(ts.TraceVerbose && someBizCondition, "Verbose!!!");
Debug.Flush();

 使用XML来配置,如下:

<system.diagnostics>
<switches>
<add name="mySwitch" value="1" />
</switches>
</system.diagnostics>

5:使用TraceSource代替Trace和Debug

       从FRAMEWORK2.0开始,就不建议使用Trace和Debug了,而改而用TraceSouce。TraceSource 旨在用作增强的跟踪系统,并且可代替较旧的 TraceDebug 跟踪类的静态方法使用。熟悉的 TraceDebug 类仍然存在,不过建议的做法是使用 TraceSource 类进行跟踪。

Exapmle:
 private static TraceSource mySource = new TraceSource("TraceSourceApp");
static void Main(string[] args)
{
mySource.Switch = new SourceSwitch("sourceSwitch", "Error");
mySource.Listeners.Remove("Default");

TextWriterTraceListener textListener = new TextWriterTraceListener("myListener.log");
textListener.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.Callstack;
textListener.Filter = new EventTypeFilter(SourceLevels.Error);
mySource.Listeners.Add(textListener);

ConsoleTraceListener console = new ConsoleTraceListener(false);
console.Filter = new EventTypeFilter(SourceLevels.Information);
console.Name = "console";
mySource.Listeners.Add(console);
Activity1();

// Set the filter settings for the
// console trace listener.
mySource.Listeners["console"].Filter = new EventTypeFilter(SourceLevels.Critical);
Activity2();

// Allow the trace source to send messages to
// listeners for all event types.
mySource.Switch.Level = SourceLevels.All;

// Change the filter settings for the console trace listener.
mySource.Listeners["console"].Filter = new EventTypeFilter(SourceLevels.Information);
Activity3();
mySource.Close();

return;
}
static void Activity1()
{
mySource.TraceEvent(TraceEventType.Error, 1, "Error message.");
mySource.TraceEvent(TraceEventType.Warning, 2, "Warning message.");
}
static void Activity2()
{
mySource.TraceEvent(TraceEventType.Critical, 3, "Critical message.");
mySource.TraceInformation("Informational message.");
}
static void Activity3()
{
mySource.TraceEvent(TraceEventType.Error, 4, "Error message.");
mySource.TraceInformation("Informational message.");
}

      以上代码,如果使用配置文件的方式实现,如下:

 <system.diagnostics>
<sources>
<source name="TraceSourceApp" switchName="sourceSwitch" switchType="System.Diagnostics.SourceSwitch">
<listeners>
<add name="console" type="System.Diagnostics.ConsoleTraceListener">
<filter type="System.Diagnostics.EventTypeFilter" initializeData="Warning"/>
</add>
<add name="myListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="myListener.log"
traceOutputOptions="Callstack">
<filter type="System.Diagnostics.EventTypeFilter" initializeData="Error"></filter>
</add>
<remove name="Default"/>
</listeners>
</source>
</sources>
<switches>
<add name="sourceSwitch" value="Warning"/>
</switches>
</system.diagnostics>

      配置文件实现的对应代码部分为:

 private static TraceSource mySource = new TraceSource("TraceSourceApp");
static void Main(string[] args)
{
Activity1();
Activity2();
Activity3();
mySource.Close();
return;
}
static void Activity1()
{
mySource.TraceEvent(TraceEventType.Error, 1, "Error message.");
mySource.TraceEvent(TraceEventType.Warning, 2, "Warning message.");
}
static void Activity2()
{
mySource.TraceEvent(TraceEventType.Critical, 3, "Critical message.");
mySource.TraceInformation("Informational message.");
}
static void Activity3()
{
mySource.TraceEvent(TraceEventType.Error, 4, "Error message.");
mySource.TraceInformation("Informational message.");
}

6:日志写入事件查看器

 

EventLog elApplication = new EventLog();
elApplication.Log = "Selcom";
elApplication.Source = "Demand Analysis";
EventLogTraceListener elTraceListener = new EventLogTraceListener();
elTraceListener.EventLog = elApplication;
Trace.Listeners.RemoveAt(0);
Trace.Listeners.Add(elTraceListener);
Trace.WriteLine("应用程序启动!");
Trace.WriteLine("应用程序的其他事件记录");

 

 

#region 默认在“应用程序”导航下面
EventLogTraceListener myTraceListener = new EventLogTraceListener("myEventLogSource");
Trace.Listeners.Add(myTraceListener);
Trace.WriteLine("Test output");
#endregion
 


7:设计一个日志系统

      有了以上的知识之后,我们就可以来设计一个应用程序的日志系统(是的,我们不在需要LOG4NET)。我们来假设这个日志系统最基础的功能:

      1、以日期来创建日志名,以免历史日志全部写入一个文件中去;

      2、可以配置想要输出的日志类别,如Warning或者Error等;

      3、可以配置想要的日志内容:如StackTrace或者错误信息等;

Example:

 

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  LogExample
{
    /// <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

    }
}

 

使用:

 

using System.Collections.Generic;
using System;
using System.Data.SqlClient;
namespace LogExample
{

    public class Program
    {

        static void Main(string[] args)
        {
            //错误记录
            IList<string> list = null;

            try
            {
                Console.WriteLine(list.Count);
            }
            catch (Exception ex)
            {
                Logger.Trace(ex);
            }

            Console.ReadLine();


            //数据库记录
            using (SqlConnection con = new SqlConnection("Data Source=zhouyz;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", "tt"));

                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();
        }
    }
}

 

 

 

posted @ 2011-08-02 15:20  星释天狼  阅读(1131)  评论(0)    收藏  举报