//LogManager.cs //Copyright (c) 2013 StockSharp LLC, all rights reserved. //This code module is part of StockSharp library. //This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3. //See the file License.txt for the license details. //More info on: http://stocksharp.com using System.Collections.Concurrent; namespace StockSharp.Logging { using System; using System.Collections.Generic; using System.Threading; using Ecng.Collections; using Ecng.Common; using Ecng.Configuration; /// /// Менеджер логирования сообщений, который мониторит событие и перенаправляет сообщения в . /// public class LogManager : Disposable { private sealed class ApplicationReceiver : BaseLogReceiver { public ApplicationReceiver() { Name = TypeHelper.ApplicationName; LogLevel = LogLevels.Info; } } private sealed class LogSourceList : BaseList { private readonly LogManager _parent; public LogSourceList(LogManager parent) { if (parent == null) throw new ArgumentNullException("parent"); _parent = parent; } protected override bool OnAdding(ILogSource item) { item.Log += _parent.SourceLog; return base.OnAdding(item); } protected override bool OnRemoving(ILogSource item) { item.Log -= _parent.SourceLog; return base.OnRemoving(item); } protected override bool OnClearing() { foreach (var item in this) OnRemoving(item); return base.OnClearing(); } } private readonly ConcurrentQueue _pendingMessages = new ConcurrentQueue(); private readonly Timer _flushTimer; /// /// Создать . /// public LogManager() { ConfigManager.TryRegisterService(this); Sources = new LogSourceList(this) { Application, new UnhandledExceptionSource() }; _flushTimer = ThreadingHelper.Timer(Flush); FlushInterval = TimeSpan.FromMilliseconds(500); } private bool _isFlusing; private void Flush() { if (_isFlusing) return; try { // операция не критичная. lock не нужен. Коллекция обеспечивает свою безопасность сама. _isFlusing = true; var msgs = new List(); LogMessage message; while (_pendingMessages.TryDequeue(out message)) { msgs.Add(message); } if (msgs.Count == 0) return; _listeners.Cache.ForEach(l => l.WriteMessages(msgs)); } finally { _isFlusing = false; } } private ILogReceiver _application = new ApplicationReceiver(); /// /// Получатель логов уровня всего приложения. /// public ILogReceiver Application { get { return _application; } set { if (value == null) throw new ArgumentNullException("value"); _application = value; } } private readonly CachedSynchronizedSet _listeners = new CachedSynchronizedSet(); /// /// Логгеры сообщений, приходящие от . /// public IList Listeners { get { return _listeners; } } /// /// Источники логов, у которых слушается событие . /// public IList Sources { get; private set; } /// /// Интервал передачи накопленных от сообщений в . /// По-умолчанию равно 500 млс. /// public TimeSpan FlushInterval { get { return _flushTimer.Interval(); } set { if (value <= TimeSpan.Zero) throw new ArgumentOutOfRangeException("value", value, "Интервал должен быть положительным."); _flushTimer.Interval(value); } } private int _maxMessageCount = 1000; /// /// Максимальное количество накопленных от сообщений, прежде чем они будут отправлены в . /// По умолчанию равно 1000. /// /// Значение 0 означает бесконечные размер буфера. public int MaxMessageCount { get { return _maxMessageCount; } set { if (value < 0) throw new ArgumentOutOfRangeException("value", value, "Количество не может быть отрицательным."); _maxMessageCount = value; } } private void SourceLog(LogMessage message) { if (message == null) throw new ArgumentNullException("message"); if (GetSourceLevel(message.Source) > message.Level) return; _pendingMessages.Enqueue(message); bool needFlush = MaxMessageCount > 0 && _pendingMessages.Count > MaxMessageCount; if (needFlush) _flushTimer.Change(0, FlushInterval.Milliseconds); } private LogLevels GetSourceLevel(ILogSource source) { var level = source.LogLevel; if (level != LogLevels.Inherit) return level; var parent = source.Parent; return parent != null ? GetSourceLevel(parent) : Application.LogLevel; } /// /// Освободить ресурсы. /// protected override void DisposeManaged() { Sources.Clear(); _flushTimer.Dispose(); _listeners.SyncDo(c => { c.ForEach(l => l.DoDispose()); c.Clear(); }); base.DisposeManaged(); } } }