Тупик с помощью log4net.Appender.ColoredConsoleAppender - PullRequest
0 голосов
/ 21 ноября 2018

Безопасен ли поток ColoredConsoleAppender?

У меня есть многопоточное консольное приложение, в котором я использую log4net.Appender.ColoredConsoleAppender для вывода сообщений об ошибках, напечатанных в окне консоли.

Я также использую неблокирующее консольное устройство чтения, которое описано здесь: https://stackoverflow.com/a/18342182/1688642

Иногда приложение блокирует и нажимает клавишу возврата (Enter) в консоли, чтобы удалить тупик.Это всегда сопровождается ошибкой из log4net (через ColoredConsoleAppender).Я подозреваю, что существует тупик между Console.ReadLine в считывателе консоли и записью внутри ColoredConsoleAppender (который не является простым Console.WriteLine).

Я посмотрел на исходный код ColoredConsoleAppender и онбыло гораздо сложнее, чем я думал, и я подозреваю, что это не потокобезопасно.

Я также читал о потенциальной тупиковой ситуации, которая может возникнуть между Console.ReadLine и Console.WriteLine, описанной здесь: http://blogs.microsoft.co.il/dorony/2012/09/12/consolereadkey-net-45-changes-may-deadlock-your-system/ но я пришел к выводу, что это не та же проблема.

Обновление 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}");
        }
    }

Ниже приведена конфигурация log4net из файла App.config:

<?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 голосов
/ 05 февраля 2019

Я нашел причину своей проблемы.Это не связано с ColoredConsoleAppender.Это ошибка / функция командной строки Windows, которая вызывает проблему.

Если вы войдете в режим выбора в командной строке, все выходные данные будут заблокированы (!).Если включен режим быстрого редактирования, достаточно щелкнуть внутри окна консоли.Текстовый маркер изменится на маленький заполненный прямоугольник, указывающий, что консоль сейчас находится в режиме выделения.Не легко обнаружить!

Проблема описана здесь: Почему моя командная строка зависает в 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.Это позволяет писать неблокирующий TextWriter, который можно установить с помощью Console.SetOut () и Console.SetError ().ManagedColoredConsoleAppender будет использовать новый TextWriter, а ColoredConsoleAppender - нет.

...