##// 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:

r52:edf0bc558596 default
r62:62b440d46313 default
Show More
TraceContext.cs
238 lines | 11.2 KiB | text/x-csharp | CSharpLexer
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Implab.Diagnostics {
/// <summary>
/// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
/// </summary>
/// <remarks>
/// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
/// </remarks>
public class TraceContext {
LogicalOperation m_currentOperation;
readonly LogicalOperation m_bound;
readonly int m_threadId;
[ThreadStatic]
static TraceContext _current;
/// <summary>
/// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
/// </summary>
public static TraceContext Current {
get {
if (_current == null) {
_current = new TraceContext();
_current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
}
return _current;
}
}
TraceContext(TraceContext context)
: this(context, false) {
}
TraceContext(TraceContext context, bool attach) {
if (context == null)
throw new ArgumentNullException("context");
m_currentOperation = context.CurrentOperation;
m_bound = attach ? context.BoundOperation : context.CurrentOperation;
m_threadId = Thread.CurrentThread.ManagedThreadId;
}
TraceContext() {
m_currentOperation = new LogicalOperation();
m_bound = m_currentOperation;
m_threadId = Thread.CurrentThread.ManagedThreadId;
}
/// <summary>
/// При необходимости копирует состояние контекста трассивровки в текущий поток.
/// </summary>
/// <param name="from">Исходный контекст трассировки, который передается.</param>
/// <remarks>
/// <para>
/// Копирование происходит за счет создания нового контекста трассировки и заполнением его
/// состояния из переданного контекста. При этом копируется стек операций, однако в новом
/// контексте ранее начатые логические операции не могут быть завершены.
/// </para>
/// <para>
/// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
/// </para>
/// </remarks>
public static void Fork(TraceContext from) {
if (_current == from)
return;
if (from != null) {
var context = new TraceContext(from);
context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
_current = context;
} else {
_current = new TraceContext();
}
}
/// <summary>
/// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
/// логические операции в указанном контексте.
/// </summary>
/// <param name="source"></param>
public static void Attach(TraceContext source) {
if (_current == source)
return;
if (source != null) {
var context = new TraceContext(source, true);
context.LogEvent(TraceEventType.Attach, "[{0}]-->[{1}]", source.ThreadId, context.ThreadId);
_current = context;
} else {
_current = new TraceContext();
}
}
/// <summary>
/// Отсоединяет текущий контекст трассировки от потока, для дальнейшей его передачи другому потоку
/// <see cref="Attach(TraceContext)"/>.
/// </summary>
/// <returns>Контекст трассировки потока</returns>
/// <remarks>
/// После отсоединения контекста трассировки от потока, при первом обращении к трассировке в этом
/// потоке будет создан новый контекст.
/// </remarks>
public static TraceContext Detach() {
var context = Current;
context.LogEvent(TraceEventType.Detach, null);
_current = null;
return context;
}
/// <summary>
/// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Fork(TraceContext)"/>
/// </summary>
/// <returns>Копия текущего контекста трассировки.</returns>
public static TraceContext Snapshot() {
return _current == null ? new TraceContext() : new TraceContext(_current,false);
}
/// <summary>
/// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока.
/// </summary>
/// <param name="action"></param>
public void Invoke(Action action) {
if (action == null)
throw new ArgumentNullException("action");
var old = _current;
Fork(this);
try {
action();
} finally {
if(_current != null)
_current.EndAllOperations();
_current = old;
}
}
/// <summary>
/// Текущая логическая операция.
/// </summary>
public LogicalOperation CurrentOperation {
get {
return m_currentOperation;
}
}
/// <summary>
/// Операция ниже которой нельзя опускаться в стеке логических операций, т.е. она не может быть завершена в текущем контексте.
/// </summary>
public LogicalOperation BoundOperation {
get {
return m_bound;
}
}
/// <summary>
/// Поток, в котором создан контекст трассировки.
/// </summary>
public int ThreadId {
get {
return m_threadId;
}
}
/// <summary>
/// Начинает безымянную логическую операцию.
/// </summary>
public void StartLogicalOperation() {
StartLogicalOperation(null);
}
/// <summary>
/// Начинает логическую операцию с указанным именем. Созданная операция будет добвалена в стек логических операций контекста, затем будет создано соответсвующее событие.
/// </summary>
/// <param name="name">Имя начинаемой операции.</param>
public void StartLogicalOperation(string name) {
m_currentOperation = new LogicalOperation(name, m_currentOperation);
LogEvent(TraceEventType.OperationStarted, name);
}
/// <summary>
/// Заканчивает логическую операцию начатую в текущем контексте. Операции, начатые в других контекстах не могут быть закончены в текущем контексте.
/// </summary>
/// <remarks>
/// При вызове данного метода создается событие журнала трассировки, либо о завершении операции, либо об ошибки, поскольку данная операция
/// начата в другом контексте.
/// </remarks>
public void EndLogicalOperation() {
if (m_bound == m_currentOperation) {
LogEvent(TraceEventType.Error, "Trying to end the operation which isn't belongs to current trace");
} else {
var op = m_currentOperation;
LogEvent(TraceEventType.OperationCompleted, "{0} {1} ms", op.Name, op.Duration);
m_currentOperation = m_currentOperation.Parent;
}
}
/// <summary>
/// Создает копию контекста и возвращается на предыдущую операцию в текущем контексте, это позволяет начать операцию в одном потоке, а завершить - в другом.
/// </summary>
/// <returns>Контекст трассировки, который можно присоединить к другому потоку.</returns>
public TraceContext DetachLogicalOperation() {
if (m_bound == m_currentOperation) {
return new TraceContext();
} else {
var detached = new TraceContext(this, true);
m_currentOperation = m_currentOperation.Parent;
return detached;
}
}
public void BindLogicalOperationToPromise(IPromiseBase promise) {
Safe.ArgumentNotNull(promise, "promise");
var ctx = DetachLogicalOperation();
promise.Finally(() => {
var old = _current;
TraceContext.Attach(ctx);
TraceContext.Current.EndLogicalOperation();
_current = old;
});
}
/// <summary>
/// Заврешает все начатые в этом контексте операции
/// </summary>
public void EndAllOperations() {
while (m_bound != m_currentOperation)
EndLogicalOperation();
}
void LogEvent(TraceEventType type, string format, params object[] args) {
LogChannel<TraceEvent>.Default.LogEvent(this, TraceEvent.Create(type, format, args));
}
}
}