65.9K
CodeProject 正在变化。 阅读更多。
Home

适用于 .net 应用程序的简单且完整的记录器

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.71/5 (5投票s)

2008年12月30日

CPOL

1分钟阅读

viewsIcon

18983

适用于 .net 的非常完整的记录器

引言

这里提供了一种为您的 .NET 应用程序创建非常好的记录器的方法。

背景

我希望创建一个具有几乎所有功能的记录器,也就是说,获取行号、方法、线程 ID 等。

使用示例

首先必须调用 Init 方法
// Here is the method signature
public void Init(LogLevel minLevel, string dirPath, string appId, int closeFileMinutes,
    long maxFileSize_KB, bool overwriteLogFile)

//Example:
CLog.Instance.Init(LogLevel.NOTICE, "." /*current dir*/,
    "myAPpId", 1440 /*a day*/, 4096 /*4 MB */, false);
现在我们可以将任何内容发送到记录器
// Here are the methods signatures
public void Log(LogLevel logLevel, params string[] logData)
public void Log(int stackLevel, LogLevel logLevel, params string[] logData)

//Example:
catch (Exception ex){
    CLog.Instance.Log(LogLevel.ERROR, "Error logging in", ex.ToString());
}
如果您想创建另一个记录器,只需将委托添加到 SendLog 事件,并在其中执行您想要的操作(我没有使用它,但它在那里)
public delegate void DoLog(int stackLevel, LogLevel logLevel, params string[] logData);

public event DoLog SendLog{add;remove}

代码

日志级别的定义,非常简单

    public enum LogLevel
    {
        DEBUG = 0,
        NOTICE,
        WARNING,
        ERROR,
        FATAL,
    }

现在记录器需要工作的几个变量

// CLog is a Singleton
public static readonly CLog Instance = new CLog();

private LogLevel m_minLevel;        // The min level to be logged

private object m_lockLog;           // Critical section

private StreamWriter m_logStream;   // Log stream for writing

private long
    m_maxFileSize,        // Max size in bytes for file
    m_currentFileSize;    // Current file size

private System.Threading.Timer m_closeTimer;     // Timer for reset on time out

private int
    m_closeFileTime,                     // Time to reset (milliseconds)
    m_delegateStackLevel,                // Fucking delegates!
    m_delegateCount;                     // Again fucking delegates

private bool m_overwriteLogFile;         // If false, filename has the created date
                                         // time on his name, if true, the name is
                                         // always the app id

private string
    m_logDirectory,    // Log directory
    m_appId,           // Application Id
    m_logFileName;     // Full path of the log file

private DoLog m_sendLog;    // Broadcast to all listening loggers 
                            // (implementing DoLog delegate)

注意:一个有趣的事情是,如果我为委托添加一个监听器,CLR 使用一个堆栈来调用委托。但是如果我添加更多的委托,它会使用 2 个堆栈来调用该方法,我没有找到原因。这就是为什么我使用了变量 m_delegateStackLevelm_delegateCount

Init 方法
public void Init(LogLevel minLevel, string dirPath, string appId,
    int closeFileMinutes, long maxFileSize_KB, bool overwriteLogFile) {
    lock (m_lockLog) {
        if (m_logStream == null) {
            m_overwriteLogFile = overwriteLogFile;
            m_minLevel = minLevel;
            m_appId = appId;
            m_closeFileTime = closeFileMinutes * 60000;
            m_maxFileSize = maxFileSize_KB * 1024;
            try {
                if (dirPath == String.Empty)
                    dirPath = ".";
                if (!Directory.Exists(dirPath))
                    Directory.CreateDirectory(dirPath);

                DirectoryInfo dirInfo = new DirectoryInfo(dirPath);

                m_logDirectory = dirInfo.FullName;

                m_logFileName = Path.Combine(m_logDirectory,
                                         String.Concat("Log_", m_appId,
                                         m_overwriteLogFile ? String.Empty : 
                                         DateTime.Now.ToString("yyyy-MM-dd_HH.mm.ss"),
                                         ".log"));
                
                m_logStream = new StreamWriter(m_logFileName,
                                         false, Encoding.ASCII);
                m_logStream.AutoFlush = true;

                m_closeTimer = new System.Threading.Timer(
                                         new TimerCallback(newFile), null,
                                         m_closeFileTime, m_closeFileTime);
                Log(2, LogLevel.NOTICE, "Log started,
                                         min log level is: " + minLevel);
            }
            catch { }
        }
    }
}

Log 主要方法

public void Log(int stackLevel, LogLevel logLevel, params string[] logData) {
    if (logLevel >= m_minLevel) {  // Maybe i had to lock the critical section here
        try {
            StackFrame sf = new StackFrame(stackLevel, true);
            string fileName = sf.GetFileName();
            StringBuilder logBuilder = new StringBuilder(
                                DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + ";"
            + logLevel + ";"
            + "Thread:" + Thread.CurrentThread.ManagedThreadId + ";"
            + sf.GetMethod() + ";"
                        // Never fails, if it cannot get filename, will 
                        // return -1
                  + fileName.Substring(
                               fileName.LastIndexOf(Path.DirectorySeparatorChar) + 1) + ";"  
            + "Line:" + sf.GetFileLineNumber()
            ,1024);   // I think that will be enough

            foreach (string data in logData) {
                logBuilder.Append(";" + data);
            }
            // Locking as minimum as i can
            lock (m_lockLog) {
#if(DEBUG)
                Debug.WriteLine(logBuilder);
#endif
                m_logStream.WriteLine(logBuilder);

                     m_currentFileSize += Encoding.ASCII.GetByteCount(
                                    logBuilder.ToString());  // Best way to handle file size

                if (m_currentFileSize > m_maxFileSize)
                    m_closeTimer.Change(0,
                                              m_closeFileTime); // Max file size exceeded,
                                                                // reset file immediately
                if (m_sendLog != null) {
                    m_sendLog(stackLevel + m_delegateStackLevel,
                                                  logLevel, logData);
                }
            }
        }
        catch { }  // Only fails if it is called before Init(),
                              // or if someone misses up the things
    }
}

现在是完整的类代码

// Logger.cs
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;

/*************************************************************************
 * Created by Juan Ramirez (ichr@mm)
 * juanantonio.ram@gmail.com
 * 
 * You can do wherever you want with that copy of the program, but just
 * keep this lines on it, it is better than stealing all the credit for your self 
 * 
 * ***********************************************************************/


namespace Logger
{
    public enum LogLevel
    {
        DEBUG = 0,
        NOTICE,
        WARNING,
        ERROR,
        FATAL,
    }

    public delegate void DoLog(int stackLevel, LogLevel logLevel,
        params string[] logData);

    public sealed class CLog // Singleton
    {
        public static readonly CLog Instance = new CLog();

        private CLog() {
            m_lockLog = new object();
            m_currentFileSize = 0;
            m_sendLog = null;
            m_delegateStackLevel = 1;
        }


        #region member vars

        private LogLevel m_minLevel;

        private object m_lockLog;           // Critical section

        private StreamWriter m_logStream;   // Log stream for writing

        private long
            m_maxFileSize,        // Max size in bytes for file
            m_currentFileSize;    // Current file size

        private System.Threading.Timer m_closeTimer;     // Timer for reset on time out

        private int
            m_closeFileTime,                     // Time to reset (milliseconds)
            m_delegateStackLevel,                // Fucking delegates!
            m_delegateCount;                     // Again fucking delegates
        
        private bool m_overwriteLogFile;         // If false, filename has the
                                                 // created date time on his name,
                                                 // if true, the name is always the app id

        private string
            m_logDirectory,    // Log directory
            m_appId,           // Application Id
            m_logFileName;     // Full path of the log file

        private DoLog m_sendLog;    // Broadcast to all listening
                                    // loggers (implementing DoLog delegate)

        #endregion

        #region Props

        public event DoLog SendLog {
            add {
                lock (m_lockLog) {
                    m_sendLog += value;
                    m_delegateCount += 1;
                    if (m_delegateCount == 2) {
                        m_delegateStackLevel = 2;
                    }
                }
            }
            remove {
                lock (m_lockLog) {
                    m_sendLog -= value;
                    m_delegateCount -= 1;
                    if (m_delegateCount == 1) {
                        m_delegateStackLevel = 1;
                    }
                }
            }
        }

        public string LogFileName {
            get {
                lock (m_lockLog) {
                    return m_logFileName;
                }
            }
        }

        public LogLevel MinLevel {
            get {
                return m_minLevel;
            }
            set {
                m_minLevel = value;
            }
        }

        #endregion


        public void Init(LogLevel minLevel, string dirPath, string appId,
            int closeFileMinutes, long maxFileSize_KB, bool overwriteLogFile) {
            lock (m_lockLog) {
                if (m_logStream == null) {
                    m_overwriteLogFile = overwriteLogFile;
                    m_minLevel = minLevel;
                    m_appId = appId;
                    m_closeFileTime = closeFileMinutes * 60000;
                    m_maxFileSize = maxFileSize_KB * 1024;
                    try {
                        if (dirPath == String.Empty)
                            dirPath = ".";
                        if (!Directory.Exists(dirPath))
                            Directory.CreateDirectory(dirPath);

                        DirectoryInfo dirInfo = new DirectoryInfo(dirPath);

                        m_logDirectory = dirInfo.FullName;

                        m_logFileName = Path.Combine(m_logDirectory,
                            String.Concat("Log_", m_appId,
                            m_overwriteLogFile ? String.Empty : DateTime.Now.ToString(
                            "yyyy-MM-dd_HH.mm.ss"), ".log"));
                        
                        m_logStream = new StreamWriter(m_logFileName, false,
                            Encoding.ASCII);
                        m_logStream.AutoFlush = true;

                        m_closeTimer = new System.Threading.Timer(new TimerCallback(
                            newFile), null, m_closeFileTime, m_closeFileTime);
                        Log(2, LogLevel.NOTICE, "Log started, min log level is: " +
                            minLevel);
                    }
                    catch { }
                }
            }
        }


        public void Log(LogLevel logLevel, params string[] logData) {
            Log(2, logLevel, logData);
        }


        public void Log(int stackLevel, LogLevel logLevel, params string[] logData) {
            if (logLevel >= m_minLevel) {  // Maybe i had to lock the critical section here
                try {
                    StackFrame sf = new StackFrame(stackLevel, true);
                    string fileName = sf.GetFileName();
                    StringBuilder logBuilder = new StringBuilder(
                                                    DateTime.Now.ToString(
                                                        "yyyy-MM-dd HH:mm:ss.fff") + ";"
                                                    + logLevel + ";"
                                                    + "Thread:" +
                                                        Thread.CurrentThread.ManagedThreadId + ";"
                                                    + sf.GetMethod() + ";"
                                                    // Never fails, if it cannot get filename,
                                                    // will return -1
                                                      + fileName.Substring(
                                                         fileName.LastIndexOf(
                                                         Path.DirectorySeparatorChar) + 1) + ";"  
                                                    + "Line:" + sf.GetFileLineNumber()
                                                ,
                                                1024);   // I think that will be enough

                    foreach (string data in logData) {
                        logBuilder.Append(";" + data);
                    }
                    // Locking as minimum as i can
                    lock (m_lockLog) {
#if(DEBUG)
                        Debug.WriteLine(logBuilder);
#endif
                        m_logStream.WriteLine(logBuilder);

                        m_currentFileSize += Encoding.ASCII.GetByteCount(
                            logBuilder.ToString());  // Best way to handle file size

                        if (m_currentFileSize > m_maxFileSize)
                            m_closeTimer.Change(0, m_closeFileTime); // Max file size
                            // exceeded, reset file immediately                          

                        if (m_sendLog != null) {
                            m_sendLog(stackLevel + m_delegateStackLevel, logLevel, logData);
                        }
                    }
                }
                catch { }  // Only fails if it is called before Init(), or if someone
                           // misses up the things
            }
        }


        private void newFile(object o) {
            lock (m_lockLog) {
                try {
                    m_logStream.Close();

                    if (m_overwriteLogFile == false) {
                        m_logFileName = Path.Combine(m_logDirectory, string.Concat(
                            "Log_", m_appId, DateTime.Now.ToString(
                            "yyyy-MM-dd_HH.mm.ss"), ".log"));
                    }
                    m_currentFileSize = 0;
                    m_logStream = new StreamWriter(m_logFileName);
                    m_logStream.AutoFlush = true;
                }
                catch { }
            }
        }
    }
}

注释

该类在开发时注重性能,因此尝试用更少的工作获取大量信息,例如,我获取文件大小的方式是通过正在写入的字符串的大小。

任何建议、建设性的批评,请随时提出。

此致

© . All rights reserved.