comparison Implab/Diagnostics/TraceContext.cs @ 92:4c0e5ef99986 v2

rewritten tracing
author cin
date Wed, 22 Oct 2014 18:37:56 +0400
parents c761fc982e1d
children dc4942d09e74
comparison
equal deleted inserted replaced
91:cdaaf4792c22 92:4c0e5ef99986
1 using System; 1 using System;
2 using System.Collections.Generic; 2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading; 3 using System.Threading;
6 using System.Threading.Tasks;
7 4
8 namespace Implab.Diagnostics { 5 namespace Implab.Diagnostics {
9 /// <summary> 6 /// <summary>
10 /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций. 7 /// Trace context is bound to the specific thread, each thread has it's own ThreadContext.
11 /// </summary> 8 /// </summary>
12 /// <remarks> 9 /// <remarks>
13 /// Контекст трассировки передается слушателям событий для определения места, где возникло событие. 10 /// ThreadContext manages relations between logical operations and threads.
14 /// </remarks> 11 /// </remarks>
15 public class TraceContext { 12 public class TraceContext {
16 LogicalOperation m_currentOperation; 13
17 readonly LogicalOperation m_bound; 14 [ThreadStatic]
15 static TraceContext _instance;
16
17 OperationContext m_current = OperationContext.EMPTY;
18 readonly Stack<OperationContext> m_stack = new Stack<OperationContext>();
18 readonly int m_threadId; 19 readonly int m_threadId;
19 20
20 [ThreadStatic] 21 public static TraceContext Instance {
21 static TraceContext _current;
22
23 /// <summary>
24 /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
25 /// </summary>
26 public static TraceContext Current {
27 get { 22 get {
28 if (_current == null) { 23 if (_instance == null)
29 _current = new TraceContext(); 24 _instance = new TraceContext();
30 _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId); 25 return _instance;
31 }
32 return _current;
33 } 26 }
34 } 27 }
35 28
36 TraceContext(TraceContext context) 29 public TraceContext() {
37 : this(context, false) {
38 }
39
40 TraceContext(TraceContext context, bool attach) {
41 if (context == null)
42 throw new ArgumentNullException("context");
43
44 m_currentOperation = context.CurrentOperation;
45 m_bound = attach ? context.BoundOperation : context.CurrentOperation;
46 m_threadId = Thread.CurrentThread.ManagedThreadId; 30 m_threadId = Thread.CurrentThread.ManagedThreadId;
47 } 31 }
48 32
49 TraceContext() { 33 public int ThreadId {
50 m_currentOperation = new LogicalOperation(); 34 get { return m_threadId; }
51 m_bound = m_currentOperation;
52 m_threadId = Thread.CurrentThread.ManagedThreadId;
53 } 35 }
54 36
55 /// <summary> 37 public LogicalOperation CurrentOperation {
56 /// При необходимости копирует состояние контекста трассивровки в текущий поток. 38 get {
57 /// </summary> 39 return m_current.CurrentOperation;
58 /// <param name="from">Исходный контекст трассировки, который передается.</param>
59 /// <remarks>
60 /// <para>
61 /// Копирование происходит за счет создания нового контекста трассировки и заполнением его
62 /// состояния из переданного контекста. При этом копируется стек операций, однако в новом
63 /// контексте ранее начатые логические операции не могут быть завершены.
64 /// </para>
65 /// <para>
66 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
67 /// </para>
68 /// </remarks>
69 public static void Fork(TraceContext from) {
70 if (_current == from)
71 return;
72 if (from != null) {
73 var context = new TraceContext(from);
74 context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
75 _current = context;
76 } else {
77 _current = new TraceContext();
78 } 40 }
79 } 41 }
80 42
81 /// <summary> 43 public void EnterLogicalOperation(LogicalOperation operation, bool takeOwnership) {
82 /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые 44 // TODO Emit event
83 /// логические операции в указанном контексте. 45 m_stack.Push(m_current);
84 /// </summary> 46 m_current = new OperationContext(operation, takeOwnership);
85 /// <param name="source"></param>
86 public static void Attach(TraceContext source) {
87 if (_current == source)
88 return;
89 if (source != null) {
90 var context = new TraceContext(source, true);
91 context.LogEvent(TraceEventType.Attach, "[{0}]-->[{1}]", source.ThreadId, context.ThreadId);
92 _current = context;
93 } else {
94 _current = new TraceContext();
95 }
96 } 47 }
97 48
98 /// <summary> 49 public void StartLogicalOperation(string name) {
99 /// Отсоединяет текущий контекст трассировки от потока, для дальнейшей его передачи другому потоку 50 m_current.BeginLogicalOperation(name);
100 /// <see cref="Attach(TraceContext)"/>.
101 /// </summary>
102 /// <returns>Контекст трассировки потока</returns>
103 /// <remarks>
104 /// После отсоединения контекста трассировки от потока, при первом обращении к трассировке в этом
105 /// потоке будет создан новый контекст.
106 /// </remarks>
107 public static TraceContext Detach() {
108 var context = Current;
109 context.LogEvent(TraceEventType.Detach, null);
110 _current = null;
111 return context;
112 } 51 }
113 52
114 /// <summary> 53 public void StartLogicalOperation() {
115 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Fork(TraceContext)"/> 54 // TODO Emit Event
116 /// </summary> 55 m_current.BeginLogicalOperation(String.Empty);
117 /// <returns>Копия текущего контекста трассировки.</returns>
118 public static TraceContext Snapshot() {
119 return _current == null ? new TraceContext() : new TraceContext(_current,false);
120 } 56 }
121 57
122 /// <summary> 58 public void EndLogicalOperation() {
123 /// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока. 59 // TODO Emit event
124 /// </summary> 60 m_current.EndLogicalOperation();
125 /// <param name="action"></param>
126 public void Invoke(Action action) {
127 if (action == null)
128 throw new ArgumentNullException("action");
129 var old = _current;
130 Fork(this);
131 try {
132 action();
133 } finally {
134 if(_current != null)
135 _current.EndAllOperations();
136 _current = old;
137 }
138 } 61 }
139 62
140 /// <summary> 63 public LogicalOperation DetachLogicalOperation() {
141 /// Текущая логическая операция. 64 // TODO Emit event
142 /// </summary> 65 return m_current.DetachLogicalOperation();
143 public LogicalOperation CurrentOperation {
144 get {
145 return m_currentOperation;
146 }
147 } 66 }
148 67
149 /// <summary> 68 public void Leave() {
150 /// Операция ниже которой нельзя опускаться в стеке логических операций, т.е. она не может быть завершена в текущем контексте. 69 // TODO Emit event
151 /// </summary> 70 if (m_stack.Count > 0)
152 public LogicalOperation BoundOperation { 71 m_current = m_stack.Pop();
153 get { 72 else {
154 return m_bound; 73 TraceLog.TraceWarning("Attemtp to leave the last operation context");
74 m_current = OperationContext.EMPTY;
155 } 75 }
156 }
157
158 /// <summary>
159 /// Поток, в котором создан контекст трассировки.
160 /// </summary>
161 public int ThreadId {
162 get {
163 return m_threadId;
164 }
165 }
166
167 /// <summary>
168 /// Начинает безымянную логическую операцию.
169 /// </summary>
170 public void StartLogicalOperation() {
171 StartLogicalOperation(null);
172 }
173
174 /// <summary>
175 /// Начинает логическую операцию с указанным именем. Созданная операция будет добвалена в стек логических операций контекста, затем будет создано соответсвующее событие.
176 /// </summary>
177 /// <param name="name">Имя начинаемой операции.</param>
178 public void StartLogicalOperation(string name) {
179 m_currentOperation = new LogicalOperation(name, m_currentOperation);
180 LogEvent(TraceEventType.OperationStarted, name);
181 }
182
183 /// <summary>
184 /// Заканчивает логическую операцию начатую в текущем контексте. Операции, начатые в других контекстах не могут быть закончены в текущем контексте.
185 /// </summary>
186 /// <remarks>
187 /// При вызове данного метода создается событие журнала трассировки, либо о завершении операции, либо об ошибки, поскольку данная операция
188 /// начата в другом контексте.
189 /// </remarks>
190 public void EndLogicalOperation() {
191 if (m_bound == m_currentOperation) {
192 LogEvent(TraceEventType.Error, "Trying to end the operation which isn't belongs to current trace");
193 } else {
194 var op = m_currentOperation;
195 LogEvent(TraceEventType.OperationCompleted, "{0} {1} ms", op.Name, op.Duration);
196 m_currentOperation = m_currentOperation.Parent;
197 }
198 }
199
200 /// <summary>
201 /// Создает копию контекста и возвращается на предыдущую операцию в текущем контексте, это позволяет начать операцию в одном потоке, а завершить - в другом.
202 /// </summary>
203 /// <returns>Контекст трассировки, который можно присоединить к другому потоку.</returns>
204 public TraceContext DetachLogicalOperation() {
205 if (m_bound == m_currentOperation) {
206 return new TraceContext();
207 } else {
208 var detached = new TraceContext(this, true);
209 m_currentOperation = m_currentOperation.Parent;
210 return detached;
211 }
212 }
213
214 public void BindLogicalOperationToPromise(IPromise promise) {
215 Safe.ArgumentNotNull(promise, "promise");
216
217 var ctx = DetachLogicalOperation();
218 promise.Anyway(() => {
219 var old = _current;
220 TraceContext.Attach(ctx);
221 TraceContext.Current.EndLogicalOperation();
222 _current = old;
223 });
224 }
225
226 /// <summary>
227 /// Заврешает все начатые в этом контексте операции
228 /// </summary>
229 public void EndAllOperations() {
230 while (m_bound != m_currentOperation)
231 EndLogicalOperation();
232 }
233
234 void LogEvent(TraceEventType type, string format, params object[] args) {
235 LogChannel<TraceEvent>.Default.LogEvent(this, TraceEvent.Create(type, format, args));
236 } 76 }
237 } 77 }
238 } 78 }
79