做為一個開發人員,不管是在維護舊code或是開發新功能的時候常常或有兩個情況
還有一種是QA轉頭就冒一句,欸為什麼這個功能壞掉了
這時候的心理OS是,我又不是開發這個功能的人,你問我不如擲筊還比較準。
當然,心裡想的是一套,實際上還是得要使用通靈之術,從各種蛛絲馬跡中找出可能的原因。
這時候,良好的log就派上用場了。
好的log可以降低你通靈的時間,快速的對焦出問題根源
現在讓我們來看看.Net 中的log機制如何運作的吧
這邊介紹的是原生的log 方式
指的是當應用程式附加到當前偵錯工具(ex. ide(visual studio))時,可以透過Debugger.Log 的方式來log Debugger.Log
在我現在的公司,滿常使用attach to process 將iis process 掛到當前ide去做debug的,這時候debugger算是不錯的log工具(雖然下中斷點比較快)。
也可以用Debug.WriteLine()的寫法
通常一個log事件可以拆分為
TraceSource透過Enum TraceEventType 來定義其等級,Crtical 最嚴重,Resume最不重要
public enum TraceEventType
{
/// <summary>Fatal error or application crash.</summary>
Critical = 1,
/// <summary>Recoverable error.</summary>
Error = 2,
/// <summary>Noncritical problem.</summary>
Warning = 4,
/// <summary>Informational message.</summary>
Information = 8,
/// <summary>Debugging trace.</summary>
Verbose = 16, // 0x00000010
/// <summary>Starting of a logical operation.</summary>
Start = 256, // 0x00000100
/// <summary>Stopping of a logical operation.</summary>
Stop = 512, // 0x00000200
/// <summary>Suspension of a logical operation.</summary>
Suspend = 1024, // 0x00000400
/// <summary>Resumption of a logical operation.</summary>
Resume = 2048, // 0x00000800
/// <summary>Changing of correlation identity.</summary>
Transfer = 4096, // 0x00001000
}

// SourceLevels.All表示最低紀錄層級為全部
var traceSource = new TraceSource("my_event", SourceLevels.All);
另外,TraceSource 是採用觀察者模式,也就是可以透過訂閱TraceSource來處理 Log
原先在Debug Console有東西是因為IDE會自動幫你註冊一個Listener。
var traceSource = new TraceSource("my_event", SourceLevels.All);
var listener = new Listener();
traceSource.Listeners.Add(listener);
traceSource.TraceEvent(TraceEventType.Critical, 1, "密碼已輸入錯誤3次,即將啟動自爆裝置");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 5...");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 4...");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 3...");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 2...");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 1...");
traceSource.TraceEvent(TraceEventType.Error, 3, "權限不足,自爆失敗");
class Listener: TraceListener
{
public override void Write(string message)
{
Console.Write(message);
}
public override void WriteLine(string message)
{
Console.WriteLine(message);
}
}
原先只在debug console出現的文字,透過自定義管道輸出到console中
微軟文件
一樣是走觀察者模式,所以同樣會有log的發布者,與決定要如何輸出的訂閱者。
HostEventSource.cs 發布者
class HostEventSource : EventSource
{
public static HostEventSource Log = new HostEventSource();
[Event(1, Level = EventLevel.Informational)]
public void Start(string msg)
{
WriteEvent(1, msg);
}
[Event(2, Level = EventLevel.Informational)]
public void Stop(string msg)
{
WriteEvent(2, msg);
}
[Event(3, Level = EventLevel.Critical)]
public void Shutdown( string message)
{
WriteEvent(3, message);
}
}
若無指定eventId,系統由1遞增,自動幫忙加
Listener.cs
class Listener: EventListener
{
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
Console.WriteLine(eventData.EventName);
Console.WriteLine(eventData.Payload?[0]);
}
}
Program.cs
var listener = new Listener();
listener.EnableEvents(HostEventSource.Log, EventLevel.Informational);
HostEventSource.Log.Start("Host started");
HostEventSource.Log.Shutdown("Host shutting down");
HostEventSource.Log.Stop("Host stopped");

同樣還是觀察者模式,實作了IObservable<T>與 IObserver<T> 的觀察者介面
Observer.cs
class Observer<T> : IObserver<T>
{
public Observer(Action<T> onNext, Action onCompleted)
{
_onNext = onNext ?? new Action<T>(_ => { });
_onCompleted = onCompleted ?? new Action(() => { });
}
public void OnCompleted() { _onCompleted(); }
public void OnError(Exception error) { }
public void OnNext(T value) { _onNext(value); }
private Action<T> _onNext;
private Action _onCompleted;
}
Program.cs
using System.Diagnostics;
using System.Text.Json;
DiagnosticListener.AllListeners.Subscribe(new Observer<DiagnosticListener>(listener =>
{
IObserver<KeyValuePair<string, object>> observer = new Observer<KeyValuePair<string, object>>(x =>
{
Console.WriteLine(x.Key);
Console.WriteLine(JsonSerializer.Serialize(x.Value));
},
() => { });
listener.Subscribe(observer!);
},
() => { }));
var diagnosticListener = new DiagnosticListener("Host");
diagnosticListener.Write("Start", "HostStarting");