Auto status change to "Under Review"
@@ -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 |
|
|
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 |
|
|
|
24 | ||
|
25 |
|
|
|
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, |
|
|
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, |
|
|
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, |
|
|
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, |
|
|
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
ok, latest stable version should be in default
You need to be logged in to leave comments.
Login now