|
|
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(IPromise promise) {
|
|
|
Safe.ArgumentNotNull(promise, "promise");
|
|
|
|
|
|
var ctx = DetachLogicalOperation();
|
|
|
promise.Anyway(() => {
|
|
|
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));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|