##// END OF EJS Templates
Interactive tracing...
cin -
r48:d9d794b61bb9 interactive logger
parent child
Show More
@@ -9,15 +9,14 using System.Windows.Forms;
9 9
10 10 namespace Implab.Diagnostics.Interactive
11 11 {
12 public class InteractiveListener: Disposable
12 public class InteractiveListener: TextListenerBase
13 13 {
14 14 TraceForm m_form;
15 15
16 16 SynchronizationContext m_syncGuiThread;
17 readonly Promise<object> m_guiStarted = new Promise<object>();
17 18
18 19 readonly IPromiseBase m_guiFinished;
19 readonly Promise<object> m_guiStarted = new Promise<object>();
20
21 20 readonly IPromiseBase m_workerFinished = new Promise<object>();
22 21
23 22 readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
@@ -30,17 +29,19 namespace Implab.Diagnostics.Interactive
30 29 bool m_paused;
31 30 readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
32 31
33 public InteractiveListener() {
34 m_guiFinished = AsyncPool.InvokeNewThread(() => {
35 GuiThread();
36 return 0;
37 });
32 public InteractiveListener(bool global) : base(global) {
33 m_guiFinished = AsyncPool.InvokeNewThread(GuiThread);
34 m_workerFinished = AsyncPool.InvokeNewThread(QueueThread);
38 35
39 36 m_guiStarted.Join();
40 37 }
41 38
42 39 void GuiThread() {
43 40 m_form = new TraceForm(); // will create SynchronizationContext
41
42 m_form.PauseEvents += (s,a) => Pause();
43 m_form.ResumeEvents += (s, a) => Resume();
44
44 45 m_syncGuiThread = SynchronizationContext.Current;
45 46 m_guiStarted.Resolve();
46 47 Application.Run();
@@ -93,6 +94,8 namespace Implab.Diagnostics.Interactive
93 94 }
94 95
95 96 void Terminate() {
97 m_exitPending = true;
98 Resume();
96 99 m_syncGuiThread.Post(x => Application.ExitThread(), null);
97 100 }
98 101
@@ -103,5 +106,17 namespace Implab.Diagnostics.Interactive
103 106 }
104 107 base.Dispose(disposing);
105 108 }
109
110 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
111 var item = new TraceViewItem {
112 Indent = text.indent,
113 Message = text.content,
114 Thread = context.ThreadId,
115 Channel = channel,
116 Timestamp = Environment.TickCount
117 };
118
119 Enqueue(item);
106 120 }
107 121 }
122 }
@@ -30,6 +30,7
30 30 this.eventsDataGrid = new System.Windows.Forms.DataGridView();
31 31 this.traceViewItemBindingSource = new System.Windows.Forms.BindingSource(this.components);
32 32 this.threadDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
33 this.Channel = new System.Windows.Forms.DataGridViewTextBoxColumn();
33 34 this.messageDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
34 35 ((System.ComponentModel.ISupportInitialize)(this.eventsDataGrid)).BeginInit();
35 36 ((System.ComponentModel.ISupportInitialize)(this.traceViewItemBindingSource)).BeginInit();
@@ -55,6 +56,7
55 56 this.eventsDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
56 57 this.eventsDataGrid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
57 58 this.threadDataGridViewTextBoxColumn,
59 this.Channel,
58 60 this.messageDataGridViewTextBoxColumn});
59 61 this.eventsDataGrid.DataSource = this.traceViewItemBindingSource;
60 62 dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
@@ -87,6 +89,15
87 89 this.threadDataGridViewTextBoxColumn.ReadOnly = true;
88 90 this.threadDataGridViewTextBoxColumn.Width = 5;
89 91 //
92 // Channel
93 //
94 this.Channel.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCells;
95 this.Channel.DataPropertyName = "Channel";
96 this.Channel.HeaderText = "Channel";
97 this.Channel.Name = "Channel";
98 this.Channel.ReadOnly = true;
99 this.Channel.Width = 79;
100 //
90 101 // messageDataGridViewTextBoxColumn
91 102 //
92 103 this.messageDataGridViewTextBoxColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
@@ -116,6 +127,7
116 127 private System.Windows.Forms.DataGridView eventsDataGrid;
117 128 private System.Windows.Forms.BindingSource traceViewItemBindingSource;
118 129 private System.Windows.Forms.DataGridViewTextBoxColumn threadDataGridViewTextBoxColumn;
130 private System.Windows.Forms.DataGridViewTextBoxColumn Channel;
119 131 private System.Windows.Forms.DataGridViewTextBoxColumn messageDataGridViewTextBoxColumn;
120 132
121 133 }
@@ -13,9 +13,12 namespace Implab.Diagnostics.Interactive
13 13 readonly Dictionary<int, Color> m_threadColors = new Dictionary<int,Color>();
14 14 readonly Random m_rand = new Random();
15 15
16 public event EventHandler PauseEvents;
17
18 public event EventHandler ResumeEvents;
19
16 20 public TraceForm() {
17 21 InitializeComponent();
18
19 22 }
20 23
21 24 protected override void OnFormClosing(FormClosingEventArgs e) {
@@ -26,18 +29,9 namespace Implab.Diagnostics.Interactive
26 29 }
27 30 }
28 31
29 public void AddTraceEvent(int indent, int thread, string message) {
30 traceViewItemBindingSource.Add(new TraceViewItem {
31 Indent = indent,
32 Thread = thread,
33 Message = message,
34 Timestamp = Environment.TickCount
35 });
36
37 }
38
39 32 public void AddTraceEvent(TraceViewItem item) {
40 33 traceViewItemBindingSource.Add(item);
34 eventsDataGrid.FirstDisplayedScrollingRowIndex = eventsDataGrid.RowCount - 1;
41 35 }
42 36
43 37 Color GetThreadColor(int thread) {
@@ -51,6 +45,7 namespace Implab.Diagnostics.Interactive
51 45
52 46 private void eventsDataGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) {
53 47 var data = (TraceViewItem)traceViewItemBindingSource[e.RowIndex];
48 if (e.ColumnIndex == messageDataGridViewTextBoxColumn.Index)
54 49 e.CellStyle.Padding = new Padding(data.Indent * 10,0,0,0);
55 50 e.CellStyle.ForeColor = GetThreadColor(data.Thread);
56 51 }
@@ -117,6 +117,9
117 117 <resheader name="writer">
118 118 <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119 119 </resheader>
120 <metadata name="Channel.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
121 <value>True</value>
122 </metadata>
120 123 <metadata name="traceViewItemBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
121 124 <value>17, 17</value>
122 125 </metadata>
@@ -12,6 +12,7 namespace Implab.Diagnostics.Interactive
12 12 public int Timestamp { get; set; }
13 13 public int Indent { get; set; }
14 14 public int Thread { get; set; }
15 public string Channel { get; set; }
15 16
16 17 public string FormattedMessage {
17 18 get {
@@ -13,17 +13,17 namespace Implab.Diagnostics {
13 13
14 14 }
15 15
16 public ConsoleTraceListener(bool local)
17 : base(local) {
16 public ConsoleTraceListener(bool global)
17 : base(global) {
18 18
19 19 }
20 20
21 protected override void WriteEntry(TraceContext context, EventText text) {
21 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
22 22 var msg = new StringBuilder();
23 23
24 24 for (int i = 0; i < text.indent; i++)
25 25 msg.Append(" ");
26 msg.AppendFormat("[{0}]: {1}", context.ThreadId, text.content);
26 msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
27 27
28 28 lock (_consoleLock) {
29 29 Console.ForegroundColor = (ConsoleColor)(context.ThreadId % 15 + 1);
@@ -4,23 +4,74 using System.Linq;
4 4 using System.Text;
5 5
6 6 namespace Implab.Diagnostics {
7 /// <summary>
8 /// Канал, через который публикуются события журнала.
9 /// </summary>
10 /// <typeparam name="TEvent">Тип событий в канале</typeparam>
11 /// <remarks>
12 /// Событиями журнала могут быть любые типы, например строки, в которых будет передаваться
13 /// информация, или структуры с набором полей, описывающих важность, текст и другую информацию.
14 /// </remarks>
7 15 public class LogChannel<TEvent> {
8 16 static LogChannel<TEvent> _default = new LogChannel<TEvent>();
9 17
18 /// <summary>
19 /// Канал по-умолчанию для событий типа <typeparam name="TEvent"/>.
20 /// </summary>
10 21 public static LogChannel<TEvent> Default {
11 22 get {
12 23 return _default;
13 24 }
14 25 }
15 26
27 /// <summary>
28 /// Событие появление новой записи в журнале, на это событие подписываются слушатели.
29 /// </summary>
16 30 public event EventHandler<ValueEventArgs<TEvent>> Events;
17 31
32 /// <summary>
33 /// Имя канала, полезно для отображения в журнале
34 /// </summary>
35 public string Name {
36 get;
37 private set;
38 }
39
40 /// <summary>
41 /// Создает журнал, имя типа событий назначается в качетве имени канала.
42 /// </summary>
43 public LogChannel()
44 : this(null) {
45 }
46
47 /// <summary>
48 /// Содает канал с указанным именем.
49 /// </summary>
50 /// <param name="name">Имя канала.</param>
51 public LogChannel(string name) {
52 if (String.IsNullOrEmpty(name))
53 name = typeof(TEvent).Name;
54 Name = name;
55 }
56
57 /// <summary>
58 /// Отправляет запись журнала через канал подписчикам.
59 /// </summary>
60 /// <param name="data">Запись журнала.</param>
61 /// <remarks>
62 /// Контекст трассировки от которого рассылается сообщение определяется автоматически из текущего потока.
63 /// </remarks>
18 64 public void LogEvent(TEvent data) {
19 65 var t = Events;
20 66 if (t!= null)
21 67 t(TraceContext.Current,new ValueEventArgs<TEvent>(data));
22 68 }
23 69
70 /// <summary>
71 /// Отправляет запись журнала через канал подписчикам.
72 /// </summary>
73 /// <param name="data">Запись журнала.</param>
74 /// <param name="context">Контекст трассировки от которого рассылается сообщение/</param>
24 75 public void LogEvent(TraceContext context,TEvent data) {
25 76 var t = Events;
26 77 if (t != null)
@@ -8,18 +8,19 namespace Implab.Diagnostics {
8 8 public class TextFileListener: TextListenerBase {
9 9 readonly TextWriter m_textWriter;
10 10
11 public TextFileListener(string fileName, bool local) : base(local) {
11 public TextFileListener(string fileName, bool global)
12 : base(global) {
12 13 m_textWriter = File.CreateText(fileName);
13 14
14 15 m_textWriter.WriteLine("LOG {0}", DateTime.Now);
15 16 Register(this);
16 17 }
17 18
18 protected override void WriteEntry(TraceContext context, EventText text) {
19 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
19 20 var msg = new StringBuilder();
20 21 for (int i = 0; i < text.indent; i++)
21 22 msg.Append(" ");
22 msg.AppendFormat("[{0}]: {1}", context.ThreadId, text.content);
23 msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
23 24
24 25 lock (m_textWriter) {
25 26 if (!IsDisposed) {
@@ -10,9 +10,9 namespace Implab.Diagnostics {
10 10 readonly LogicalOperation m_boundOperation;
11 11 readonly int m_baseIndent;
12 12
13 protected TextListenerBase(bool local) {
13 protected TextListenerBase(bool global) {
14 14 Register(this);
15 if (local) {
15 if (!global) {
16 16 m_boundOperation = TraceContext.Current.CurrentOperation;
17 17 m_baseIndent = Math.Max(0, m_boundOperation.Level - 1);
18 18 }
@@ -36,6 +36,7 namespace Implab.Diagnostics {
36 36 AssertNotDisposed();
37 37
38 38 var formatter = GetService<IEventTextFormatter<TEvent>>();
39 var channelName = channel.Name;
39 40
40 41 EventHandler<ValueEventArgs<TEvent>> handler = (sender, args) => {
41 42 TraceContext context = (TraceContext)sender;
@@ -43,7 +44,7 namespace Implab.Diagnostics {
43 44 text.indent -= m_baseIndent;
44 45
45 46 if (IsRelated(context.CurrentOperation))
46 WriteEntry(context, text);
47 WriteEntry(context, text, channelName);
47 48 };
48 49
49 50 if (m_subscriptions.ContainsKey(channel))
@@ -98,7 +99,7 namespace Implab.Diagnostics {
98 99 /// </remarks>
99 100 /// <param name="context">Контекст трассировки.</param>
100 101 /// <param name="text">Текст сообщения.</param>
101 protected abstract void WriteEntry(TraceContext context, EventText text);
102 protected abstract void WriteEntry(TraceContext context, EventText text, string channel);
102 103
103 104 public EventText Format(TraceContext context, object data) {
104 105 return new EventText {
@@ -25,18 +25,24 namespace Implab.Diagnostics {
25 25 /// </summary>
26 26 public static TraceContext Current {
27 27 get {
28 if (_current == null)
28 if (_current == null) {
29 29 _current = new TraceContext();
30 _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
31 }
30 32 return _current;
31 33 }
32 34 }
33 35
34 TraceContext(TraceContext context) {
36 TraceContext(TraceContext context)
37 : this(context, false) {
38 }
39
40 TraceContext(TraceContext context, bool attach) {
35 41 if (context == null)
36 42 throw new ArgumentNullException("context");
37 43
38 44 m_currentOperation = context.CurrentOperation;
39 m_bound = context.CurrentOperation;
45 m_bound = attach ? context.BoundOperation : context.CurrentOperation;
40 46 m_threadId = Thread.CurrentThread.ManagedThreadId;
41 47 }
42 48
@@ -57,15 +63,15 namespace Implab.Diagnostics {
57 63 /// контексте ранее начатые логические операции не могут быть завершены.
58 64 /// </para>
59 65 /// <para>
60 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Transfer"/>.
66 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
61 67 /// </para>
62 68 /// </remarks>
63 public static void Transfer(TraceContext from) {
69 public static void Fork(TraceContext from) {
64 70 if (_current == from)
65 71 return;
66 72 if (from != null) {
67 73 var context = new TraceContext(from);
68 context.LogEvent(TraceEventType.Transfer, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
74 context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
69 75 _current = context;
70 76 } else {
71 77 _current = new TraceContext();
@@ -73,7 +79,40 namespace Implab.Diagnostics {
73 79 }
74 80
75 81 /// <summary>
76 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Transfer(TraceContext)"/>
82 /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
83 /// логические операции в указанном контексте.
84 /// </summary>
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 }
97
98 /// <summary>
99 /// Отсоединяет текущий контекст трассировки от потока, для дальнейшей его передачи другому потоку
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 }
113
114 /// <summary>
115 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Fork(TraceContext)"/>
77 116 /// </summary>
78 117 /// <returns>Копия текущего контекста трассировки.</returns>
79 118 public static TraceContext Snapshot() {
@@ -88,7 +127,7 namespace Implab.Diagnostics {
88 127 if (action == null)
89 128 throw new ArgumentNullException("action");
90 129 var old = _current;
91 Transfer(this);
130 Fork(this);
92 131 try {
93 132 action();
94 133 } finally {
@@ -11,6 +11,9 namespace Implab.Diagnostics {
11 11 Error,
12 12 OperationStarted,
13 13 OperationCompleted,
14 Transfer
14 Fork,
15 Attach,
16 Detach,
17 Created
15 18 }
16 19 }
@@ -1,10 +1,14
1 using System;
1 using Implab.Diagnostics;
2 using System;
2 3 using System.Collections.Generic;
3 4 using System.Diagnostics;
4 5 using System.Linq;
5 6 using System.Web;
6 7
7 8 namespace Implab {
9 /// <summary>
10 /// Объект, поддерживающий освобождение ресурсов.
11 /// </summary>
8 12 public class Disposable : IDisposable {
9 13
10 14 bool m_disposed;
@@ -19,7 +23,16 namespace Implab {
19 23 if (m_disposed)
20 24 throw new ObjectDisposedException(this.ToString());
21 25 }
22
26 /// <summary>
27 /// Переводит объект в состояние <c>Disposed</c> и вызывает событие <see cref="Disposed"/>
28 /// </summary>
29 /// <param name="disposing">Признак того, что нужно освободить ресурсы, иначе данный метод
30 /// вызван сборщиком мусора и нужно освобождать ТОЛЬКО неуправляемые ресурсы ТОЛЬКО этого
31 /// объекта.</param>
32 /// <remarks>
33 /// Данный метод осуществляет проверку того, что объект уже был освобожден, чтобы не вызывать
34 /// событие <see cref="Disposed"/>. Не поддерживает многопоточность.
35 /// </remarks>
23 36 protected virtual void Dispose(bool disposing) {
24 37 if (disposing && !m_disposed) {
25 38 m_disposed = true;
@@ -34,8 +47,11 namespace Implab {
34 47 GC.SuppressFinalize(this);
35 48 }
36 49
50 /// <summary>
51 /// Записывает сообщение об утечке объекта.
52 /// </summary>
37 53 protected virtual void ReportObjectLeaks() {
38 Trace.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this);
54 TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this);
39 55 }
40 56
41 57 ~Disposable() {
@@ -42,7 +42,7 namespace Implab.Parallels {
42 42 }
43 43
44 44 protected override void Worker() {
45 TraceContext.Transfer(m_traceContext);
45 TraceContext.Fork(m_traceContext);
46 46 base.Worker();
47 47 }
48 48
@@ -99,7 +99,7 namespace Implab.Parallels {
99 99 }
100 100
101 101 protected override void Worker() {
102 TraceContext.Transfer(m_traceContext);
102 TraceContext.Fork(m_traceContext);
103 103 base.Worker();
104 104 }
105 105
@@ -17,7 +17,7 namespace Implab.Parallels {
17 17 var caller = TraceContext.Snapshot();
18 18
19 19 ThreadPool.QueueUserWorkItem(param => {
20 TraceContext.Transfer(caller);
20 TraceContext.Fork(caller);
21 21 try {
22 22 p.Resolve(func());
23 23 } catch(Exception e) {
@@ -34,7 +34,7 namespace Implab.Parallels {
34 34 var caller = TraceContext.Snapshot();
35 35
36 36 var worker = new Thread(() => {
37 TraceContext.Transfer(caller);
37 TraceContext.Fork(caller);
38 38 try {
39 39 p.Resolve(func());
40 40 } catch (Exception e) {
@@ -46,5 +46,26 namespace Implab.Parallels {
46 46
47 47 return p;
48 48 }
49
50
51 public static IPromiseBase InvokeNewThread(Action func) {
52 var p = new Promise<object>();
53
54 var caller = TraceContext.Snapshot();
55
56 var worker = new Thread(() => {
57 TraceContext.Fork(caller);
58 try {
59 func();
60 p.Resolve();
61 } catch (Exception e) {
62 p.Reject(e);
63 }
64 });
65 worker.IsBackground = true;
66 worker.Start();
67
68 return p;
49 69 }
50 70 }
71 }
General Comments 0
You need to be logged in to leave comments. Login now