##// END OF EJS Templates
Added Skip method to JSON parser to skip contents of the current node
Added Skip method to JSON parser to skip contents of the current node

File last commit:

r48:d9d794b61bb9 interactive logger
r62:62b440d46313 default
Show More
TextListenerBase.cs
129 lines | 4.8 KiB | text/x-csharp | CSharpLexer
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Implab.Diagnostics {
public abstract class TextListenerBase : ServiceLocator, IEventTextFormatter<object>, IEventTextFormatter<TraceEvent> {
readonly Dictionary<object, Action> m_subscriptions = new Dictionary<object, Action>();
readonly LogicalOperation m_boundOperation;
readonly int m_baseIndent;
protected TextListenerBase(bool global) {
Register(this);
if (!global) {
m_boundOperation = TraceContext.Current.CurrentOperation;
m_baseIndent = Math.Max(0, m_boundOperation.Level - 1);
}
}
public void Subscribe(Type eventType) {
if (eventType == null)
throw new ArgumentNullException("eventType");
GetType().GetMethod("Subscribe", new Type[0]).MakeGenericMethod(eventType).Invoke(this, null);
}
public void Subscribe<TEvent>() {
Subscribe<TEvent>(LogChannel<TEvent>.Default);
}
public void Subscribe<TEvent>(LogChannel<TEvent> channel) {
if (channel == null)
throw new ArgumentNullException("channel");
lock (m_subscriptions) {
AssertNotDisposed();
var formatter = GetService<IEventTextFormatter<TEvent>>();
var channelName = channel.Name;
EventHandler<ValueEventArgs<TEvent>> handler = (sender, args) => {
TraceContext context = (TraceContext)sender;
var text = formatter.Format(context, args.Value);
text.indent -= m_baseIndent;
if (IsRelated(context.CurrentOperation))
WriteEntry(context, text, channelName);
};
if (m_subscriptions.ContainsKey(channel))
return;
channel.Events += handler;
Action unsubscribe = () => {
channel.Events -= handler;
};
m_subscriptions.Add(channel, unsubscribe);
}
}
public bool IsRelated(LogicalOperation op) {
if (m_boundOperation == null)
return true;
while (op != m_boundOperation && op.Level > m_boundOperation.Level)
op = op.Parent;
return op == m_boundOperation;
}
public void Unsubscribe<TEvent>(LogChannel<TEvent> channel) {
if (channel == null)
throw new ArgumentNullException("channel");
lock (m_subscriptions) {
Action subscription;
if (m_subscriptions.TryGetValue(channel, out subscription)) {
subscription();
m_subscriptions.Remove(channel);
}
}
}
public void UnsubscribeAll() {
lock (m_subscriptions) {
foreach (var subscription in m_subscriptions.Values)
subscription();
m_subscriptions.Clear();
}
}
/// <summary>
/// Вызывается для записи текста сообщения, в журнал.
/// </summary>
/// <remarks>
/// Данный метод может вызваться из разных потоков одновременно. Возможна ситуация, когда
/// данный метод вызывается уже после освобождения ообъекта методом <see cref="Dispose()"/>.
/// </remarks>
/// <param name="context">Контекст трассировки.</param>
/// <param name="text">Текст сообщения.</param>
protected abstract void WriteEntry(TraceContext context, EventText text, string channel);
public EventText Format(TraceContext context, object data) {
return new EventText {
indent = context.CurrentOperation.Level,
content = data.ToString()
};
}
public EventText Format(TraceContext context, TraceEvent data) {
var level = context.CurrentOperation.Level;
if (data.EventType == TraceEventType.OperationCompleted || data.EventType == TraceEventType.OperationStarted)
level--;
return new EventText {
indent = level,
content = data.ToString()
};
}
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
if (disposing) {
UnsubscribeAll();
}
}
}
}