Mercurial > pub > ImplabNet
view Implab/Diagnostics/TraceContext.cs @ 65:653c4e04968b
minor changes
author | cin |
---|---|
date | Mon, 30 Jun 2014 13:55:22 +0400 |
parents | edf0bc558596 |
children | 790e8a997d30 |
line wrap: on
line source
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)); } } }