微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 为每个客户端和日期分隔日志文件和目录

我有一个 Windows TCP服务,它有许多设备连接到它,客户端可以有一个或多个设备.

需求:

每个客户端分开文件夹,每个设备都有单独的日志文件.

所以这样的东西:

/MyService/25-04-2016/
    Client 1/
        Device1.txt 
        Device2.txt 
        Device3.txt 

    Client 2/
        Device1.txt 
        Device2.txt 
        Device3.txt

现在我没有使用第三方库,如log4net或NLog,我有一个处理这个的类.

public class xPTLogger : Idisposable
{
    private static object fileLocker = new object();

    private readonly string _logFileName;
    private readonly string _logFilesLocation;
    private readonly int _clientId;

    public xPTLogger() : this("General") { }

    public xPTLogger(string logFileName)
    {
        _clientId = -1;
        _logFileName = logFileName;
        _logFilesLocation = SharedConstants.LogFilesLocation; // D:/LogFiles/
    }

    public xPTLogger(string logFileName,int companyId)
    {
        _clientId = companyId;
        _logFileName = logFileName;
        _logFilesLocation = SharedConstants.LogFilesLocation;
    }

    public void LogMessage(MessageType messageType,string message)
    {
        LogMessage(messageType,message,_logFileName);
    }

    public void LogExceptionMessage(string message,Exception innerException,string stackTrace)
    {
        var exceptionMessage = innerException != null
                ? string.Format("Exception: [{0}],Inner: [{1}],Stack Trace: [{2}]",innerException.Message,stackTrace)
                : string.Format("Exception: [{0}],Stack Trace: [{1}]",stackTrace);

        LogMessage(MessageType.Error,exceptionMessage,"Exceptions");
    }

    public void LogMessage(MessageType messageType,string message,string logFileName)
    {
        var dateTime = DateTime.UtcNow.ToString("dd-MMM-yyyy");

        var logFilesLocation = string.Format("{0}{1}\\",_logFilesLocation,dateTime);

        if (_clientId > -1) { logFilesLocation = string.Format("{0}{1}\\{2}\\",dateTime,_clientId); }


        var fullLogFile = string.IsNullOrEmpty(logFileName) ? "GeneralLog.txt" : string.Format("{0}.txt",logFileName);


        var msg = string.Format("{0} | {1} | {2}\r\n",DateTime.UtcNow.ToString("dd-MMM-yyyy HH:mm:ss"),messageType,message);

        fullLogFile = GenerateLogFilePath(logFilesLocation,fullLogFile);

        LogToFile(fullLogFile,msg);
    }

    private string GenerateLogFilePath(string objectLogDirectory,string objectLogFileName)
    {
        if (string.IsNullOrEmpty(objectLogDirectory))
            throw new ArgumentNullException(string.Format("{0} location cannot be null or empty","objectLogDirectory"));
        if (string.IsNullOrEmpty(objectLogFileName))
            throw new ArgumentNullException(string.Format("{0} cannot be null or empty","objectLogFileName"));

        if (!Directory.Exists(objectLogDirectory))
            Directory.CreateDirectory(objectLogDirectory);
        string logFilePath = string.Format("{0}\\{1}",objectLogDirectory,objectLogFileName);
        return logFilePath;
    }

    private void LogToFile(string logFilePath,string message)
    {
        if (!File.Exists(logFilePath))
        {
            File.WriteallText(logFilePath,message);
        }
        else
        {
            lock (fileLocker)
            {
                File.AppendAllText(logFilePath,message);
            }
        }
    }

    public void dispose()
    {
        fileLocker = new object();
    }
}

然后我可以这样使用它:

var _logger = new xPTLogger("deviceid",12);

 _logger.LogMessage(MessageType.Info,string.Format("information Message = [{0}]",1));

上述类的问题是,由于服务是多线程的,一些线程尝试访问相同的日志文件,同时导致异常抛出.

25-Apr-2016 13:07:00 | Error | Exception: The process cannot access the file 'D:\LogFiles\25-Apr-2016\0\LogFile.txt' because it is being used by another process.

这有时导致我的服务崩溃.

如何使我的Logger类工作在多线程服务中?

编辑

对Logger类的更改

public class xPTLogger : Idisposable
{
    private object fileLocker = new object();

    private readonly string _logFileName;
    private readonly string _logFilesLocation;
    private readonly int _companyId;

    public xPTLogger() : this("General") { }

    public xPTLogger(string logFileName)
    {
        _companyId = -1;
        _logFileName = logFileName;
        _logFilesLocation = SharedConstants.LogFilesLocation; // "D:\\MyLogs";
    }

    public xPTLogger(string logFileName,int companyId)
    {
        _companyId = companyId;
        _logFileName = logFileName;
        _logFilesLocation = SharedConstants.LogFilesLocation;
    }

    public void LogMessage(MessageType messageType,string logFileName)
    {
        if (messageType == MessageType.Debug)
        {
            if (!SharedConstants.EnableDebugLog)
                return;
        }

        var dateTime = DateTime.UtcNow.ToString("dd-MMM-yyyy");

        var logFilesLocation = string.Format("{0}{1}\\",dateTime);

        if (_companyId > -1) { logFilesLocation = string.Format("{0}{1}\\{2}\\",_companyId); }


        var fullLogFile = string.IsNullOrEmpty(logFileName) ? "GeneralLog.txt" : string.Format("{0}.txt",string message)
    {
        lock (fileLocker)
        {
            try
            {
                if (!File.Exists(logFilePath))
                {
                    File.WriteallText(logFilePath,message);
                }
                else
                {
                    File.AppendAllText(logFilePath,message);
                }
            }
            catch (Exception ex)
            {
                var exceptionMessage = ex.InnerException != null
                                ? string.Format("Exception: [{0}],ex.Message,ex.InnerException.Message,ex.StackTrace)
                                : string.Format("Exception: [{0}],ex.StackTrace);

                var logFilesLocation = string.Format("{0}{1}\\",DateTime.UtcNow.ToString("dd-MMM-yyyy"));

                var logFile = GenerateLogFilePath(logFilesLocation,"FileAccessExceptions.txt");

                try
                {
                    if (!File.Exists(logFile))
                    {
                        File.WriteallText(logFile,exceptionMessage);
                    }
                    else
                    {
                        File.AppendAllText(logFile,exceptionMessage);
                    }
                }
                catch (Exception) { }
            }

        }
    }

    public void dispose()
    {
        //fileLocker = new object();
        //_logFileName = null;
        //_logFilesLocation = null;
        //_companyId = null;
    }
}

解决方法

如果不想使用现有的解决方案,在记录器中处理多线程写入的合理方法是使用队列.这是草图:
public class LogQueue : Idisposable {
    private static readonly Lazy<LogQueue> _isntance = new Lazy<LogQueue>(CreateInstance,true);
    private Thread _thread;
    private readonly BlockingCollection<LogItem> _queue = new BlockingCollection<LogItem>(new ConcurrentQueue<LogItem>());

    private static LogQueue CreateInstance() {
        var queue = new LogQueue();
        queue.Start();
        return queue;
    }

    public static LogQueue Instance => _isntance.Value;

    public void QueueItem(LogItem item) {
        _queue.Add(item);
    }

    public void dispose() {
        _queue.CompleteAdding();
        // wait here until all pending messages are written
        _thread.Join();
    }

    private void Start() {
        _thread = new Thread(ConsumeQueue) {
            IsBackground = true
        };
        _thread.Start();
    }

    private void ConsumeQueue() {
        foreach (var item in _queue.GetConsumingEnumerable()) {
            try {
                // append to your item.TargetFile here                    
            }
            catch (Exception ex) {
                // do something or ignore
            }
        }
    }
}

public class LogItem {
    public string TargetFile { get; set; }
    public string Message { get; set; }
    public MessageType MessageType { get; set; }
}

然后在你的记录器类中:

private void LogToFile(string logFilePath,string message) {
    LogQueue.Instance.QueueItem(new LogItem() {
        TargetFile = logFilePath,Message = message
    });
}

在这里,我们将实际的日志记录委托给一个逐个写入日志的单独的类,所以不能有任何多线程问题.这种方法的另外的好处是记录异步发生,因此不会减慢实际工作.

缺点是在进程崩溃的情况下可能会丢失一些消息(不要以为这是真的是一个问题,但仍然提到它),并且您消耗单独的线程以异步记录.当有一个线程时,它不是一个问题,但是如果您为每个设备创建一个线程,那可能是(尽管不需要 – 只需使用单个队列,除非您每秒写入大量消息).

原文地址:https://www.jb51.cc/csharp/94628.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐