做為一個開發人員,不管是在維護舊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");