Herald.OSS
0.10.2
dotnet add package Herald.OSS --version 0.10.2
NuGet\Install-Package Herald.OSS -Version 0.10.2
<PackageReference Include="Herald.OSS" Version="0.10.2" />
<PackageVersion Include="Herald.OSS" Version="0.10.2" />
<PackageReference Include="Herald.OSS" />
paket add Herald.OSS --version 0.10.2
#r "nuget: Herald.OSS, 0.10.2"
#:package Herald.OSS@0.10.2
#addin nuget:?package=Herald.OSS&version=0.10.2
#tool nuget:?package=Herald.OSS&version=0.10.2
Herald.OSS
Open-source structured logging core for .NET. Apache 2.0.
Herald.OSS is the upstream distribution of the Herald logging kernel.
The kernel passes a stack-allocated LogEventBuffer directly to sinks
through one contract — IKernelSink. Every built-in sink implements
it; the HeraldSinkBase abstract class is the one-line entry point
for custom sinks. The accept path stays zero-allocation across every
call shape — typed-args, params ReadOnlySpan<LogProperty>, the
interpolated handler, and the level-bound interpolated variant.
The package multi-targets net8.0, net9.0, and net10.0. net8.0
is the minimum — a net9 or net10 project restores the matching binary
automatically. AOT-clean. Trim-safe.
Status — v0.10.2
Herald.OSS is the canonical Apache 2.0 upstream that the rest of the Herald ecosystem absorbs from.
v0.10.2 lands three pieces. Each one has a design-decision write-up in Herald.Documentation; this README points the reader there rather than restating the detail.
Async-sink cross-tenant PII fix.
FastPathAsyncSinkdefers events from the producer thread to a background consumer. ALogProperty.Lazy(...)closure that capturedAsyncLocal,HttpContext, or any tenant-scoped state used to resolve on the consumer thread, where the producer's scope no longer existed. The fix is layered: the producer resolves everyFunc<object?>value eagerly while the original scope is still in effect, the factory finalisation pass walks the property list before the immutable event freezes,LogPropertyVisibility.PiiSensitiveforces resolved PII values through.ToString()at the producer boundary, the drain entry asserts the consumer thread's tenant matches the construction- time tenant, and a structured fail-loud diagnostic path replaces the prior empty catch. Full posture, threat model, and trust boundary: Async-sink cross-tenant PII — security posture.Lever A — inline
AsyncEnvelopeas the default async-handoff.FastPathAsyncSinknow rides aChannel<AsyncEnvelope>instead of aChannel<LogEvent>. The producer constructs a value-typed envelope (header +[InlineArray(8)]slot buffer + optional overflow array) on its stack and copies it into the channel — zero per-event heap on the producer for the 99%+ arity-≤-8 case. The drain reconstitutes aLogEventBufferon its own stack forIKernelSinkinners (truly 0-alloc system-wide) or a heapLogEventfor legacy inners. The paced-regime re-measure at 24-conn × 100KHz pacing-locks throughput and produces ~6× fewer gen0 collections / ~15× fewer gen1; the oversubscription regime at 96-conn flat-out doubles throughput and cuts B/event from 296 to 0.3. Public API unchanged. Lever A async-handoff — design decision.Canonical-equivalence-by-construction on the compact path.
LogPropertyCompactcarries name and value only. Non-default axes (CaptureMode,Format,Visibility) cannot be represented on the compact slot.ToLogProperty()is canonically equivalent to a directLogProperty(name, value)because no axis information is present to lose. The XML doc now states this contract on the type; HERALD014 enforces it at compile time. Compact-path default-axes-only — design decision.
Analyzer family. HERALD008–HERALD014 ship in
MMP.Herald.OSS.Generators alongside the existing
HERALD001–HERALD007 / HRLD00xx rules. HERALD008–HERALD013 flag the
async-sink lazy-closure capture shapes (AsyncLocal, HttpContext,
[ThreadStatic], mutable reference fields, ILogScopeProvider) at
compile time. HERALD014 flags axis-bearing LogProperty instances
flowing into a compact-path API. [HeraldDrainSafe(Reason = "...")]
on the enclosing method, property, or field suppresses HERALD008–
HERALD012 with a required non-empty reason string. The existing
<HeraldStrictMode>true</HeraldStrictMode> MSBuild switch escalates
the warnings to errors for consumers wanting hard enforcement.
See CHANGELOG.md for per-version detail.
v0.10.2 carries the multi-policy interceptor introduced in v0.4.0 and
the typed-args high-arity perf fix from v0.10.1: property names at
every literal-template call site are normalized through the active
naming policy at the consumer's compile time, so events with the same
template produce the same downstream schema regardless of caller
variable names. Consumers committed to the default Pascal policy can
opt into a single-lane interceptor via
<HeraldNamingPolicyAssertion>Default</HeraldNamingPolicyAssertion>
for an additional ~4 ns per emit.
Each release lands here first; the commercial Herald.Core
distribution picks up the changes and layers edition-gated extensions
on top. FORK_SCOPE.md is the authoritative inventory
of what does and does not ship in OSS.
What ships in Herald.OSS
src/— the kernel, pipeline, formatters, and addons. Multi-tenancy (HeraldTenant,HeraldRegistry) and plugin trust are structural OSS features and ship with no gate.native/dotnet/— the .NET implementation of the kernel, pipeline, and bootstrap (includesStructuredLoggerand the typed-args overload set emitted by the generator).generators/— source-generator project.[HeraldLog]forstatic partiallog methods plus the per-sink[ModuleInitializer]auto-registration generator. Packed intoanalyzers/dotnet/cs/inside the nupkg so downstream sinks pick it up without an extra analyzer reference.tests/— the workhorse test suite, organised across 14 subdirectories (AOT, Addons, Bootstrap, Configuration, Diagnostics, Failures, Generators, Helpers, Otlp, Output, Pipeline, Quick, Routing, Templating). 495+ passing on net8 / 496+ on net9 / 496+ on net10. Multi-TFM clean across all three.benchmarking/library/{net8,net9,net10}/— narrow Herald-only benches across TFMs.benchmarking/comparisons/net10/— head-to-head benches against Serilog, NLog, MEL, ZLogger, and log4net.docs/howtos/— task-oriented guides (quickstart, sinks, operations).docs/guides/— architectural and SDK references.docs/benchmarks/— benchmark methodology, per-bench records, and the consolidated rollup.LICENSE/NOTICE— Apache 2.0 license and attribution.
Notable surfaces in the public SDK:
- Quick builder —
QuickLogBuilder,QuickLogResult, theHeraldRegistrystatic façade, andHeraldHostfor hosts that need per-instance isolation. - Pipeline filters — level filtering plus
WithSampling,WithThrottling, andWithAdaptiveSampling. The three sampling methods compose: each call appends a rule, and the rules build into a singleCompositeSamplingFilterwhere the first matching rule wins. Call one alone and you get the single-rule behavior it always had; layer them and you get sampling, throttling, and adaptive capture on the same pipeline. - Network sinks —
WithNetworkSink(kind, endpoint)declares a network sink by kind and endpoint without a kind-specific method. TheWithHttpJsonSink/WithOtlpSinkshortcuts are convenience wrappers over this same shape. A host that drives sinks from a catalog declares each one through this single seam instead of growing a method per destination. Pair it with a registered provider for the same kind. - Kernel + sinks —
IKernelSink,HeraldSinkBase,KernelBufferAdapter.MaterializeAndRender,LogEventBuffer,LogPropertyCompact. - Source generation + compile-time interceptor —
[HeraldLog]for explicitstatic partiallog methods, plus an automatic interceptor that bakes property names into every literal-templatelogger.Info(...)call site at the consumer's compile time. Three built-in policies (Pascal / Snake / Camel) all baked per call site; the active policy lane is selected at runtime via the publicBuiltinPolicyenum +StructuredLogger.CurrentPolicyKindproperty. Asserting consumers opt into a single-lane emit via<HeraldNamingPolicyAssertion>Default</HeraldNamingPolicyAssertion>for additional perf.[assembly: HeraldBuildAssertion]is auto-emitted into every consumer assembly so a host process can observe at runtime which compile-time shape the consumer chose. - Hot-reload —
IConfigReloadSource,FileConfigReloadSource,HotReloadableLoggingBootstrap.ExecuteReload, and the level-only fast path that recomputes theIsXxxAcceptableproperties. - Management API —
HeraldManagementApi,IManagementApiAuthorizer,AuthorizationDecision,OnAuthorizationDenied,DefaultAuthorizerFactory,LicenseStatusProvider,FileSinkPathResolver, and theRejectUnconfinedFileSinkPathsstrict-mode guard. Ships in OSS at the source level; the upstream Herald.Core gates it behind Pro. - Diagnostics channel —
HeraldRuntimeMessages/HeraldRuntimeMessagesInstance,RuntimeNotice,NoticeSeverity,BoundedNoticeBuffer<T>,DiagnosticLogFailureSink. Framework notices stay off user pipelines. - OTLP receivers — JSON and protobuf decoders under
Addons/OtlpSinks/. Destination OTLP sinks ship separately underHerald.Sinks.Otlp. - Flight recorder —
FlightRecorderLoggerandCrashSafeRingBufferfor trigger-level drain on crash. - MEL adapter —
HeraldLoggerProviderexposes Herald as aMicrosoft.Extensions.Logging.ILoggerProvider. - Multi-tenant routing —
HeraldTenant,HeraldTenantScope, per-tenantStructuredLogger. TheGenSourceGatedSinkprovenance decorator andHeraldEditioninformational badge stay visible to downstream wrappers; OSS enforces nothing against them. - Redaction fast path —
FastPathRedactorfor fixed-rule redaction at the kernel boundary. ~8 ns per event over the baseline. - Sink isolation — a throwing sink does not take down siblings;
failures route through
ILogFailureSinkon both the kernel and chain paths.
Benchmark headlines
4-property accept call, net10. Competitor rows regenerated 2026-05-16 against current package versions.
| Library | Latency | Allocation |
|---|---|---|
| Herald.OSS — asserted default | 27 ns | 0 B |
| Herald.OSS — multi-policy | 31 ns | 0 B |
| NLog | 59 ns | 248 B |
| MEL | 160 ns | 0 B |
| log4net | 192 ns | 336 B |
| Serilog | 210 ns | 720 B |
| ZLogger | 290 ns | 81 B |
Herald's two rows show the V1.1 trade. Consumers who commit at build
time to the default Pascal policy via
<HeraldNamingPolicyAssertion>Default</HeraldNamingPolicyAssertion>
get a single-lane interceptor with no runtime dispatch. Consumers who
want full Pascal / Snake / Camel coverage with runtime
WithNamingPolicy(...) switching get the multi-policy emit at every
call site. Both paths are allocation-free.
Real-sink benches confirm the delta is consumer-observable: file sink, counter sink, and null sink all land within 0.7 ns of each other. Herald's built-in sinks are async-buffered, so per-emit cost is dispatch + buffer-fill regardless of sink shape — the dispatch saving on the asserted path translates to real consumer throughput.
Full results, methodology, and reproduction commands live under
docs/benchmarks/. The consolidated rollup is
docs/benchmarks/consolidated-benchmarks.md.
Getting started
New to Herald? Start at
docs/howtos/HOWTO-QUICKSTART.md.Need a custom sink?
docs/howtos/HOWTO-SINKS.md.Running in production?
docs/howtos/HOWTO-OPERATIONS.md.Hosting an HTTP collector?
docs/howtos/HOWTO-SERVER-OSS.md.Want an operator UI?
docs/howtos/HOWTO-DASHBOARD-OSS.md.Want to see it running with no setup? Install the demo tool:
dotnet tool install --global Herald.DemoAppRun it and open the browser it points you to. The tool runs a Herald pipeline, serves a small UI, and streams live events so you can watch the pipeline work.
Want to see it embedded end-to-end? The Herald.SampleApps.HttpApi sample drops Herald.OSS into an ASP.NET Core HTTP API and lights up live-log capture via SSE.
Guides (conceptual + SDK):
docs/guides/architecture.md— the three-layer picture.docs/guides/building-sinks.md— how sinks plug in and what it costs at runtime.docs/guides/kernel-sink-pattern.md— zero-allocation custom sinks viaIKernelSink.docs/guides/aot-and-trimming.md— publishing native AOT against Herald.OSS.docs/guides/security-overview.md— what the pipeline defends and what it does not.
Install
dotnet add package Herald.OSS
Or pin the version in your project file:
<PackageReference Include="Herald.OSS" Version="0.10.2" />
Quick example
using MMP.Herald.Events;
using MMP.Herald.Quick;
var result = QuickLogBuilder.Create()
.WithConsoleSink()
.WithMinimumLevel("info")
.BuildAndCommit();
result.Logger.Info(LogCategory.App,
"User {UserId} purchased {Sku} for {Price}", 42, "alpha", 9.99);
Compose more than one filter on the same pipeline — level, sampling, throttling, and adaptive capture all chain off the builder:
var result = QuickLogBuilder.Create()
.WithConsoleSink()
.WithMinimumLevel("info")
.WithThrottling(maxPerSecond: 5000) // cap the firehose
.WithAdaptiveSampling( // but capture everything on an error spike
normalSampleRate: 10,
errorThreshold: 20)
.BuildAndCommit();
Relationship to the Herald Ecosystem
Herald.OSS is the spine — the structured-event engine every other Herald package attaches to. Ingestion shells, analytics overlays, compliance frameworks, commercial editions, sinks: each one builds on the same kernel and pipeline. None of them reimplements the data path.
Herald.OSS (Apache 2.0, this repo) ← the structured-logging spine
│
├──► Commercial editions (license-gated)
│ • Herald.Pro — resilience decorators
│ • Herald.Enterprise — WAL + audit chain
│ • Herald.Compliance — HIPAA / SOC 2 / EU AI Act overlays
│
├──► Host shells (Apache 2.0)
│ • Herald.Lean — headless, config-driven
│ • Herald.Server — HTTP collection + query
│ • Herald.Dashboard — operator UI
│ • Herald.ManagementApi
│
├──► Enrichers & addons (Apache 2.0)
│ • Herald.Sci — HPC + MPI
│ • Herald.ML — batch + epoch + GPU
│ • Herald.Embed — one-line drop-in (+ Game, Godot, MEL)
│
└──► MMP.Herald.Sinks.* (separate repo, 80+ destinations)
Sink packages ship under the MMP.Herald.Sinks.* namespace — MMP is
the MMPWorks vendor prefix. Reference the one you need
(MMP.Herald.Sinks.Seq, MMP.Herald.Sinks.Datadog,
MMP.Herald.Sinks.Otlp, and the rest) and call its
RegisterAll on the sink-provider registry. The
<Vendor>.Herald.Sinks.* shape is open for third-party sinks too.
Feature work that doesn't depend on edition machinery lands in Herald.OSS first; the commercial layer absorbs it. Edition-gated work lands directly in the commercial layer. The OSS repo and the commercial repos move forward together — neither is a frozen snapshot of the other.
Contributing
Contributions welcome. See CONTRIBUTING.md for the
process. First-time contributors will be asked to sign the
CLA — the same CLA covers
every Herald repository.
Security vulnerabilities: see SECURITY.md. Do not file
public issues.
License
| Product | Versions 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 is compatible. 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 is compatible. 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. |
-
net10.0
- Google.Protobuf (>= 3.34.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Superpower (>= 3.0.0)
-
net8.0
- Google.Protobuf (>= 3.34.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Superpower (>= 3.0.0)
-
net9.0
- Google.Protobuf (>= 3.34.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Superpower (>= 3.0.0)
NuGet packages (89)
Showing the top 5 NuGet packages that depend on Herald.OSS:
| Package | Downloads |
|---|---|
|
MMP.Licensing.Contracts
Canonical contracts surface for Herald licensing: HeraldLicenseException, HeraldLicenseRevokedException, LicenseInfo, HeraldLicense static gate, and the v2.1 EditionCapabilityPresets catalog (generated from data/licensing/{capabilities,presets}/*.json by tools/catalog-gen/). Paid packages + Server/ManagementApi ProjectReference this package so the exception types have one runtime identity at the Server middleware boundary, regardless of which paid assembly throws. |
|
|
MMP.Herald.Business
Herald observability metapackage for business and enterprise deployments. Pulls in every Herald.Sinks destination — HTTP, TCP, UDP, every Enterprise HTTP sink (Seq, Splunk, Honeycomb, Datadog, Loki, SignalFx, Sentry, PagerDuty), community transports (Elasticsearch, Slack, GenericWebhook), and the OTLP trio. Depend on this one package and pick destinations at registration time. |
|
|
MMP.Licensing
Ed25519 license verification for MMPWorks paid packages. v2 wire format + v2.1 caps/cv claim expansion. Engine (pure verifier with EditionCapabilityPresets cap-set resolution) + platform (locator, cache, gate) layers. v2.2 adds license-lifecycle surface (LicenseStateMachine, DemoModeSource, LicenseNag), trusted-clock binding (ITrustedClock three-tier resolution), and hosted check-in client (CheckInClient with Polly v8 resilience) per ADR-211/214/216/219. v2.3 consumes Herald.OSS 0.8.0 cap surface: HeraldLicenseVerifierV2.Verify seeds HeraldVersion.CurrentCapabilities (first-write-wins); new ComponentLifecycleCoordinator drives Active ↔ Unsupported transitions per ADR-220 and owns HeraldVersion.ReplaceCurrentCapabilities for post-boot cap-set changes. Source-linked by consumer products per ADR-0001. |
|
|
MMP.Herald.Sinks.Otlp
Herald sinks for the OpenTelemetry Logs Protocol. Ships three sinks (OTLP JSON, OTLP protobuf, length-delimited protobuf file) that share a hand-rolled protobuf writer so the package stays AOT-clean — no generated messages, no reflection, no IMessage codegen. |
|
|
MMP.Herald.Sinks.Honeycomb
Posts Herald log events to Honeycomb's batch ingest endpoint. Maps events onto Honeycomb's flat data-object shape with per-event sample-rate support. |
GitHub repositories
This package is not used by any popular GitHub repositories.