使用log4net.Appender.ColoredConsoleAppender的死锁

时间:2018-11-21 11:52:55

标签: c# console log4net log4net-appender

ColoredConsoleAppender线程安全吗?

我有一个多线程控制台应用程序,在其中我使用log4net.Appender.ColoredConsoleAppender来获取打印在控制台窗口中的错误消息。

我还使用此处介绍的非阻塞控制台阅读器:https://stackoverflow.com/a/18342182/1688642

有时,应用程序会阻塞,然后在控制台中按回车键(Enter)可以消除死锁。这总是跟在log4net上的错误(通过ColoredConsoleAppender)。我怀疑控制台阅读器中的Console.ReadLine和coloredConsoleAppender内部的文字之间存在死锁(这不是简单的Console.WriteLine)。

我查看了ColoredConsoleAppender的源代码,它比我想象的要复杂得多,我怀疑它不是线程安全的。

我还阅读了http://blogs.microsoft.co.il/dorony/2012/09/12/consolereadkey-net-45-changes-may-deadlock-your-system/中描述的Console.ReadLine和Console.WriteLine之间可能发生的潜在死锁。但是我得出的结论是,这不是同一个问题。

更新1: 下面的代码是一个说明,不是真实的代码。而且此代码不会死锁....

using System;
using System.IO;
using System.Reflection;
using System.Threading;
using log4net;
using log4net.Config;
...
class Program
{
    static void Main(string[] args)
    {
        var exeLocation = new FileInfo(Assembly.GetEntryAssembly().Location);
        var appConfig = new FileInfo(Path.Combine(exeLocation.DirectoryName, Assembly.GetEntryAssembly().GetName().Name + ".exe.config"));
        XmlConfigurator.Configure(appConfig);

        // Start two threads that writes log messages
        Thread t1 = new Thread(ThreadLoop);
        t1.Start("T1");

        Thread t2 = new Thread(ThreadLoop);
        t2.Start("T2");

        ILog log = LogManager.GetLogger("MAIN_LOG");
        Console.Write("$ ");
        while (true)
        {
            string line;
            if (Reader.TryReadLine(out line, 100))
            {
                bool handled = ParseAndExecuteCommand(line);
                if (!handled)
                {
                    Console.WriteLine("Unknown command (type 'h' to get help).");
                }
                Console.Write("$ ");
            }
            log.Info($"Info from main {Environment.TickCount}");
            Console.WriteLine($"Console.WriteLine from main {Environment.TickCount}");
        }
    }

    private static void ThreadLoop(object name)
    {
        while (true)
        {
            Thread.Sleep(1000);
            ILog log = LogManager.GetLogger("THREAD_LOG");
            log.Info($"Info from thread {name} {Environment.TickCount}");
            log.Warn($"Warning from thread {name}  {Environment.TickCount}");
            log.Error($"Error from thread  {name} {Environment.TickCount}");

            Console.WriteLine($"Console.WriteLine from thread {name} {Environment.TickCount}");
        }
    }

下面是App.config中的log4net配置:

<?xml version="1.0" encoding="utf-8"?>
<configuration>  
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>
  <log4net>
    <appender name="ConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
      <mapping>
        <level value="ERROR" />
        <foreColor value="Red, HighIntensity" />
      </mapping>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
      <threshold value="ERROR" />
    </appender>
    <root>
      <level value="INFO" />
      <appender-ref ref="ConsoleAppender" />
    </root>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
  </startup>
</configuration>

1 个答案:

答案 0 :(得分:0)

我已找到问题的原因。它与ColoredConsoleAppender不相关。这是Windows Command提示符的错误/功能,导致该问题。

如果在命令提示符下进入选择模式,则所有输出均被阻止(!)。如果启用了快速编辑模式,则只需在控制台窗口中单击即可。然后,文本标记将变为一个小的填充矩形,指示控制台现在处于选择模式。不容易发现!

问题描述如下: Why is my command prompt freezing on Windows 10?

我已经使用多个主题广泛测试了ColoredConsoleAppender,但没有发现任何死锁。我已经阅读了追加程序的源代码。

ColoredConsoleAppender不是线程安全的,但是我发现的唯一真正的同步问题是文本颜色混合在一起。附加程序完成的颜色更改可能会影响正在写入控制台的其他线程。

可在以下位置找到附加程序的源代码:
 https://github.com/apache/logging-log4net/blob/master/src/Appender/ColoredConsoleAppender.cs

更新:ManagedColoredConsoleAppender(https://logging.apache.org/log4net/log4net-1.2.13/release/sdk/log4net.Appender.ManagedColoredConsoleAppender.html)是更好的选择。

当控制台进入选择模式时,它仍然会阻塞,但是它使用标准的Console.Out和Console.Error TextWriters。这样就可以编写可通过Console.SetOut()和Console.SetError()设置的非阻塞TextWriter。 ManagedColoredConsoleAppender将使用新的TextWriter,而ColoredConsoleAppender将不使用。