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