Windows 事件記錄・ETW 入門 ── 讓業務應用程式的日誌搭上 OS 標準機制
· 小村 豪 · CSharp, .NET, 事件記錄, ETW, EventSource, 日誌設計, 故障調查, Windows 開發
在用 C# 開發業務應用程式或 Windows 服務時,通常會用自製的檔案記錄器,或 Serilog、NLog 這類函式庫來輸出每天的日誌。那麼 Windows 事件記錄或 ETW(Event Tracing for Windows)是不是已經不需要了呢?並非如此。這兩者並不是「檔案日誌的替代品」,而是維運人員或 OS 標準工具能看到的另一個層級的記錄。
本文將整理檔案日誌、事件記錄、ETW 的分工方式,.NET 上的實作方法,ETW 最基本的重點,收集與調查的實務,以及現場常踩到的陷阱。
1. 先下結論
- 三種手段不是替代關係,而是分工。 檔案日誌是開發者事後追查細節用的記錄,事件記錄是讓維運人員或 OS 標準的事件檢視器、監控工具察覺異常用的記錄,ETW 則是為了效能調查或難以重現的缺陷分析,依需要啟用的高頻率追蹤。基本上三者會一起使用,只是各自寫入的資訊量不同。
- 事件記錄只寫「啟動、停止、嚴重錯誤」。 如果把所有日誌都灌進事件記錄,維運人員真正該看到的異常反而會被埋沒。檔案日誌一如既往寫得詳細,事件記錄則要收斂範圍。
- 註冊事件來源需要系統管理員權限。
EventLog.CreateEventSource從 Windows Vista 之後,沒有系統管理員權限就無法呼叫。應避免設計成在執行時第一次存取才註冊,而是在安裝程式階段就先完成註冊。1 - ETW 並不是「隨時都在收集」。 即使用
EventSource定義好了事件,只要沒有訂閱者(dotnet-trace、PerfView、以 ETW 為基礎的工具),就什麼都不會被記錄下來。基本用法是只在做效能分析或特定缺陷調查時,才指定目標 provider 進行收集。2 - 訊息要用範本格式撰寫,而不是字串串接或字串插值。 像
logger.LogInformation("Order {OrderId} failed", orderId)這樣寫,就能以結構化日誌的形式,用具名的預留位置搜尋 OrderId。用字串串接或插值組出來的訊息,會讓這個結構資訊消失。3 - 日誌肥大化的問題,常常是因為預設值太小。 事件記錄的預設最大容量只有 512KB,如果業務應用程式會日常性地使用事件記錄,就應該明確擴大容量,並在設計階段就決定容量滿了之後的行為(要覆寫舊資料,還是捨棄新資料)。4
下面是判斷表。
| 觀點 | 檔案日誌 | 事件記錄 | ETW |
|---|---|---|---|
| 主要讀者 | 開發者、維護人員 | 維運人員、監控工具、OS 標準的事件檢視器 | 效能調查人員、支援工程師 |
| 輸出量參考 | 詳細(可以包含 Trace/Debug) | 少(僅啟動、停止、嚴重錯誤) | 啟用時才大量產生。預設情況下什麼都不會記錄 |
| 生命週期・保存 | 依輪替設定,容易長期保存 | 達到日誌容量上限後,會從舊的開始覆寫或捨棄 | 以工作階段為單位。收集完成後以 .etl/.nettrace 檔案儲存 |
| 與 OS 標準工具的整合 | 無(需要自製檢視器) | 可用事件檢視器、wevtutil、Get-WinEvent 等標準方式查看 |
用 dotnet-trace、PerfView、WPR/WPA 查看 |
| 權限 | 僅需應用程式執行使用者的寫入權限 | 註冊來源需要系統管理員權限(若來源已註冊,寫入本身標準使用者也可以) | 開始收集多半需要系統管理員權限 |
2. Windows 事件記錄的基礎
Windows 預設有 Application、System、Security 三種日誌,其中 Security 為唯讀。業務應用程式或服務會寫入 Application 日誌,或應用程式自訂的日誌(頻道)。裝置驅動程式則慣例上寫入 System 日誌。5
寫入的單位是「事件來源(event source)」。來源是用來識別應用程式的名稱,要先用 EventLog.CreateEventSource 註冊。從 Windows Vista 開始,註冊來源需要系統管理員權限。 這是因為要確認來源名稱的唯一性,必須搜尋包含 Security 日誌的所有事件記錄,而標準使用者沒有存取 Security 日誌的權限。1
using System.Diagnostics;
// 在安裝程式或設定流程中,於擁有系統管理員權限期間執行
const string SourceName = "KomuraSoft.OrderService";
const string LogName = "Application";
if (!EventLog.SourceExists(SourceName))
{
EventLog.CreateEventSource(SourceName, LogName);
}
如果想在執行時第一次存取才建立來源,在應用程式以標準使用者身分執行的環境中,這個呼叫就會失敗。關於何時需要系統管理員權限的一般性說明,整理在「Windows 什麼時候需要系統管理員權限」一文中,事件來源的註冊正好符合這個模式:在安裝程式中完成註冊,應用程式本體只寫入已註冊好的來源,才是安全的設計。
事件會附帶「層級(level)」與「事件識別碼(event ID)」兩種識別資訊。層級是 EventLogEntryType(Information、Warning、Error、SuccessAudit、FailureAudit),會用於事件檢視器的圖示與預設篩選條件。事件識別碼是應用程式自行定義的整數,用作日後搜尋、統計同類事件的鍵值。
3. 從 .NET 寫入事件記錄
.NET 寫入事件記錄的路徑大致有兩種。
3.1 直接使用 System.Diagnostics.EventLog
這是低階 API,適合需要精細控制(分類、附加二進位資料等)的場合。在 .NET(非 .NET Framework)中,需要參考 System.Diagnostics.EventLog 這個 NuGet 套件。
using System.Diagnostics;
public sealed class OrderServiceEventLog
{
private const string SourceName = "KomuraSoft.OrderService";
public void WriteServiceStarted()
{
EventLog.WriteEntry(SourceName, "已啟動 OrderService。",
EventLogEntryType.Information, eventID: 1000);
}
public void WriteFatalError(Exception ex)
{
EventLog.WriteEntry(SourceName,
$"發生嚴重錯誤,無法繼續處理。詳細內容請確認檔案日誌。{ex.GetType().Name}: {ex.Message}",
EventLogEntryType.Error, eventID: 1999);
}
}
3.2 使用 ILogger + AddEventLog
如果已經用 ILogger 建立了結構化日誌,使用 Microsoft.Extensions.Logging.EventLog 套件的 AddEventLog,會更順理成章地接上既有的日誌管線。以 ASP.NET Core 或 Generic Host 為基礎的 Worker Service,在 Windows 上執行時預設就會啟用 EventLog provider。6
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddEventLog(settings =>
{
settings.SourceName = "KomuraSoft.OrderService";
settings.LogName = "Application";
// 除了預設的分類篩選之外,事件記錄只讓
// Warning 以上等級通過(詳細內容交給檔案日誌那邊)
settings.Filter = (_, level) => level >= LogLevel.Warning;
});
using IHost host = builder.Build();
await host.RunAsync();
如果省略 EventLogSettings,LogName 預設會是 "Application",SourceName 預設會是 ".NET Runtime"。7 如果維持 .NET Runtime 這種通用的來源名稱,在事件檢視器上會很難分辨究竟是自己應用程式的日誌,還是其他 .NET 應用程式的日誌,因此請務必把 SourceName 明確指定為自家應用程式專用的名稱。
在 Windows 服務或工作排程器上執行的批次處理中,只把「啟動」「停止」「嚴重錯誤」寫入事件記錄,其他詳細內容交給檔案日誌,是實務上比較均衡的做法。服務的建立方式與維運,整理在「Windows 服務的建立方式與維運」;透過工作排程器執行時的問題排查,則整理在「工作排程器的工作不執行、或以 0x1 結束」。這兩種情境下,異常發生時維運人員最先看到的多半是事件記錄,這裡是否留有重點,會直接影響初期調查的速度。
4. 什麼是 ETW ── 貫穿整個 OS 的追蹤基礎
Event Tracing for Windows(ETW)是一套能以共同基礎、從核心到使用者模式應用程式進行儀器化的追蹤機制。它最大的特色,是能把核心事件(磁碟 I/O、程序建立等)和應用程式自訂的事件放在同一條時間軸上,一併記錄與分析。8
.NET 可以透過建立繼承 System.Diagnostics.Tracing.EventSource 的類別,定義自己的 ETW provider。ETW 以發佈—訂閱(Publish-Subscribe)模式運作,沒有訂閱者的話事件就不會被記錄。也就是說,光是實作 EventSource 什麼事都不會發生,要等收集工具明確啟用之後,事件才會真正被記錄下來。2
using System.Diagnostics.Tracing;
[EventSource(Name = "KomuraSoft-OrderService")]
internal sealed class OrderServiceEventSource : EventSource
{
public static readonly OrderServiceEventSource Log = new();
[Event(1, Level = EventLevel.Informational, Message = "已開始處理訂單。OrderId={0}")]
public void OrderProcessingStart(string orderId)
{
if (IsEnabled())
{
WriteEvent(1, orderId);
}
}
[Event(2, Level = EventLevel.Informational, Message = "訂單處理已完成。OrderId={0}")]
public void OrderProcessingStop(string orderId)
{
if (IsEnabled())
{
WriteEvent(2, orderId);
}
}
}
呼叫端的用法如下。在真正寫入事件之前,先用 IsEnabled() 確認是否已啟用,可以避免在沒有收集時產生多餘的成本。
OrderServiceEventSource.Log.OrderProcessingStart(order.Id);
try
{
ProcessOrder(order);
}
finally
{
OrderServiceEventSource.Log.OrderProcessingStop(order.Id);
}
要收集這個事件,最簡單的方式是使用跨平台工具 dotnet-trace。9
dotnet-trace collect --providers KomuraSoft-OrderService -- OrderService.exe
收集到的 .nettrace 檔案,可以用 Visual Studio 或 PerfView 開啟查看內容。如果想做更深入的效能調查,例如連核心事件都要一併分析,就會變成使用 Windows Assessment and Deployment Kit(ADK)內含的 Windows Performance Recorder(WPR)收集,再用 Windows Performance Analyzer(WPA)查看的架構。10 不過,本文的範圍僅止於「ETW 能做什麼、什麼時候用」,不會深入 PerfView 或 WPA 的詳細分析步驟。就開發與維護業務應用程式的角度而言,只要掌握到能用 EventSource 定義自己的 provider、並能用 dotnet-trace 收集,就已經足夠。
5. 結構化日誌的實務
ILogger 的訊息範本,要寫成包含 {PlaceHolder} 這種具名預留位置的固定字串。這不只是表面上的寫法慣例,而是代表在不改變範本本身的前提下,只傳遞引數,就能讓日誌基礎架構那一側維持預留位置名稱與值之間的對應關係(結構化資料)。11
// 建議:範本固定,引數另外傳遞
logger.LogWarning("Order {OrderId} 保留庫存失敗。倉庫={WarehouseId}", orderId, warehouseId);
// 不建議:字串串接、插值會破壞範本與值的對應關係
logger.LogWarning("Order " + orderId + " 保留庫存失敗。倉庫=" + warehouseId);
logger.LogWarning($"Order {orderId} 保留庫存失敗。倉庫={warehouseId}");
程式碼分析器規則 CA2254,可以偵測到這種不建議的模式(範本會隨每次呼叫而改變的寫法)。3 在呼叫頻率高的熱路徑(hot path)上,進一步使用 LoggerMessageAttribute 做原始碼產生,可以避免 boxing 與範本解析的成本。12
using Microsoft.Extensions.Logging;
internal static partial class Log
{
[LoggerMessage(
EventId = 2001,
Level = LogLevel.Warning,
Message = "Order {OrderId} 保留庫存失敗。倉庫={WarehouseId}")]
public static partial void StockReservationFailed(
this ILogger logger, string orderId, string warehouseId);
}
// 呼叫端
logger.StockReservationFailed(orderId, warehouseId);
採用這種架構,同一次日誌呼叫就能分別把詳細內容送到檔案日誌(Serilog/NLog 等 sink)、把收斂後的內容送到事件記錄、把低成本的追蹤透過 EventSourceLoggerProvider 送到 ETW。基本作法是註冊多個 ILoggerProvider,再用 Filter 依 provider 分別調整輸出層級。自製記錄器的最低要求,整理在「自製記錄器的最低要求與整合測試檢查清單」;當機時該如何把日誌與傾印檔綁在一起保留,整理在「Windows 應用程式當機時保留日誌與傾印的設計」,請參考。
6. 收集與調查 ── 讀取已寫入日誌的一方
在事件檢視器中,可以從日誌清單使用「篩選目前的日誌」或「建立自訂檢視」,只取出特定來源、層級、事件識別碼的項目。經常使用的條件,建議存成自訂檢視,之後調查會更快。自訂檢視是用 XPath 查詢定義的,舉例來說,只取出特定事件名稱的查詢大致如下。13
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">
*[System[Provider[@Name='KomuraSoft.OrderService'] and (Level=2 or Level=3)]]
</Select>
</Query>
</QueryList>
在命令列上,可以用 wevtutil 匯出日誌或變更設定。作為障礙調查的一環,也可以把事件發生前後的事件記錄匯出,送給支援團隊,這種運維方式很實用。14
:: 將 Application 日誌整個匯出成 evtx 檔案
wevtutil epl Application C:\logs\application_20260707.evtx
:: 只顯示特定來源的事件(最近 10 筆)
wevtutil qe Application /q:"*[System[Provider[@Name='KomuraSoft.OrderService']]]" /c:10 /rd:true /f:text
在當機調查中,基本做法是把事件記錄、檔案日誌、當機傾印這三者依時間對齊。以事件記錄中留下的「嚴重錯誤」時間戳記為起點,比對同一時刻的檔案日誌細節,以及用 WER/ProcDump 採集到的傾印檔。傾印檔的採集方式整理在「Windows 當機傾印收集入門 - WER/ProcDump/WinDbg」,採集後實際的分析步驟整理在「用 WinDbg + SOS 讀取當機傾印」。
7. 常見陷阱
- 沒註冊來源就想寫入,結果失敗。 用未註冊的來源名稱進行相當於
RegisterEventSource的呼叫時,事件本身確實會寫進 Application 日誌,但因為沒有對應的訊息資源 DLL,事件檢視器無法顯示說明文字,會呈現錯誤狀態。15 更進一步,如果設計成在執行時用CreateEventSource自動註冊,很容易在非系統管理員使用者執行的程序第一次啟動時,因例外而當掉。來源註冊務必在安裝程式端就完成。 - 「竟然可以用別的來源名稱寫入」的混亂。 事件記錄只要有寫入權限,就能用應用程式從未註冊過的其他來源名稱寫入。來源名稱理應在一台電腦上是唯一的,但 OS 並沒有機制去強制這一點,全靠應用程式端自律。5 如果選用過於通用的來源名稱(不含公司名或產品名的短名稱),就容易和其他廠商的應用程式衝突,或不小心沿用了別的應用程式的來源名稱。
- 日誌肥大化與保存政策。 事件記錄的預設最大容量僅有 512KB。4 如果業務應用程式會日常使用事件記錄,就要用
wevtutil sl或安裝程式明確擴大容量,並在設計時就意識到達到上限後,要選擇「從舊資料開始覆寫」還是「捨棄新資料」。也要注意OverwriteOlder已經被標記為棄用,即使指定了,實際上仍可能變成「不覆寫」的行為。16 - 多語言環境下的訊息顯示。 如果採用本地化的訊息資源檔(
MessageResourceFile),一旦在缺少對應資源 DLL、或版本不一致的環境中檢視日誌,就會顯示成「無法找到事件識別碼 ○○ 的說明」。像是把轉送過來的事件記錄拿到別的伺服器上看,或應用程式已經解除安裝、卻只留下日誌,都是容易出現這類問題的場景。如果採用直接用WriteEntry寫入字串(不使用資源檔)的架構,就可以從根本上避免這類問題。 - 因權限不足而無法寫入的情況。 這裡常被誤解:註冊來源需要系統管理員權限,但寫入已註冊的來源,標準使用者也可以做到。請不要一看到「沒有系統管理員權限就跑不動」就急著把整個應用程式改成以系統管理員身分執行,先分清楚實際上是哪個操作需要權限。
相關文章
- Windows 什麼時候需要系統管理員權限 - UAC、受保護區域與設計上的判斷方式
- Windows 服務的建立方式與維運 ── 從與工作排程器的分工到將 BackgroundService 服務化
- 工作排程器的工作不執行、或以 0x1 結束 ── 原因排查與安全的維運設計
- 自製記錄器的最低要求與整合測試檢查清單
- Windows 應用程式當機時保留日誌與傾印的設計
- Windows 當機傾印收集入門 - WER/ProcDump/WinDbg
- 用 WinDbg + SOS 讀取當機傾印 ── 收集後的實務分析入門
相關諮詢領域
合同會社小村軟體處理 Windows 業務應用程式的日誌、事件記錄、ETW 基礎架構設計,以及以障礙調查為前提的觀測點設計技術諮詢。
參考連結
-
Microsoft Learn, EventLog.CreateEventSource Method. 說明從 Windows Vista 起,建立事件來源需要系統管理員權限的原因(必須搜尋包含 Security 日誌的所有日誌)。 ↩ ↩2
-
Microsoft Learn, Getting Started with EventSource. 說明 EventSource 以發佈—訂閱模式運作,沒有訂閱者(ETW 或 EventPipe)就不會記錄事件,以及用 dotnet-trace collect 收集的方法。 ↩ ↩2
-
Microsoft Learn, CA2254: Template should be a static expression. 說明在日誌訊息範本中使用字串串接或插值,會讓預留位置名稱與值的對應關係消失。 ↩ ↩2
-
Microsoft Learn, EventLog.MaximumKilobytes Property. 說明事件記錄的預設最大容量為 512 KB。 ↩ ↩2
-
Microsoft Learn, EventLog Class. 說明 Application/System/Security 三種預設日誌、來源必須在同一台電腦上保持唯一,以及只要有寫入權限就能用任何已註冊來源名稱寫入的機制。 ↩ ↩2
-
Microsoft Learn, Logging providers in .NET. 說明 Generic Host 預設的記錄提供者包含 Console、Debug、EventSource、EventLog(僅 Windows)。 ↩
-
Microsoft Learn, EventLogSettings Class. 說明 AddEventLog 的預設值(LogName 為 “Application”、SourceName 為 “.NET Runtime”)。 ↩
-
Microsoft Learn, Event Tracing. 說明 Event Tracing for Windows(ETW)是用來啟動、停止、儀器化與消費應用程式追蹤事件的機制。 ↩
-
Microsoft Learn, dotnet-trace performance analysis utility. 說明基於 EventPipe 的跨平台追蹤收集工具 dotnet-trace 的概要與 collect 指令。 ↩
-
Microsoft Learn, Introduction to WPR. 說明 Windows Performance Recorder(WPR)擴充了 ETW,是提供系統與應用程式詳細記錄的工具。 ↩
-
Microsoft Learn, Logging in C# and .NET. 說明日誌訊息範本的 {PlaceHolder} 語法,以及範本中的鍵名如何成為日誌的屬性名稱。 ↩
-
Microsoft Learn, High-performance logging in .NET. 說明透過 LoggerMessageAttribute 產生原始碼的記錄方式,可以避免 boxing 與範本解析的成本。 ↩
-
Microsoft Learn, Comparison of ETW and EventLog logger functionality. 說明在事件檢視器中使用 XPath 查詢,依事件名稱等條件建立自訂檢視的方法。 ↩
-
Microsoft Learn, wevtutil. 說明匯出事件記錄(epl)、查詢(qe)、變更設定(sl)等命令列操作。 ↩
-
Microsoft Learn, Event Sources. 說明用未註冊的來源名稱寫入時會回退到 Application 日誌,但因為沒有訊息檔而導致事件檢視器無法顯示說明文字、呈現錯誤的情況。 ↩
-
Microsoft Learn, OverflowAction Enum. 說明事件記錄達到容量上限時的行為(覆寫、捨棄),以及 OverwriteOlder 已被標記為棄用。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
用 PerfView 與 dotnet-trace 找出「變慢」的原因 ── .NET 效能調查實務入門
當商用應用程式「變慢」「CPU 卡住不動」「偶爾當掉」時,該用哪個工具看什麼?本文整理 PerfView 與 dotnet-trace 的角色分工、CPU 取樣的讀法(inclusive/exclusive)、用 ThreadTime 調查阻塞時間,以及與 EventSou...
不只是 appsettings.json ── Windows 商用應用程式的組態管理實務(環境別設定・機密資訊・寫入位置)
本文從實務角度整理 Windows 商用應用程式的組態管理,涵蓋 appsettings.json 的分層、IConfiguration 與 IOptions/IOptionsSnapshot/IOptionsMonitor 的選用方式、環境別設定、可寫入設定的存放位置、機...
Windows 業務應用程式的列印與 PDF 輸出 ── System.Drawing.Printing / WPF / 報表函式庫的取捨
本文以要件別判斷表整理 PrintDocument 的 WinForms 列印、WPF 的 FlowDocument/FixedDocument 列印,以及 PDF 輸出的各種選項。並從實務角度說明分頁控制、DPI 落差等實作陷阱,以及從 Windows 服務列印、依賴預設...
委外・委託開發 Windows 應用程式前該整理的事項
在委外・委託開發 Windows 應用程式之前,整理既有軟體改版、設備整合、COM/ActiveX、發布與更新、維護等應留意的重點。
工業相機控制應用跑一個月後突然崩潰時(前篇) - handle leak 的找法與長時間運轉用的日誌設計
本文以工業相機控制應用連續運轉約一個月後突然崩潰的案例,整理 handle leak 的特徵、與記憶體流失的差異,以及如何用 Handle Count 斜率與 create/close 配對日誌追出真正洩漏的位置。讀者可學到把長時間運轉的故障切成可觀測形式,並用短迴圈反覆壓...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
故障調查 & 長期運行故障
整理間歇性故障、通訊診斷、長期運行當機、失敗路徑測試基礎的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
故障調查 & 根本原因分析
調查難以重現的故障、長時間執行後的問題、記憶體洩漏、通訊停滯等棘手的正式環境問題。
常見問題
整理諮詢這個主題時常見的問題。
- 有檔案日誌的話,事件記錄就不需要了嗎?
- 如果只是為了做詳細調查,檔案日誌往往就已經足夠。但如果希望維運人員能透過 OS 標準的事件檢視器或監控工具(例如工作排程器的失敗通知、SCOM 等)察覺應用程式的異常,同時把重點寫入事件記錄會更實務。兩者不是替代關係,而是分屬不同層級的記錄。
- 事件識別碼(Event ID)的編號規則該怎麼決定?
- 沒有固定答案,但實務上遵守以下三點可以減少事故:『依功能領域分段(例如啟動類用 1000 系列、通訊類用 2000 系列)』、『一旦發出的識別碼不再改變其含義、也不重複使用』、『允許出現跳號』。ILogger 的 EventId 也會被當作結構化日誌的搜尋鍵,因此採用容易日後文件化的編號方式,對維運會很有幫助。
- 我們在用 Serilog 或 NLog,這篇文章的內容和它們有什麼關係?
- Serilog 和 NLog 是在 ILogger 之下,把同一筆日誌分派到檔案、事件記錄、ETW、外部 SaaS 等多個 sink 的其中一種實作方式。本文提到的『事件記錄只寫重點』『不要破壞訊息範本』這些設計方針,無論是直接使用 ILogger,還是透過 Serilog/NLog,都同樣適用。至於要用事件記錄專用的 sink(例如 Serilog.Sinks.EventLog),還是用標準的 AddEventLog,屬於實作上的選擇。
- ETW 學到什麼程度才算足夠?
- 如果主要目的是開發與維護業務應用程式,只要掌握到能用 EventSource 建立自己的 provider、能用 dotnet-trace 收集自己 provider 的事件,就已經足夠。用 PerfView 做呼叫堆疊分析,或用 WPR 收集核心事件,屬於專門做效能調查階段才需要學習的範疇,本文刻意將其排除在範圍之外。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。