Mercurial > pub > ImplabNet
diff Implab/Diagnostics/TraceContext.cs @ 192:f1da3afc3521 release v2.1
Слияние с v2
author | cin |
---|---|
date | Fri, 22 Apr 2016 13:10:34 +0300 |
parents | 04d4c92d0f28 |
children | ea485487a424 |
line wrap: on
line diff
--- a/Implab/Diagnostics/TraceContext.cs Wed Sep 03 18:34:02 2014 +0400 +++ b/Implab/Diagnostics/TraceContext.cs Fri Apr 22 13:10:34 2016 +0300 @@ -1,238 +1,83 @@ -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.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)); - } - } -} +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Implab.Diagnostics { + /// <summary> + /// Trace context is bound to the specific thread, each thread has it's own ThreadContext. + /// </summary> + /// <remarks> + /// ThreadContext manages relations between logical operations and threads. + /// </remarks> + public class TraceContext { + + [ThreadStatic] + static TraceContext _instance; + + OperationContext m_current = OperationContext.EMPTY; + readonly Stack<OperationContext> m_stack = new Stack<OperationContext>(); + readonly int m_threadId; + + public static TraceContext Instance { + get { + if (_instance == null) + _instance = new TraceContext(); + return _instance; + } + } + + public TraceContext() { + m_threadId = Thread.CurrentThread.ManagedThreadId; + } + + public int ThreadId { + get { return m_threadId; } + } + + public LogicalOperation CurrentOperation { + get { + return m_current.CurrentOperation; + } + } + + public void EnterLogicalOperation(LogicalOperation operation, bool takeOwnership) { + //var prev = CurrentOperation; + //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(takeOwnership ? TraceEventType.Attach : TraceEventType.Enter, String.Format("{0} -> {1}",prev.Name, operation.Name))); + m_stack.Push(m_current); + m_current = new OperationContext(operation, takeOwnership); + } + + public void StartLogicalOperation(string name) { + LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.OperationStarted, name)); + m_current.BeginLogicalOperation(name); + } + + public void StartLogicalOperation() { + StartLogicalOperation(String.Empty); + } + + public void EndLogicalOperation() { + var op = m_current.EndLogicalOperation(); + LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.OperationCompleted, String.Format("-{0} : {1}ms",op.Name, op.Duration))); + } + + public LogicalOperation DetachLogicalOperation() { + var prev = m_current.DetachLogicalOperation(); + //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.Detach, String.Format("{0} -> {1}",prev.Name, CurrentOperation.Name))); + return prev; + } + + public void Leave() { + if (m_stack.Count > 0) { + m_current.Leave(); + //var prev = CurrentOperation; + m_current = m_stack.Pop(); + //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.Leave, String.Format("{0} -> {1}", prev.Name, CurrentOperation.Name))); + } else { + TraceLog.TraceWarning("Attempt to leave the last operation context"); + m_current = OperationContext.EMPTY; + } + } + } +} +