##// END OF EJS Templates
improved tracing, TextListenerBase can be bound to logical operation scope.
cin -
r43:7c2369f580b8 default
parent child
Show More
@@ -1,24 +1,34
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5
5
6 namespace Implab.Diagnostics {
6 namespace Implab.Diagnostics {
7 public class ConsoleTraceListener: TextListenerBase {
7 public class ConsoleTraceListener: TextListenerBase {
8
8
9 static readonly object _consoleLock = new object();
9 static readonly object _consoleLock = new object();
10
10
11 public ConsoleTraceListener()
12 : base(true) {
13
14 }
15
16 public ConsoleTraceListener(bool local)
17 : base(local) {
18
19 }
20
11 protected override void WriteEntry(TraceContext context, EventText text) {
21 protected override void WriteEntry(TraceContext context, EventText text) {
12 var msg = new StringBuilder();
22 var msg = new StringBuilder();
13
23
14 for (int i = 0; i < text.indent; i++)
24 for (int i = 0; i < text.indent; i++)
15 msg.Append(" ");
25 msg.Append(" ");
16 msg.AppendFormat("[{0}]: {1}", context.ThreadId, text.content);
26 msg.AppendFormat("[{0}]: {1}", context.ThreadId, text.content);
17
27
18 lock (_consoleLock) {
28 lock (_consoleLock) {
19 Console.ForegroundColor = (ConsoleColor)(context.ThreadId % 15 + 1);
29 Console.ForegroundColor = (ConsoleColor)(context.ThreadId % 15 + 1);
20 Console.WriteLine(msg.ToString());
30 Console.WriteLine(msg.ToString());
21 }
31 }
22 }
32 }
23 }
33 }
24 }
34 }
@@ -1,44 +1,44
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.IO;
3 using System.IO;
4 using System.Linq;
4 using System.Linq;
5 using System.Text;
5 using System.Text;
6
6
7 namespace Implab.Diagnostics {
7 namespace Implab.Diagnostics {
8 public class TextFileListener: TextListenerBase {
8 public class TextFileListener: TextListenerBase {
9 readonly TextWriter m_textWriter;
9 readonly TextWriter m_textWriter;
10
10
11 public TextFileListener(string fileName) {
11 public TextFileListener(string fileName) : base(true) {
12 m_textWriter = File.CreateText(fileName);
12 m_textWriter = File.CreateText(fileName);
13
13
14 m_textWriter.WriteLine("LOG {0}", DateTime.Now);
14 m_textWriter.WriteLine("LOG {0}", DateTime.Now);
15 Register(this);
15 Register(this);
16 }
16 }
17
17
18 protected override void WriteEntry(TraceContext context, EventText text) {
18 protected override void WriteEntry(TraceContext context, EventText text) {
19 var msg = new StringBuilder();
19 var msg = new StringBuilder();
20 for (int i = 0; i < text.indent; i++)
20 for (int i = 0; i < text.indent; i++)
21 msg.Append(" ");
21 msg.Append(" ");
22 msg.AppendFormat("[{0}]: {1}", context.ThreadId, text.content);
22 msg.AppendFormat("[{0}]: {1}", context.ThreadId, text.content);
23
23
24 lock (m_textWriter) {
24 lock (m_textWriter) {
25 if (!IsDisposed) {
25 if (!IsDisposed) {
26 m_textWriter.WriteLine(msg.ToString());
26 m_textWriter.WriteLine(msg.ToString());
27 m_textWriter.Flush();
27 m_textWriter.Flush();
28 }
28 }
29 }
29 }
30 }
30 }
31
31
32
32
33 protected override void Dispose(bool disposing) {
33 protected override void Dispose(bool disposing) {
34 base.Dispose(disposing);
34 base.Dispose(disposing);
35 if (disposing) {
35 if (disposing) {
36 lock (m_textWriter) {
36 lock (m_textWriter) {
37 Safe.Dispose(m_textWriter);
37 Safe.Dispose(m_textWriter);
38 }
38 }
39 }
39 }
40 }
40 }
41
41
42
42
43 }
43 }
44 }
44 }
@@ -1,99 +1,119
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5
5
6 namespace Implab.Diagnostics {
6 namespace Implab.Diagnostics {
7 public abstract class TextListenerBase : ServiceLocator, IEventTextFormatter<object>, IEventTextFormatter<TraceEvent> {
7 public abstract class TextListenerBase : ServiceLocator, IEventTextFormatter<object>, IEventTextFormatter<TraceEvent> {
8
8
9 readonly Dictionary<object, Action> m_subscriptions = new Dictionary<object, Action>();
9 readonly Dictionary<object, Action> m_subscriptions = new Dictionary<object, Action>();
10 readonly LogicalOperation m_boundOperation;
11 readonly int m_baseIndent;
10
12
11 protected TextListenerBase() {
13 protected TextListenerBase(bool local) {
12 Register(this);
14 Register(this);
15 if (local) {
16 m_boundOperation = TraceContext.Current.CurrentOperation;
17 m_baseIndent = Math.Max(0, m_boundOperation.Level - 1);
18 }
13 }
19 }
14
20
15 public void Subscribe(Type eventType) {
21 public void Subscribe(Type eventType) {
16 if (eventType == null)
22 if (eventType == null)
17 throw new ArgumentNullException("eventType");
23 throw new ArgumentNullException("eventType");
18 GetType().GetMethod("Subscribe", new Type[0]).MakeGenericMethod(eventType).Invoke(this, null);
24 GetType().GetMethod("Subscribe", new Type[0]).MakeGenericMethod(eventType).Invoke(this, null);
19 }
25 }
20
26
21 public void Subscribe<TEvent>() {
27 public void Subscribe<TEvent>() {
22 Subscribe<TEvent>(LogChannel<TEvent>.Default);
28 Subscribe<TEvent>(LogChannel<TEvent>.Default);
23 }
29 }
24
30
25 public void Subscribe<TEvent>(LogChannel<TEvent> channel) {
31 public void Subscribe<TEvent>(LogChannel<TEvent> channel) {
26 if (channel == null)
32 if (channel == null)
27 throw new ArgumentNullException("channel");
33 throw new ArgumentNullException("channel");
28
34
29 lock (m_subscriptions) {
35 lock (m_subscriptions) {
30 AssertNotDisposed();
36 AssertNotDisposed();
31
37
32 var formatter = GetService<IEventTextFormatter<TEvent>>();
38 var formatter = GetService<IEventTextFormatter<TEvent>>();
33
39
34 EventHandler<ValueEventArgs<TEvent>> handler = (sender, args) => {
40 EventHandler<ValueEventArgs<TEvent>> handler = (sender, args) => {
35 WriteEntry((TraceContext)sender, formatter.Format((TraceContext)sender, args.Value));
41 TraceContext context = (TraceContext)sender;
42 var text = formatter.Format(context, args.Value);
43 text.indent -= m_baseIndent;
44
45 if (IsRelated(context.CurrentOperation))
46 WriteEntry(context, text);
36 };
47 };
37
48
38 if (m_subscriptions.ContainsKey(channel))
49 if (m_subscriptions.ContainsKey(channel))
39 return;
50 return;
40
51
41 channel.Events += handler;
52 channel.Events += handler;
42
53
43 Action unsubscribe = () => {
54 Action unsubscribe = () => {
44 channel.Events -= handler;
55 channel.Events -= handler;
45 };
56 };
46
57
47 m_subscriptions.Add(channel, unsubscribe);
58 m_subscriptions.Add(channel, unsubscribe);
48 }
59 }
49 }
60 }
50
61
62 public bool IsRelated(LogicalOperation op) {
63 if (m_boundOperation == null)
64 return true;
65
66 while (op != m_boundOperation && op.Level > m_boundOperation.Level)
67 op = op.Parent;
68 return op == m_boundOperation;
69 }
70
51 public void Unsubscribe<TEvent>(LogChannel<TEvent> channel) {
71 public void Unsubscribe<TEvent>(LogChannel<TEvent> channel) {
52 if (channel == null)
72 if (channel == null)
53 throw new ArgumentNullException("channel");
73 throw new ArgumentNullException("channel");
54
74
55 lock (m_subscriptions) {
75 lock (m_subscriptions) {
56 Action subscription;
76 Action subscription;
57 if (m_subscriptions.TryGetValue(channel, out subscription)) {
77 if (m_subscriptions.TryGetValue(channel, out subscription)) {
58 subscription();
78 subscription();
59 m_subscriptions.Remove(channel);
79 m_subscriptions.Remove(channel);
60 }
80 }
61 }
81 }
62 }
82 }
63
83
64 public void UnsubscribeAll() {
84 public void UnsubscribeAll() {
65 lock (m_subscriptions) {
85 lock (m_subscriptions) {
66 foreach (var subscription in m_subscriptions.Values)
86 foreach (var subscription in m_subscriptions.Values)
67 subscription();
87 subscription();
68 m_subscriptions.Clear();
88 m_subscriptions.Clear();
69 }
89 }
70 }
90 }
71
91
72 protected abstract void WriteEntry(TraceContext context, EventText text);
92 protected abstract void WriteEntry(TraceContext context, EventText text);
73
93
74 public EventText Format(TraceContext context, object data) {
94 public EventText Format(TraceContext context, object data) {
75 return new EventText {
95 return new EventText {
76 indent = context.CurrentOperation.Level,
96 indent = context.CurrentOperation.Level,
77 content = data.ToString()
97 content = data.ToString()
78 };
98 };
79 }
99 }
80
100
81 public EventText Format(TraceContext context, TraceEvent data) {
101 public EventText Format(TraceContext context, TraceEvent data) {
82 var level = context.CurrentOperation.Level;
102 var level = context.CurrentOperation.Level;
83 if (data.EventType == TraceEventType.OperationCompleted || data.EventType == TraceEventType.OperationStarted)
103 if (data.EventType == TraceEventType.OperationCompleted || data.EventType == TraceEventType.OperationStarted)
84 level--;
104 level--;
85
105
86 return new EventText {
106 return new EventText {
87 indent = level,
107 indent = level,
88 content = data.ToString()
108 content = data.ToString()
89 };
109 };
90 }
110 }
91
111
92 protected override void Dispose(bool disposing) {
112 protected override void Dispose(bool disposing) {
93 if (disposing) {
113 if (disposing) {
94 UnsubscribeAll();
114 UnsubscribeAll();
95 }
115 }
96 base.Dispose(disposing);
116 base.Dispose(disposing);
97 }
117 }
98 }
118 }
99 }
119 }
@@ -1,163 +1,172
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5 using System.Threading;
5 using System.Threading;
6 using System.Threading.Tasks;
6 using System.Threading.Tasks;
7
7
8 namespace Implab.Diagnostics {
8 namespace Implab.Diagnostics {
9 /// <summary>
9 /// <summary>
10 /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
10 /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
11 /// </summary>
11 /// </summary>
12 /// <remarks>
12 /// <remarks>
13 /// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
13 /// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
14 /// </remarks>
14 /// </remarks>
15 public class TraceContext {
15 public class TraceContext {
16 LogicalOperation m_currentOperation;
16 LogicalOperation m_currentOperation;
17 readonly LogicalOperation m_bound;
17 readonly LogicalOperation m_bound;
18 readonly int m_threadId;
18 readonly int m_threadId;
19
19
20 [ThreadStatic]
20 [ThreadStatic]
21 static TraceContext _current;
21 static TraceContext _current;
22
22
23 /// <summary>
23 /// <summary>
24 /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
24 /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
25 /// </summary>
25 /// </summary>
26 public static TraceContext Current {
26 public static TraceContext Current {
27 get {
27 get {
28 if (_current == null)
28 if (_current == null)
29 _current = new TraceContext();
29 _current = new TraceContext();
30 return _current;
30 return _current;
31 }
31 }
32 }
32 }
33
33
34 TraceContext(TraceContext context) {
34 TraceContext(TraceContext context) {
35 if (context == null)
35 if (context == null)
36 throw new ArgumentNullException("context");
36 throw new ArgumentNullException("context");
37
37
38 m_currentOperation = context.CurrentOperation;
38 m_currentOperation = context.CurrentOperation;
39 m_bound = context.CurrentOperation;
39 m_bound = context.CurrentOperation;
40 m_threadId = Thread.CurrentThread.ManagedThreadId;
40 m_threadId = Thread.CurrentThread.ManagedThreadId;
41 }
41 }
42
42
43 TraceContext() {
43 TraceContext() {
44 m_currentOperation = new LogicalOperation();
44 m_currentOperation = new LogicalOperation();
45 m_bound = m_currentOperation;
45 m_bound = m_currentOperation;
46 m_threadId = Thread.CurrentThread.ManagedThreadId;
46 m_threadId = Thread.CurrentThread.ManagedThreadId;
47 }
47 }
48
48
49 /// <summary>
49 /// <summary>
50 /// При необходимости копирует состояние контекста трассивровки в текущий поток.
50 /// При необходимости копирует состояние контекста трассивровки в текущий поток.
51 /// </summary>
51 /// </summary>
52 /// <param name="from">Исходный контекст трассировки, который передается.</param>
52 /// <param name="from">Исходный контекст трассировки, который передается.</param>
53 /// <remarks>
53 /// <remarks>
54 /// <para>
54 /// <para>
55 /// Копирование происходит за счет создания нового контекста трассировки и заполнением его
55 /// Копирование происходит за счет создания нового контекста трассировки и заполнением его
56 /// состояния из переданного контекста. При этом копируется стек операций, однако в новом
56 /// состояния из переданного контекста. При этом копируется стек операций, однако в новом
57 /// контексте ранее начатые логические операции не могут быть завершены.
57 /// контексте ранее начатые логические операции не могут быть завершены.
58 /// </para>
58 /// </para>
59 /// <para>
59 /// <para>
60 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Transfer"/>.
60 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Transfer"/>.
61 /// </para>
61 /// </para>
62 /// </remarks>
62 /// </remarks>
63 public static void Transfer(TraceContext from) {
63 public static void Transfer(TraceContext from) {
64 if (_current == from)
64 if (_current == from)
65 return;
65 return;
66 if (from != null) {
66 if (from != null) {
67 var context = new TraceContext(from);
67 var context = new TraceContext(from);
68 context.LogEvent(TraceEventType.Transfer, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
68 context.LogEvent(TraceEventType.Transfer, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
69 _current = context;
69 _current = context;
70 } else {
70 } else {
71 _current = new TraceContext();
71 _current = new TraceContext();
72 }
72 }
73 }
73 }
74
74
75 /// <summary>
75 /// <summary>
76 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Transfer(TraceContext)"/>
76 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Transfer(TraceContext)"/>
77 /// </summary>
77 /// </summary>
78 /// <returns>Копия текущего контекста трассировки.</returns>
78 /// <returns>Копия текущего контекста трассировки.</returns>
79 public static TraceContext Snapshot() {
79 public static TraceContext Snapshot() {
80 return _current == null ? new TraceContext() : new TraceContext(_current);
80 return _current == null ? new TraceContext() : new TraceContext(_current);
81 }
81 }
82
82
83 /// <summary>
83 /// <summary>
84 /// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока.
84 /// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока.
85 /// </summary>
85 /// </summary>
86 /// <param name="action"></param>
86 /// <param name="action"></param>
87 public void Invoke(Action action) {
87 public void Invoke(Action action) {
88 if (action == null)
88 if (action == null)
89 throw new ArgumentNullException("action");
89 throw new ArgumentNullException("action");
90 var old = _current;
90 var old = _current;
91 Transfer(this);
91 Transfer(this);
92 try {
92 try {
93 action();
93 action();
94 } finally {
94 } finally {
95 _current.EndAllOperations();
95 _current = old;
96 _current = old;
96 }
97 }
97 }
98 }
98
99
99 /// <summary>
100 /// <summary>
100 /// Текущая логическая операция.
101 /// Текущая логическая операция.
101 /// </summary>
102 /// </summary>
102 public LogicalOperation CurrentOperation {
103 public LogicalOperation CurrentOperation {
103 get {
104 get {
104 return m_currentOperation;
105 return m_currentOperation;
105 }
106 }
106 }
107 }
107
108
108 /// <summary>
109 /// <summary>
109 /// Операция ниже которой нельзя опускаться в стеке логических операций, т.е. она не может быть завершена в текущем контексте.
110 /// Операция ниже которой нельзя опускаться в стеке логических операций, т.е. она не может быть завершена в текущем контексте.
110 /// </summary>
111 /// </summary>
111 public LogicalOperation BoundOperation {
112 public LogicalOperation BoundOperation {
112 get {
113 get {
113 return m_bound;
114 return m_bound;
114 }
115 }
115 }
116 }
116
117
117 /// <summary>
118 /// <summary>
118 /// Поток, в котором создан контекст трассировки.
119 /// Поток, в котором создан контекст трассировки.
119 /// </summary>
120 /// </summary>
120 public int ThreadId {
121 public int ThreadId {
121 get {
122 get {
122 return m_threadId;
123 return m_threadId;
123 }
124 }
124 }
125 }
125
126
126 /// <summary>
127 /// <summary>
127 /// Начинает безымянную логическую операцию.
128 /// Начинает безымянную логическую операцию.
128 /// </summary>
129 /// </summary>
129 public void StartLogicalOperation() {
130 public void StartLogicalOperation() {
130 StartLogicalOperation(null);
131 StartLogicalOperation(null);
131 }
132 }
132
133
133 /// <summary>
134 /// <summary>
134 /// Начинает логическую операцию с указанным именем. Созданная операция будет добвалена в стек логических операций контекста, затем будет создано соответсвующее событие.
135 /// Начинает логическую операцию с указанным именем. Созданная операция будет добвалена в стек логических операций контекста, затем будет создано соответсвующее событие.
135 /// </summary>
136 /// </summary>
136 /// <param name="name">Имя начинаемой операции.</param>
137 /// <param name="name">Имя начинаемой операции.</param>
137 public void StartLogicalOperation(string name) {
138 public void StartLogicalOperation(string name) {
138 m_currentOperation = new LogicalOperation(name, m_currentOperation);
139 m_currentOperation = new LogicalOperation(name, m_currentOperation);
139 LogEvent(TraceEventType.OperationStarted, name);
140 LogEvent(TraceEventType.OperationStarted, name);
140 }
141 }
141
142
142 /// <summary>
143 /// <summary>
143 /// Заканчивает логическую операцию начатую в текущем контексте. Операции, начатые в других контекстах не могут быть закончены в текущем контексте.
144 /// Заканчивает логическую операцию начатую в текущем контексте. Операции, начатые в других контекстах не могут быть закончены в текущем контексте.
144 /// </summary>
145 /// </summary>
145 /// <remarks>
146 /// <remarks>
146 /// При вызове данного метода создается событие журнала трассировки, либо о завершении операции, либо об ошибки, поскольку данная операция
147 /// При вызове данного метода создается событие журнала трассировки, либо о завершении операции, либо об ошибки, поскольку данная операция
147 /// начата в другом контексте.
148 /// начата в другом контексте.
148 /// </remarks>
149 /// </remarks>
149 public void EndLogicalOperation() {
150 public void EndLogicalOperation() {
150 if (m_bound == m_currentOperation) {
151 if (m_bound == m_currentOperation) {
151 LogEvent(TraceEventType.Error, "Trying to end the operation which isn't belongs to current trace");
152 LogEvent(TraceEventType.Error, "Trying to end the operation which isn't belongs to current trace");
152 } else {
153 } else {
153 var op = m_currentOperation;
154 var op = m_currentOperation;
154 LogEvent(TraceEventType.OperationCompleted, "{0} {1} ms", op.Name, op.Duration);
155 LogEvent(TraceEventType.OperationCompleted, "{0} {1} ms", op.Name, op.Duration);
155 m_currentOperation = m_currentOperation.Parent;
156 m_currentOperation = m_currentOperation.Parent;
156 }
157 }
157 }
158 }
158
159
160 /// <summary>
161 /// Заврешает все начатые в этом контексте операции
162 /// </summary>
163 public void EndAllOperations() {
164 while (m_bound != m_currentOperation)
165 EndLogicalOperation();
166 }
167
159 void LogEvent(TraceEventType type, string format, params object[] args) {
168 void LogEvent(TraceEventType type, string format, params object[] args) {
160 LogChannel<TraceEvent>.Default.LogEvent(this, TraceEvent.Create(type, format, args));
169 LogChannel<TraceEvent>.Default.LogEvent(this, TraceEvent.Create(type, format, args));
161 }
170 }
162 }
171 }
163 }
172 }
@@ -1,31 +1,31
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5
5
6 namespace Implab.Diagnostics {
6 namespace Implab.Diagnostics {
7 public class TraceEvent {
7 public class TraceEvent {
8 public string Message {
8 public string Message {
9 get;
9 get;
10 private set;
10 private set;
11 }
11 }
12
12
13 public TraceEventType EventType {
13 public TraceEventType EventType {
14 get;
14 get;
15 private set;
15 private set;
16 }
16 }
17
17
18 public TraceEvent(TraceEventType type, string message) {
18 public TraceEvent(TraceEventType type, string message) {
19 EventType = type;
19 EventType = type;
20 Message = message;
20 Message = message;
21 }
21 }
22
22
23 public override string ToString() {
23 public override string ToString() {
24 return String.Format("{0}: {1}", EventType, Message);
24 return String.Format("{0}: {1}", EventType, Message);
25 }
25 }
26
26
27 public static TraceEvent Create(TraceEventType type, string format, params object[] args) {
27 public static TraceEvent Create(TraceEventType type, string format, params object[] args) {
28 return new TraceEvent(type, String.Format(format ?? String.Empty, args));
28 return new TraceEvent(type, format == null ? String.Empty : String.Format(format, args));
29 }
29 }
30 }
30 }
31 }
31 }
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
General Comments 0
You need to be logged in to leave comments. Login now