TCIS.Logging.Wolverine 1.0.0

dotnet add package TCIS.Logging.Wolverine --version 1.0.0
                    
NuGet\Install-Package TCIS.Logging.Wolverine -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TCIS.Logging.Wolverine" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TCIS.Logging.Wolverine" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="TCIS.Logging.Wolverine" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TCIS.Logging.Wolverine --version 1.0.0
                    
#r "nuget: TCIS.Logging.Wolverine, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TCIS.Logging.Wolverine@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TCIS.Logging.Wolverine&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=TCIS.Logging.Wolverine&version=1.0.0
                    
Install as a Cake Tool

TCIS.Logging.Wolverine

Middleware tự động ghi log chuẩn Elastic Common Schema (ECS v1.3) cho message handler trong pipeline Wolverine (WolverineFx).

Wolverine sinh mã bằng Roslyn lúc khởi động chứ không dùng runtime proxy, nên TCIS.Logging.Autofac (Castle DynamicProxy) không bắt được handler của Wolverine. Gói này dệt trực tiếp vào pipeline của Wolverine theo cơ chế convention-based middleware.

Tính năng

  • Đo thời gian thực thi handler → event.duration.
  • Trích CorrelationId, TenantId từ Envelope; TraceId/SpanId từ Activity.Current; phần còn lại (user, client, site) từ IWorkContext.
  • Che dữ liệu nhạy cảm trong payload qua LogArgumentResolver (dùng chung cấu hình TLogSettings:SensitiveProps).
  • Phân loại lỗi theo tiền tố taxonomy của TBaseException.StatusCode: VALIDATION_ / GUARD_ / NOT_FOUND_ / SEC_ → WARN + event.kind = "event"; còn lại → ERROR + event.kind = "error".
  • Gọi exception.MarkLogged() để TExceptionMiddleware tầng ngoài không ghi đúp bản ghi 4xx.
  • Loại trừ message ồn qua ExcludeMessages / ExcludeAssemblies.

Cài đặt

using TCIS.Logging.Extensions;
using TCIS.Logging.Wolverine.Extensions;

var builder = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        services.AddTLogging(context.Configuration);
        services.AddTWolverineLogging(context.Configuration);   // bind TLogSettings:WolverineSettings
    })
    .UseWolverine(opts =>
    {
        opts.UseTWolverineLogging();                            // gắn middleware vào mọi handler chain
    });

Cấu hình bằng code thay cho appsettings.json:

opts.UseTWolverineLogging(o =>
{
    o.IncludeMessagePayload = false;
    o.ExcludeMessages.Add("HeartbeatCommand");
});

Cấu hình

Section: TLogSettings:WolverineSettings (cùng gốc TLogSettings với AddTLogging).

{
  "TLogSettings": {
    "WolverineSettings": {
      "IncludeMessagePayload": true,
      "ExcludeMessages": [ "PingMessage", "HeartbeatCommand" ],
      "ExcludeAssemblies": [ "TCIS.Monitoring" ]
    }
  }
}
Khoá Kiểu Mặc định Ý nghĩa
IncludeMessagePayload bool true Chụp và che payload của message vào data.parameters
ExcludeMessages HashSet<string> rỗng Tên class message bỏ qua, không phân biệt hoa thường
ExcludeAssemblies HashSet<string> rỗng Tên assembly bỏ qua; nhận cả "Acme.Ops" lẫn "Acme.Ops.dll"

⚠️ Bắt buộc: sink Serilog phải khai formatter

Gói này chỉ dựng LogDetail rồi đẩy vào IInterceptedLogWriter. Phía render, mọi sink Serilog phải khai TCISEcsJsonFormatter và host phải gọi .Destructure.AsScalar<TcisEcsDocument>(). Thiếu một trong hai thì mỗi dòng log ra đúng chuỗi TCIS.Logging.TcisEcsDocument — mất sạch message, exception và stack trace. Xem src/samples/TCIS.Logging.Wolverine.Sample.

⛔ Phiên bản WolverineFx tối thiểu: 5.28.0

Middleware dựa vào convention OnException/OnExceptionAsync. Convention này chỉ xuất hiện từ WolverineFx 5.28.0 — đo bằng cách đọc Wolverine.Middleware.MiddlewarePolicy của từng bản:

Bản OnExceptionMethodNames
3.13.5 → 5.27.0 ❌ không có
5.28.0 trở lên ✅ có

Gói hiện ghim 5.41.0 (bản 5.x mới nhất). Không nâng lên 6.x: WolverineFx 6 đã bỏ net8.0, trong khi toàn bộ TCIS.* target net8.0.

⚠️ Đây là loại hỏng im lặng: Wolverine bỏ qua method không đúng convention mà không báo lỗi, không cảnh báo. Chạy trên bản < 5.28.0 thì đường thành công vẫn ghi log bình thường, chỉ đường lỗi biến mất. TWolverineLogPipelineTests là thứ duy nhất bắt được điều này — nó chạy message qua pipeline thật.

Hai lối thoát trông hợp lý nhưng đã đo là sai, đừng dùng nếu buộc phải ở lại bản cũ:

  • Finally(..., Exception? ex) — Wolverine tự new Exception() truyền vào, ở cả đường thành công lẫn thất bại. Chép logic sang đó thì mọi message thành công đều bị ghi ERROR.
  • Envelope.Failure — vẫn null khi Finally chạy trên đường IMessageBus.InvokeAsync.

Hình dạng bản ghi so với phần còn lại của TCIS.Logging

Bản ghi đi qua đúng IInterceptedLogWriter → hàng đợi ưu tiên → sink như mọi đường log khác, nên có đủ bộ field ECS chuẩn: @timestamp, log.level, message, error.*, event.*, host, service, user, custom.{log_type,event_class,schema,error,application,data,code}. Ngoại lệ thô (new Exception(...), không phải TBaseException) cũng ra bản ghi đầy đủ: ERROR, event.kind = error, error.code = SYS-UNK-9999, có error.stack_trace, payload vẫn được mask.

Ba khác biệt có chủ đích so với TLogInterceptor (đường AOP):

Trường AOP (TLogInterceptor) Gói này
custom.code.class Lớp hiện thực Kiểu message
log.origin.function Tên method thật Để trống — Wolverine không phơi tên method handler ra Envelope; hardcode "Handle" sẽ ghi sai với handler tên Consume
error.code với TBaseException Lấy classification.ErrorCode trước Lấy StatusCode trước — khớp với mã mà TExceptionMiddleware trả về client

event.duration chỉ xuất hiện khi thời gian thực thi > 0 ms (MicrosoftLoggingSink bỏ giá trị 0).

⚠️ Tác dụng phụ: gói này ĐĂNG KÝ IExceptionClassifier cho cả app

UseTWolverineLogging() và AddTWolverineLogging() đều gọi AddExceptionClassification(), vì middleware của Wolverine nhận dependency qua tham số — Wolverine phân giải chúng lúc sinh mã nên dịch vụ bắt buộc phải có mặt, không thể để tuỳ chọn. Các gói logging khác không làm vậy; chúng tra IExceptionClassifier bằng service location và bỏ qua nếu không có.

Hệ quả trong app đã dùng AOP logging mà chưa từng gọi AddExceptionClassification(): bật Wolverine logging sẽ làm TLogInterceptor bắt đầu thấy classifier, và vì nó lấy classification.ErrorCode trước StatusCode, error.code của một TValidationException đổi từ mã nghiệp vụ (GATE_IN_REJECTED) sang mã chung SYS-VAL-0001.

Ảnh hưởng chỉ ở log, không chạm response trả về client — TExceptionMiddleware vốn đã dùng StatusCode trước. Dashboard/alert nào đang lọc theo error.code của log AOP thì cần rà lại.

Hai điều cần biết khi đọc log

  1. Wolverine ghi log lỗi của riêng nó. Ngoài bản ghi ECS của gói này, Wolverine còn ghi "Invocation of ... failed!" qua ILogger, mức ERROR và error.code = SYS-UNK-9999, kể cả với lỗi 4xx. MarkLogged() không chặn được nó (cờ đó chỉ dành cho TExceptionMiddleware). Muốn hạ ồn thì đặt MinimumLevel.Override theo SourceContext là tên kiểu message.
  2. Phân biệt hai nguồn: bản ghi của gói này có custom.code.class và custom.data.parameters; bản ghi của Wolverine đi qua nhánh fallback nên chỉ có log.logger.

Tham chiếu

  • Tài liệu kỹ thuật: docs/TCIS.Logging.Wolverine.md
  • Sample chạy được: src/samples/TCIS.Logging.Wolverine.Sample
  • Bộ test: src/tests/TCIS.Logging.Wolverine.Tests
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 33 10/1/2026
1.0.0-rc.52 35 9/29/2026
1.0.0-rc.51 57 9/23/2026
1.0.0-rc.50 56 9/21/2026
1.0.0-rc.49 62 9/18/2026
1.0.0-rc.47 63 9/17/2026
1.0.0-rc.46 52 9/17/2026
1.0.0-rc.45 109 9/11/2026