##// END OF EJS Templates
Implab.Diagnostics drafts
cin -
r253:34df34841225 v3.0.1-beta v3
parent child
Show More
@@ -0,0 +1,29
1 using System;
2 using System.Diagnostics;
3
4 namespace Implab.Diagnostics {
5 public class ActivityScope : IDisposable {
6 readonly TraceSource m_source;
7
8 readonly Guid m_prevId;
9
10 readonly string m_activity;
11
12 readonly int m_code;
13
14 internal ActivityScope(TraceSource source, Guid prevId, int code, string activity) {
15 m_source = source;
16 m_prevId = prevId;
17 m_code = code;
18 m_activity = activity;
19 }
20
21
22 public void Dispose() {
23 if (Trace.CorrelationManager.ActivityId != m_prevId)
24 m_source.TraceTransfer(m_code, "Transfer", m_prevId);
25 m_source.TraceEvent(TraceEventType.Stop, 0, m_activity);
26 Trace.CorrelationManager.ActivityId = m_prevId;
27 }
28 }
29 } No newline at end of file
@@ -0,0 +1,17
1 using System;
2 using System.Diagnostics;
3
4 namespace Implab.Diagnostics {
5 public class LogicalOperation {
6 public Stopwatch OperationStopwatch { get; private set; }
7
8 public string Name { get; private set; }
9
10 internal LogicalOperation(string name) {
11 Name = string.IsNullOrEmpty(name) ? "<unnamed>" : name;
12 OperationStopwatch = Stopwatch.StartNew();
13 }
14
15 public override string ToString() => Name;
16 }
17 } No newline at end of file
@@ -0,0 +1,21
1 using System;
2 using System.Diagnostics;
3
4 namespace Implab.Diagnostics {
5 public class LogicalOperationScope : IDisposable {
6 readonly TraceSource m_source;
7
8 readonly LogicalOperation m_operation;
9
10 internal LogicalOperationScope(TraceSource source, LogicalOperation operation) {
11 m_source = source;
12 m_operation = operation;
13 }
14
15 public void Dispose() {
16 m_operation.OperationStopwatch.Stop();
17 Trace.CorrelationManager.StopLogicalOperation();
18 m_source.TraceData(TraceEventType.Information, TraceEventCodes.StopLogicalOperation, m_operation);
19 }
20 }
21 } No newline at end of file
@@ -0,0 +1,114
1 using System;
2 using System.Diagnostics;
3 using System.IO;
4
5 namespace Implab.Diagnostics {
6 public class SimpleTraceListener : TextWriterTraceListener {
7 public SimpleTraceListener() {
8 }
9
10 public SimpleTraceListener(Stream stream) : base(stream) {
11 }
12
13 public SimpleTraceListener(TextWriter writer) : base(writer) {
14 }
15
16 public SimpleTraceListener(string fileName) : base(fileName) {
17 }
18
19 public SimpleTraceListener(Stream stream, string name) : base(stream, name) {
20 }
21
22 public SimpleTraceListener(TextWriter writer, string name) : base(writer, name) {
23 }
24
25 public SimpleTraceListener(string fileName, string name) : base(fileName, name) {
26 }
27
28 public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data) {
29 switch (id) {
30 case TraceEventCodes.StartLogicalOperation:
31 TraceEvent(eventCache, source, eventType, id, "+{0}", data);
32 break;
33 case TraceEventCodes.StopLogicalOperation:
34 TraceEvent(eventCache, source, eventType, id, FormatStopLogicalOperation(data));
35 break;
36 default:
37 TraceEvent(eventCache, source, eventType, id, data?.ToString());
38 break;
39 }
40 }
41
42 string FormatStopLogicalOperation(object data) {
43 if (data is LogicalOperation op) {
44 return string.Format("-{0} ({1})", op, FormatTimespan(op.OperationStopwatch.Elapsed));
45 } else {
46 return data?.ToString();
47 }
48 }
49
50 string FormatTimespan(TimeSpan value) {
51 if (value.TotalSeconds < 10) {
52 return value.Milliseconds.ToString() + "ms";
53 } else if (value.TotalSeconds < 30) {
54 return string.Format("{0:0.###}s", value.TotalSeconds);
55 } else {
56 return value.ToString();
57 }
58 }
59
60 public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, params object[] data) {
61 var prev = IndentLevel;
62 IndentLevel += eventCache.LogicalOperationStack.Count;
63 try {
64 base.TraceData(eventCache, source, eventType, id, data);
65 } finally {
66 IndentLevel = prev;
67 }
68 }
69
70 public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id) {
71 var prev = IndentLevel;
72 IndentLevel += eventCache.LogicalOperationStack.Count;
73 try {
74 base.TraceEvent(eventCache, source, eventType, id);
75 } finally {
76 IndentLevel = prev;
77 }
78 }
79
80 public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args) {
81 TraceEvent(eventCache, source, eventType, id, String.Format(format, args));
82 }
83
84 public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message) {
85 var prev = IndentLevel;
86 IndentLevel += eventCache.LogicalOperationStack.Count;
87 try {
88 LogicalOperation operation = null;
89 if (eventCache.LogicalOperationStack.Count > 0)
90 operation = eventCache.LogicalOperationStack.Peek() as LogicalOperation;
91
92 if (operation != null) {
93 base.TraceData(eventCache, source, eventType, id, FormatTimespan(operation.OperationStopwatch.Elapsed) + ": " + message);
94 } else {
95 base.TraceData(eventCache, source, eventType, id, message);
96 }
97 } finally {
98 IndentLevel = prev;
99 }
100 }
101
102 public override void TraceTransfer(TraceEventCache eventCache, string source, int id, string message, Guid relatedActivityId) {
103 var prev = IndentLevel;
104 IndentLevel += eventCache.LogicalOperationStack.Count;
105 try {
106 base.TraceTransfer(eventCache, source, id, message, relatedActivityId);
107 } finally {
108 IndentLevel = prev;
109 }
110 }
111
112
113 }
114 } No newline at end of file
@@ -0,0 +1,10
1 namespace Implab.Diagnostics {
2 public class TraceEventCodes {
3 public const int EventCodesBase = 1024;
4
5 public const int StartLogicalOperation = EventCodesBase + 1;
6
7 public const int StopLogicalOperation = EventCodesBase + 2;
8
9 }
10 } No newline at end of file
@@ -1,37 +1,29
1 1 using System;
2 2 using System.Diagnostics;
3 3 using System.Threading;
4 4 using Implab.Diagnostics;
5 5 using Xunit;
6 6
7 namespace Implab.Test
8 {
7 namespace Implab.Test {
8 using System.Threading.Tasks;
9 9 using static Trace<UnitTest1>;
10 public class UnitTest1
11 {
10 public class UnitTest1 {
12 11 [Fact]
13 public void Test1()
14 {
15 var listener = new TextWriterTraceListener(Console.Out);
12 public async Task Test1() {
13 var listener = new SimpleTraceListener(Console.Out);
14
16 15 var source = TraceSource;
17 16 source.Switch.Level = SourceLevels.All;
18 17
19 18 source.Listeners.Add(listener);
20 Trace.Listeners.Add(listener);
21 19
22 Trace.WriteLine("Hello!");
23 StartLogicalOperation();
24
25 Trace.WriteLine("Inner");
26 foreach(var x in Trace.CorrelationManager.LogicalOperationStack)
27 Trace.WriteLine($"-{x}");
20 using (var op = LogicalOperation(nameof(Test1)))
21 using (LogicalOperation("InnerOperation")){
22 await Task.Yield();
23 Log("Inner");
24 await Task.Yield();
28 25 Log("source event");
29
30 listener.IndentLevel = 1;
31
32 source.TraceData(TraceEventType.Start, 1, DateTime.Now);
33
34 StopLogicalOperation();
35 26 }
36 27 }
37 28 }
29 }
@@ -1,130 +1,130
1 1 using System;
2 2 using Implab.Parallels;
3 3 using System.Threading;
4 4 using System.Reflection;
5 5 using System.Diagnostics;
6 6
7 7 namespace Implab {
8 8 /// <summary>
9 9 /// Abstract class for creation of custom one-shot thread safe events.
10 10 /// </summary>
11 11 /// <remarks>
12 12 /// <para>
13 13 /// An event is something that should happen in the future and the
14 14 /// triggering of the event causes execution of some pending actions
15 15 /// which are formely event handlers. One-shot events occur only once
16 16 /// and any handler added after the event is triggered should run
17 17 /// without a delay.
18 18 /// </para>
19 19 /// <para>
20 20 /// The lifecycle of the one-shot event is tipically consists of following
21 21 /// phases.
22 22 /// <list>
23 23 /// <description>Pending state. This is the initial state of the event. Any
24 24 /// handler added to the event will be queued for the future execution.
25 25 /// </description>
26 26 /// <description>Transitional state. This is intermediate state between pending
27 27 /// and fulfilled states, during this state internal initialization and storing
28 28 /// of the result occurs.
29 29 /// </description>
30 30 /// <description>Fulfilled state. The event contains the result, all queued
31 31 /// handlers are signalled to run and newly added handlers are executed
32 32 /// immediatelly.
33 33 /// </description>
34 34 /// </list>
35 35 /// </para>
36 36 /// </remarks>
37 37 public abstract class AbstractEvent<THandler> where THandler : class {
38 38 const int PendingState = 0;
39 39
40 40 const int TransitionalState = 1;
41 41
42 42 const int ResolvedState = 2;
43 43
44 44 volatile int m_state;
45 45
46 46 THandler m_handler;
47 47 SimpleAsyncQueue<THandler> m_extraHandlers;
48 48
49 49 public bool IsResolved {
50 50 get {
51 51 return m_state > TransitionalState;
52 52 }
53 53 }
54 54
55 55 #region state managment
56 56 protected bool BeginTransit() {
57 57 return PendingState == Interlocked.CompareExchange(ref m_state, TransitionalState, PendingState);
58 58 }
59 59
60 60 protected void CompleteTransit() {
61 61 #if DEBUG
62 62 if (TransitionalState != Interlocked.CompareExchange(ref m_state, ResolvedState, TransitionalState))
63 63 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
64 64 #else
65 m_state = state;
65 m_state = ResolvedState;
66 66 #endif
67 67 Signal();
68 68 }
69 69
70 70 protected void WaitTransition() {
71 71 if (m_state == TransitionalState) {
72 72 SpinWait spin = new SpinWait();
73 73 do {
74 74 spin.SpinOnce();
75 75 } while (m_state == TransitionalState);
76 76 }
77 77 }
78 78
79 79
80 80 protected abstract void SignalHandler(THandler handler);
81 81
82 82 void Signal() {
83 83 THandler handler;
84 84 while (TryDequeueHandler(out handler))
85 85 SignalHandler(handler);
86 86 }
87 87
88 88 #endregion
89 89
90 90 #region handlers managment
91 91
92 92 protected void AddHandler(THandler handler) {
93 93
94 94 if (IsResolved) {
95 95 // the promise is in the resolved state, just invoke the handler
96 96 SignalHandler(handler);
97 97 } else {
98 98 EnqueueHandler(handler);
99 99
100 100 if (IsResolved && TryDequeueHandler(out handler))
101 101 // if the promise have been resolved while we was adding the handler to the queue
102 102 // we can't guarantee that someone is still processing it
103 103 // therefore we need to fetch a handler from the queue and execute it
104 104 // note that fetched handler may be not the one that we have added
105 105 // even we can fetch no handlers at all :)
106 106 SignalHandler(handler);
107 107 }
108 108
109 109 }
110 110
111 111 void EnqueueHandler(THandler handler) {
112 112 if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) {
113 113 if (m_extraHandlers == null)
114 114 // compare-exchange will protect from loosing already created queue
115 115 Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue<THandler>(), null);
116 116 m_extraHandlers.Enqueue(handler);
117 117 }
118 118 }
119 119
120 120 bool TryDequeueHandler(out THandler handler) {
121 121 handler = Interlocked.Exchange(ref m_handler, null);
122 122 if (handler != null)
123 123 return true;
124 124 return m_extraHandlers != null && m_extraHandlers.TryDequeue(out handler);
125 125 }
126 126
127 127 #endregion
128 128 }
129 129 }
130 130
@@ -1,73 +1,127
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Diagnostics;
4 4 using System.Linq;
5 5 using System.Text;
6 6 using System.Threading.Tasks;
7 7
8 8 namespace Implab.Diagnostics {
9 9 public static class Trace<T> {
10 10
11 11 readonly static TraceSource _traceSource = new TraceSource(typeof(T).Name);
12 12
13 13 public static TraceSource TraceSource {
14 14 get { return _traceSource; }
15 15 }
16 16
17 17 /// <summary>
18 18 /// Starts the logical operation nested to the current operation nested to the current one.
19 19 /// </summary>
20 20 [Conditional("TRACE")]
21 21 public static void StartLogicalOperation() {
22 22 Trace.CorrelationManager.StartLogicalOperation();
23 23
24 24 }
25 25
26 26 /// <summary>
27 27 /// Starts the logical operation with the specified name, this name is usefull in logs.
28 28 /// </summary>
29 29 /// <param name="name">Name.</param>
30 30 [Conditional("TRACE")]
31 31 public static void StartLogicalOperation(string name) {
32 Trace.CorrelationManager.StartLogicalOperation();
32 Trace.CorrelationManager.StartLogicalOperation(name);
33 33 }
34 34
35 35 /// <summary>
36 36 /// Ends the logical operation and restores the previous one.
37 37 /// </summary>
38 38 [Conditional("TRACE")]
39 39 public static void StopLogicalOperation() {
40 40 Trace.CorrelationManager.StopLogicalOperation();
41 41 }
42 42
43 43 /// <summary>
44 44 /// Writes an informational message.
45 45 /// </summary>
46 46 /// <param name="format">Format.</param>
47 47 /// <param name="arguments">Arguments.</param>
48 48 [Conditional("TRACE")]
49 49 public static void Log(string format, params object[] arguments) {
50 TraceSource.TraceEvent(TraceEventType.Information, 1, format, arguments);
50 TraceSource.TraceEvent(TraceEventType.Information, 0, format, arguments);
51 51 }
52 52
53 53 /// <summary>
54 54 /// Writes a warning message.
55 55 /// </summary>
56 56 /// <param name="format">Format.</param>
57 57 /// <param name="arguments">Arguments.</param>
58 58 [Conditional("TRACE")]
59 59 public static void Warn(string format, params object[] arguments) {
60 TraceSource.TraceEvent(TraceEventType.Warning, 1, format, arguments);
60 TraceSource.TraceEvent(TraceEventType.Warning, 0, format, arguments);
61 61 }
62 62
63 63 [Conditional("TRACE")]
64 64 public static void Error(string format, params object[] arguments) {
65 TraceSource.TraceEvent(TraceEventType.Error, 1, format, arguments);
65 TraceSource.TraceEvent(TraceEventType.Error, 0, format, arguments);
66 66 }
67 67
68 68 [Conditional("TRACE")]
69 69 public static void Error(Exception err) {
70 TraceSource.TraceData(TraceEventType.Error, 1, err);
70 TraceSource.TraceData(TraceEventType.Error, 0, err);
71 }
72
73 /// <summary>
74 /// This method save the current activity, and transfers to the specified activity,
75 /// emits <see cref="TraceEventType.Start"/> and returns a scope of the new
76 /// activity.
77 /// </summary>
78 /// <param name="activityName">The name of the new activity/</param>
79 /// <param name="activityId">The identifier of the activity to which
80 /// the control will be transferred</param>
81 /// <returns>A scope of the new activity, dispose it to transfer
82 /// the control back to the original activity.</returns>
83 public static ActivityScope TransferActivity(string activityName, Guid activityId) {
84 var prev = Trace.CorrelationManager.ActivityId;
85
86 TraceSource.TraceTransfer(0, "Transfer", activityId);
87 Trace.CorrelationManager.ActivityId = activityId;
88 TraceSource.TraceEvent(TraceEventType.Start, 0, activityName);
89
90 return new ActivityScope(TraceSource, prev, 0, activityName);
91 }
92
93 /// <summary>
94 /// Emits <see cref="TraceEventType.Start"/> and returns a scope of the
95 /// activity.
96 /// </summary>
97 /// <param name="activityName">The name of the activity to start</param>
98 /// <returns>A scope of the new activity, dispose it to emit
99 /// <see cref="TraceEventType.Stop"/> for the current activity.</returns>
100 public static ActivityScope StartActivity(string activityName) {
101 if (Trace.CorrelationManager.ActivityId == Guid.Empty)
102 Trace.CorrelationManager.ActivityId = Guid.NewGuid();
103
104 var prev = Trace.CorrelationManager.ActivityId;
105
106 TraceSource.TraceEvent(TraceEventType.Start, 0, activityName);
107 return new ActivityScope(TraceSource, prev, 0, activityName);
108 }
109
110 /// <summary>
111 /// Creates new <see cref="LogicalOperation(string)"/> and calls
112 /// to <see cref="CorrelationManager.StartLogicalOperation(object)"/>
113 /// passing the created operation as identity. Calls
114 /// <see cref="TraceSource.TraceData(TraceEventType, int, object)"/>
115 /// to notify listeners on operation start.
116 /// </summary>
117 /// <param name="name">The name of the logical operation.</param>
118 /// <returns>Logical operation scope, disposing it will stop
119 /// logical operation and notify trace listeners.</returns>
120 public static LogicalOperationScope LogicalOperation(string name) {
121 var operation = new LogicalOperation(name);
122 TraceSource.TraceData(TraceEventType.Information, TraceEventCodes.StartLogicalOperation, operation);
123 Trace.CorrelationManager.StartLogicalOperation(operation);
124 return new LogicalOperationScope(TraceSource, operation);
71 125 }
72 126 }
73 127 }
@@ -1,8 +1,18
1 1 <Project Sdk="Microsoft.NET.Sdk">
2 2
3 3 <PropertyGroup>
4 <Authors>Sergey Smirnov</Authors>
5 <Title>Implab library</Title>
6 <Description>Provides some helper clesses like XML serialization helpers, JSON XML reader,
7 JSON pull-parser, ECMA-style promises, lightweight synchonization routines Signal
8 and SharedLock, Trace helpers on top of System.Diagnostics, ObjectPool etc.
9 </Description>
10 <Copyright>2012-2018 Sergey Smirnov</Copyright>
11 <LicenseUrl>https://opensource.org/licenses/BSD-2-Clause</LicenseUrl>
12 <ProjectUrl>https://implab.org</ProjectUrl>
13 <RepositoryUrl>https://hg.implab.org/pub/ImplabNet/</RepositoryUrl>
4 14 <TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
5 15 <FrameworkPathOverride Condition="'$(TargetFramework)'=='net45' and '$(OSTYPE)'=='linux'">/usr/lib/mono/4.5/</FrameworkPathOverride>
6 16 </PropertyGroup>
7 17
8 18 </Project>
1 NO CONTENT: file was removed
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now