Ещё одна доработка исходников: Разгрузка основного потока от логирования. Возможность изоляции от системных ресурсов, связанных с логированием.
public abstract class BaseLogReceiver<TParent> : BaseLogSource<TParent>, ILogReceiver
{
/// <summary>
/// Инициализировать <see cref="BaseLogReceiver{TParent}"/>.
/// </summary>
protected BaseLogReceiver()
{
}
void ILogReceiver.AddLog(LogMessage message)
{
LoggingQueue.AddLogAction(()=>RaiseLog(message));
}
}
/// <summary>
/// Логирование в отдельном параллельном потоке
/// </summary>
public class LoggingQueue
{
//если понадобится подменять реализацию
public static LoggingQueue Instance { get; set; }
static LoggingQueue()
{
Instance = new LoggingQueue();
}
public LoggingQueue()
{
StartThread();
}
/// <summary>
/// Запуск отдельного параллельного потока логирования
/// </summary>
protected virtual void StartThread()
{
_loggingQueue = new ConcurrentQueue<Action>();
_waitLog = new AutoResetEvent(false);
Task.Run(() => DoLoggingListen());
}
private ConcurrentQueue<Action> _loggingQueue;
private AutoResetEvent _waitLog;
/// <summary>
/// Добавление записи в лог
/// </summary>
/// <param name="action"></param>
public static void AddLogAction(Action action)
{
Instance.AddAction(action);
}
/// <summary>
/// Добавление действия записи в лог в очередь потока логирования
/// </summary>
/// <param name="action"></param>
public virtual void AddAction(Action action)
{
_loggingQueue.Enqueue(action);
_waitLog.Set();
}
/// <summary>
/// Чтение очереди действий логирования
/// </summary>
protected virtual void DoLoggingListen()
{
do
{
Action logAction;
if (_loggingQueue.TryDequeue(out logAction))
logAction.Invoke();
else
_waitLog.WaitOne();
} while (!_stopped);
}
private bool _stopped;
/// <summary>
/// Создание условий окончания работы прослушивания очереди
/// </summary>
public virtual void Stop()
{
_stopped = true;
_waitLog.Set();
}
}
/// <summary>
/// Затычка логирования (может понадобиться для тестирования)
/// </summary>
public class LoggingQueueMock: LoggingQueue
{
//protected override void DoLoggingListen()
//{
//}
public override void AddAction(Action action)
{
}
protected override void StartThread()
{
}
}
Кстати, можно что-то придумать и для затыкания LoggingHelper.Now. Потому что привязка кода к ресурсам системного времени - это совсем не TDD.
Критика просто жизненно необходима :)
PS Беда какая-то с описанием темы. Ломается.
Комментарии (11)
Вход или Создать аккаунт, Войдите или зарегистрируйтесь, чтобы оставить комментарий
Начнем с того, что обычно формулируют проблему (типа как перед чтением стихов произносят название произведения и автора). Как решалась проблема?
Для примера. Я выделял логирование в отдельный поток, чтобы разгрузить основной поток торговли. Была проблема с перформансом логирования в файл, была решена с помощью параллельного потока.
Логирование включает файловые и другие системные операции. Это в любом случае замедляет выполнение основных алгоритмов. При том, что логирование - операция как раз совершенно не критичная к времени запуска, в отличие от торговых операций. Это не решение проблемы, это - архитектурная плюшка (о чём и сказано в комментарии темы). PS Должно было быть указано, но почему-то не было. Исправил :)
Так ведь сейчас уже в 4.1 логирование отделено от основного потока.
LogManager.cs Это к слову о важности взаимодействия.
Да и вообще была проблема того что блокирование потока логов приводило к блокированию всей системы. :). С такой плюшкой думаю сего не повторится.
Ага. Понял. Пишет пачками по таймеру (вспомнил, почему у меня пропадало сообщение о завершении работы). И всё-таки лочит основной поток при добавлении сообщения в список. Очередь была бы производительнее.
Кстати. У LogManager есть такой косяк: когда количество сообщений превышает заданный максимум (т.е. как раз когда система очень сильно нагружена), логирование запускается в потоке вызова.
Это было сделано для того, чтобы очередь не отжирала всю память.
Помимо этого есть режим MaxMessages=-1 когда логирование идет синхронно. Это сделано для того чтобы производить отладку. Иначе получится что вы уже на бреакпойнт в коде робота встали, а логи еще в файл не записались.
Изменил буфер сообщений на конкурентную очередь, убрал все локи, принудительный запуск логирования заменил перещёлкиванием таймера. Последнее исправление не тестировал.