2

这让我有点发疯,所以如果有人能帮忙,我将非常感激!

我正在尝试将我的信息记录到日志文件中,因此我使用了 Logger 类。

** Logger.cs **

 public  class Logger : IDisposable
{
    private readonly FileStream _file; //Only this instance have a right to own it
    private readonly StreamWriter _writer;
    private readonly object _mutex; //Mutex for synchronizing

    /// <summary>
    /// Call this function to use the text file
    /// </summary>
    /// <param name="logPath"></param>
    public Logger(string logPath)
    {
        if (logPath != null) _file = new FileStream(logPath, FileMode.Append);
        _writer = new StreamWriter(_file);
        _mutex = new object();
    }

    // Log is thread safe, it can be called from many threads
    public void Log(string message)
    {
        lock (_mutex)
        {
           //_writer.Write("\r\nLog Entry : ");
           // _writer.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
           //DateTime.Now.ToLongDateString());

            _writer.WriteLine("{0} {1}", DateTime.Now.ToString("yyyy-MM-dd"),
          DateTime.Now.ToLongTimeString());
            _writer.WriteLine(message);
        }
    }

    /// <summary>
    /// Call this function when it says file is already been using somewhere
    /// </summary>
    public void Dispose()
    {
        _writer.Dispose(); //Will close underlying stream
    }
}

** 我的应用程序使用 Logger 类 **

private void button1_Click(object sender, EventArgs e)
    {
        // This is the file path after d://dashboardLogfiles
        String filePath = string.Format("{0:yyyy-MM-dd}", DateTime.Now);

        // This is the text file created with time stamps
        String txtFile = string.Format("DataSummarisation{0:yyyy-MM-dd hh-mm-ss-tt}", DateTime.Now);
        // Given in config to read the path 
        var localhostizedLetter = @"d:/";
        //Create directory
        string pathString = Path.Combine(localhostizedLetter, "DataSummarisationLogfiles");
        if (!Directory.Exists(pathString))
        {
            Directory.CreateDirectory(pathString);
        }
        // Create a folder inside directory 
        // If folder exists dont create it 
        pathString = Path.Combine(pathString, filePath);
        if (!Directory.Exists(pathString))
        {
            Directory.CreateDirectory(pathString);
        }

        // create a file inside d://DataSummarisationDatetime.now//datetimewithtimestamp.txt
        // if exists please dont create it.
        pathString = Path.Combine(pathString, txtFile);
        if (!Directory.Exists(pathString))
        {
            // here my file is created and opened.
            // so I m doing a try catch to make sure if file is opened we are closing it so that nother process can use it
            File.Create(pathString).Dispose();
            var fileInfo = new FileInfo(pathString);
           // IsFileLocked(fileInfo);

        }

        _logger = new Logger(pathString);
        _logger.Log("Log File Created");
        _logger.Dispose();
        ThreadStart starter = () => MigrateProductStats(123, 0, pathString);
        var thread = new Thread(starter);
        thread.Start();
    }

** 我的函数使用相同的记录器路径 **

 private void MigrateProductStats(object corporationIdObj, object brandIdObj, object logFilePath)
    {
         _logger = new Logger(logFilePath.ToString());
        _logger.Log("Am I still writing to same file?");
        _logger.Dispose();
        for (int i = 0; i <= 10;i++ )
        {
            DoProductStatsForCorporation(123, logFilePath.ToString());
        }

    }
    private void DoProductStatsForCorporation(int corporationId, string logFilePath)
    {
        _logger = new Logger(logFilePath);
        _logger.Log("Am I still writing to same file second time?");
        _logger.Dispose();
    }

** 上述场景有效 **

** 但我希望传递对象而不是路径以避免 Reinstatiaitng **

  ThreadStart starter = () => MigrateProductStats(123, 0, _logger);
    var thread = new Thread(starter);
    thread.Start();

在上述情况下,在我的按钮单击中,我正在处理记录器并将路径发送到函数 DoProductStatsForCorporation 和 MigrateProductStats,而不是如果我尝试发送 _logger 对象而不处理它并避免在我的子函数中重新启动我收到错误无法写入文件,因为它被另一个进程使用。

我希望这是有道理的!

任何有关这方面的指导将不胜感激,因为我非常不知道该去哪里。

4

3 回答 3

2

您遇到的问题是它是 MT 并且很可能您正在写入一个已经打开的文件(竞争条件)

你为什么不为你的记录器/作家使用单例?你为什么不锁定作者并且只使用一个实例而不是总是创建一个新实例?

你的路径字符串看起来也很错误。我的意思是为什么在文件名中每隔一毫秒/滴答一下?

我建议您使用单例方法进行日志记录,或者如果必须,创建记录器的静态实例并在写入时锁定文件并在完成时处理文件。您正在写入可能正在使用的文件,而其他线程正在访问该文件。确保文件名也是唯一的 - 它可能不像您在代码中认为的那样。

于 2013-10-16T11:04:52.590 回答
2

我有点困惑。为什么将文件路径传递给 Logger?它可能应该自己控制它。就个人而言,我会创建我的 Logger 类static,然后像这样同步方法:

[MethodImpl(MethodImplOptions.Synchronized)]
public static void Log(string description)

然后,使用和处理内联编写器以避免这些问题。

using (var writer = new StreamWriter(Path + "\\" + LOG_FILE, true))
{
    writer.WriteLine(log);
    writer.Close();
}
于 2013-10-16T11:11:47.160 回答
1

对于初学者来说DoProductStatsForCorporation,logger 不应该是这个函数的参数。您的记录器应该在开始时创建,并在类中创建一个字段。

您不想将记录器传递给函数,因为函数实际上并不需要此记录器来完成其工作。

将记录器从一些按钮代码中移出,在构造函数中创建它,并从发生记录的类中的私有字段中使用。

如果您想更改同一个类中的日志文件并记录到不同的文件,那么您必须重写您的记录器类,以便它可以使用多个文件。

您还可以了解 Log4net,它非常好。

于 2013-10-16T11:11:59.167 回答