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