Releases: Dtronix/Logsmith
Release list
Logsmith v0.6.0 Released
Logsmith v0.6.0 Release Notes
Highlights
This release introduces a new ILogger API alongside the existing [LogMessage] source-generator pattern, both unified under a single dispatch pipeline. It also delivers a zero-allocation hot path for enabled log calls, DPanic correctness guards, structured timing operations, and a full DocFX documentation site at dtronix.github.io/Logsmith.
New Features
ILogger API with Compile-Time Interceptors
A new ergonomic logging surface built on C# interpolated string handlers and source-generator interceptors. Both the attribute-driven [LogMessage] pattern and the ILogger API now flow through the same unified ILogSink / DispatchInfo pipeline.
ILogger logger = LoggerContext.Root.GetLogger("MyService");
logger.Information($"Processing {count} items for user {userId}");
logger.When(condition).Debug($"Diagnostic: {expensiveValue}");
using (logger.Scoped("RequestId", requestId)) { /* ... */ }
using (logger.TimedOperation("ProcessOrder")) { /* ... */ }ILogger,ILogger<T>,NullLogger— core interface plus typed and null-object variantsLoggerContext— hierarchical path resolution with cached UTF-8 path bytesLogScope— struct-based scoped property push/pop (replaces the prior AsyncLocal type)TimingOperation— disposable timer that emits operation/outcome/elapsed_ms as structured JSON- Static
Logclass —Log.Information(...)etc. with[Conditional]-stripped call sites at Trace/Debug - Generator Stage 2 interceptors — bake caller info, event IDs, and chain carrier propagation into call sites at compile time
- New diagnostic LSMITH013 — flags broken chains where chain method results are stored in a variable
DPanic Correctness Guards
ILogger gains four DPanic overloads (string + handler, with/without exception). DPanic dispatches at Error level and optionally throws via the new ThrowOnDPanic config flag — useful for "this should never happen" assertions that are loud in development and observable in production.
builder.ThrowOnDPanic = true; // typically dev/test only
logger.DPanic($"Unexpected state: {state}");Log Static Exception Overloads
Four new exception overloads on the static Log class (Trace, Debug, Information, Warning) round out the existing Error/Critical pattern, all with appropriate [Conditional] attributes.
DI Integration
Logsmith.Extensions.Logging adds services.AddLogsmith(...) for native Logsmith DI registration, separate from the existing MEL bridge.
DocFX Documentation Site
A full DocFX-based documentation site replaces the previously inline README content. Published to GitHub Pages on every push to master, with auto-generated API reference for Logsmith and Logsmith.Extensions.Logging.
- 12 articles covering installation, modes, sinks, formatting, scoped context, performance, testing, MEL bridge, and configuration
- Internals section covering architecture and compile-time diagnostics
- README slimmed from ~1,400 lines to ~110
Performance
Hot-path allocations introduced by the ILogger rework have been eliminated:
LogHandlerCore—ArrayBufferWriter<byte>andUtf8JsonWriterinstances are now pooled via[ThreadStatic]slots inThreadBuffer. Removes 2–3 heap allocations per enabled log call after thread-local warmup.PathNode.WriteUtf8Path— eliminates astring[]heap allocation via a two-pass offset algorithm that writes directly into the destination span.LoggerContext.GetCachedPathBytes— adds acquire/releaseVolatile.Read/Writeordering on the cached path version to ensure a thread observing the new version also observes the corresponding byte array.
Result: zero-allocation hot path for enabled log calls (after warmup); [Conditional]-stripped sites compile to nothing at all. Validated by new ConditionalStrippingTests that assert IL-level call-site removal via PEReader/MetadataReader.
Breaking Changes
The unified dispatch pipeline reshapes the sink and formatter contracts. Custom sinks and formatters require updates.
ILogSink.Writesignature changed:Write(in LogEntry, ReadOnlySpan<byte>)→Write(in DispatchInfo)LogEntryremoved — replaced byDispatchInfo(structured data is exposed viainfo.Utf8Json)IStructuredLogSinkremoved — folded into the unifiedILogSink(any sink can opt into structured payloads throughDispatchInfo)IStructuredLogsmithLoggerremoved (Abstraction mode) — the abstraction surface is nowILogsmithLoggerplus the newILoggerinterfaceWriteProperties<TState>removed — structured property writes now flow through the JSON path onDispatchInfoStructuredPathEmitterremoved — structured JSON emission is now inline inMethodEmitterLogScope(the prior AsyncLocal type) replaced with the new struct-basedLogScope. Uselogger.Scoped(...)instead ofLogScope.Push()/LogScope.Current.ILogFormattersignature updated to acceptDispatchInfo
Note: README anchor links from prior versions (e.g.,
#sinks,#log-levels-and-conditional-compilation) no longer resolve — that content now lives at dtronix.github.io/Logsmith.
Migration Guide
-
Custom sinks — implement
Write(in DispatchInfo info); read structured JSON frominfo.Utf8Jsonif needed:public void Write(in DispatchInfo info) { // info.Level, info.Timestamp, info.Message (Utf8 span), info.Utf8Json }
-
Structured sinks — drop
IStructuredLogSink; the same sink implementation now handles both text and structured payloads viaDispatchInfo. -
Custom formatters — update
ILogFormattermethods to acceptDispatchInfo. -
Scoped context — replace
LogScope.Push("key", value)withlogger.Scoped("key", value). The struct-based scope is allocation-free. -
Abstraction mode consumers — replace references to
IStructuredLogsmithLoggerandWriteProperties<TState>withILoggerplus interpolated string handlers. -
ProjectReference consumers of the generator — add the interceptor namespace to your csproj to opt into Stage 2 interceptors:
<PropertyGroup> <InterceptorsNamespaces>$(InterceptorsNamespaces);Logsmith.Generated</InterceptorsNamespaces> </PropertyGroup>
-
DPanic — opt into throw behavior in development/test builds:
builder.ThrowOnDPanic = true;
Other Changes
- Sample projects updated to demonstrate the
ILoggerAPI alongside[LogMessage] - MEL bridge updated for the unified dispatch path (heap fallback for messages exceeding the thread-local buffer)
DefaultLogFormattermade bounds-safe for partial buffers- Source-generator emitted
.g.csfiles no longer tracked in source control (**/Generated/,*.g.csadded to.gitignore)
Full Changelog: v0.5.0...v0.6.0
Logsmith v0.5.0 Released
Logsmith v0.5.0 Release Notes
Highlights
This release introduces explicit mode selection via the <LogsmithMode> MSBuild property, Abstraction Mode for library authors, and restructures Logsmith.Generator as a thin meta-package.
New Features
Unified <LogsmithMode> Property
Mode selection is now controlled by a single MSBuild property:
| Mode | Default for | Description |
|---|---|---|
Shared |
Logsmith package |
Uses public types from the runtime assembly |
Standalone |
Logsmith.Generator package |
Emits all types as internal, zero runtime dependency |
Abstraction |
Explicit opt-in | Public logging interfaces for library authors, internal infrastructure |
<PropertyGroup>
<LogsmithMode>Standalone</LogsmithMode>
</PropertyGroup>Abstraction Mode for Library Authors
Library authors can now expose a logging contract without imposing Logsmith.dll on consumers. The generator emits public interfaces in a configurable namespace while keeping all infrastructure internal.
ILogsmithLogger— text-only logging interfaceIStructuredLogsmithLogger— extends with typed property access viaWriteProperties<TState>LogsmithOutput.Logger— static wiring point for consumers- Configurable namespace via
<LogsmithNamespace>or defaults to{RootNamespace}.Logging - New diagnostic LSMITH008: warns when
ILogSinkexplicit sink parameter is used in abstraction mode (useILogsmithLoggerinstead)
Package Restructuring
Logsmithnow ships both the runtime DLL (lib/) and the source generator (analyzers/dotnet/cs/), plus.propsfiles inbuild/andbuildTransitive/Logsmith.Generatoris now a thin meta-package with no DLLs — depends onLogsmithwith asset filtering (analyzers and build assets only)- NuGet props evaluation order ensures correct default precedence:
Sharedwhen both packages are present
PrivateAssets Build Validation (LSMITH010)
A new build warning is emitted when LogsmithMode is Standalone or Abstraction but the Logsmith package reference is missing PrivateAssets="all". This prevents accidental leaking of the runtime DLL to consumers.
Breaking Changes
Logsmith.Generatorpackage model changed — it is now a meta-package depending onLogsmithwith asset filtering, not a standalone analyzer package. The package reference no longer needsOutputItemType="Analyzer"orReferenceOutputAssembly="false".- Mode is now property-driven — the generator no longer auto-detects Shared vs Standalone from assembly references. Mode is determined by
<LogsmithMode>.
Migration Guide
-
Standalone mode is now explicit (previously auto-detected from missing assembly reference):
- If using
Logsmith.Generatorpackage: no change needed (defaults toStandalone) - If using
Logsmithpackage in standalone mode: add<LogsmithMode>Standalone</LogsmithMode>
- If using
-
Add
PrivateAssets="all"when using Standalone or Abstraction mode:<PackageReference Include="Logsmith" Version="0.5.0" PrivateAssets="all" />
-
Logsmith.Generatorpackage reference simplified:<!-- Before --> <PackageReference Include="Logsmith.Generator" Version="0.4.2" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> <!-- After --> <PackageReference Include="Logsmith.Generator" Version="0.5.0" />
Other Changes
- Updated GitHub Actions to v5 for Node.js 24 compatibility
- Added structured logging demonstrations to all sample projects
- Updated README with Operating Modes, Abstraction Mode, and Flushing/Shutdown documentation
Full Changelog: v0.4.2...v0.5.0
v0.4.2
What's Changed
- Fix STA sync-context deadlock in Shutdown() and Dispose() by @DJGosnell in #17
Full Changelog: v0.4.1...v0.4.2
v0.4.1
What's Changed
- Fix CS0315 for non-IUtf8SpanFormattable types (bool, enum, custom structs) by @DJGosnell in #16
Full Changelog: v0.4.0...v0.4.1
v0.4.0
What's Changed
- Add LogManager shutdown, sink ownership, and drain timeout by @DJGosnell in #12
- Add IFlushableLogSink interface and LogManager.FlushAsync by @DJGosnell in #13
- Move CaptureUnhandledExceptions into config builder by @DJGosnell in #14
- Add BufferedLogSink drop visibility (counter + error handler) by @DJGosnell in #15
Full Changelog: v0.3.2...v0.4.0
v0.3.2
What's Changed
- Fix silent data loss when log message exceeds stackalloc buffer by @DJGosnell in #7
Full Changelog: v0.3.1...v0.3.2
v0.3.1
What's Changed
- Fix ANSI escape codes leaking into redirected console output by @DJGosnell in #6
- Add benchmark warmup/iteration caps, scoped context benchmark, and results doc by @DJGosnell in #5
Full Changelog: v0.3.0...v0.3.1
v0.3.0
What's Changed
- Add scoped context, sampling, dynamic levels, exception handler, and MEL bridge by @DJGosnell in #4
Full Changelog: v0.2.0...v0.3.0
v0.2.0
What's Changed
- Add class modifier and nesting support to source generator by @DJGosnell in #2
- Add generic SetMinimumLevel() and per-category filtering by @DJGosnell in #1
- Perf: reduce allocations and syscalls in hot logging paths by @DJGosnell in #3
Full Changelog: v0.1.3...v0.2.0