← Назад

Последовательное логирование в отдельном потоке

Ещё одна доработка исходников: Разгрузка основного потока от логирования. Возможность изоляции от системных ресурсов, связанных с логированием.

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)

Вход или Создать аккаунт, Войдите или зарегистрируйтесь, чтобы оставить комментарий

Mikhail Sukhov
Mikhail Sukhov

VassilSanych: Критика просто жизненно необходима :)

Начнем с того, что обычно формулируют проблему (типа как перед чтением стихов произносят название произведения и автора). Как решалась проблема?

Для примера. Я выделял логирование в отдельный поток, чтобы разгрузить основной поток торговли. Была проблема с перформансом логирования в файл, была решена с помощью параллельного потока.

VassilSanych
VassilSanych Автор

Логирование включает файловые и другие системные операции. Это в любом случае замедляет выполнение основных алгоритмов. При том, что логирование - операция как раз совершенно не критичная к времени запуска, в отличие от торговых операций. Это не решение проблемы, это - архитектурная плюшка (о чём и сказано в комментарии темы). PS Должно было быть указано, но почему-то не было. Исправил :)

Mikhail Sukhov
Mikhail Sukhov

VassilSanych: Логирование включает файловые и другие системные операции. Это в любом случае замедляет выполнение основных алгоритмов. При том, что логирование - операция как раз совершенно не критичная к времени запуска, в отличие от торговых операций. Это не решение проблемы, это - архитектурная плюшка (о чём и сказано в комментарии темы). PS Должно было быть указано, но почему-то не было. Исправил :)

Так ведь сейчас уже в 4.1 логирование отделено от основного потока.

VassilSanych
VassilSanych Автор

Mikhail Sukhov: Так ведь сейчас уже в 4.1 логирование отделено от основного потока. Где? Я не нашёл.

Mikhail Sukhov
Mikhail Sukhov

VassilSanych:

Mikhail Sukhov: Так ведь сейчас уже в 4.1 логирование отделено от основного потока. Где? Я не нашёл.

LogManager.cs Это к слову о важности взаимодействия.

ra81
ra81

Да и вообще была проблема того что блокирование потока логов приводило к блокированию всей системы. :). С такой плюшкой думаю сего не повторится.

VassilSanych
VassilSanych Автор

Ага. Понял. Пишет пачками по таймеру (вспомнил, почему у меня пропадало сообщение о завершении работы). И всё-таки лочит основной поток при добавлении сообщения в список. Очередь была бы производительнее.

VassilSanych
VassilSanych Автор

Кстати. У LogManager есть такой косяк: когда количество сообщений превышает заданный максимум (т.е. как раз когда система очень сильно нагружена), логирование запускается в потоке вызова.

pyhta4og
pyhta4og

VassilSanych: Кстати. У LogManager есть такой косяк: когда количество сообщений превышает заданный максимум (т.е. как раз когда система очень сильно нагружена), логирование запускается в потоке вызова.

Это было сделано для того, чтобы очередь не отжирала всю память.

Помимо этого есть режим MaxMessages=-1 когда логирование идет синхронно. Это сделано для того чтобы производить отладку. Иначе получится что вы уже на бреакпойнт в коде робота встали, а логи еще в файл не записались.

VassilSanych
VassilSanych Автор

pyhta4og: Это было сделано для того, чтобы очередь не отжирала всю память. Это понятно. Вызов я не отменил, а заменил его пинанием таймера. pyhta4og: Помимо этого есть режим MaxMessages=-1 когда логирование идет синхронно. Это сделано для того чтобы производить отладку. Иначе получится что вы уже на бреакпойнт в коде робота встали, а логи еще в файл не записались. Для отладки нужно не костыли городить, а, например, сделать класс-заглушку, которая будет писать, куда надо и как надо, или просто сделать явный ключ bool OneThread.

VassilSanych
VassilSanych Автор

Изменил буфер сообщений на конкурентную очередь, убрал все локи, принудительный запуск логирования заменил перещёлкиванием таймера. Последнее исправление не тестировал.