//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();
}
}
}