Synergy.Platform.Observability 1.0.0-preview.1

This is a prerelease version of Synergy.Platform.Observability.
dotnet add package Synergy.Platform.Observability --version 1.0.0-preview.1
                    
NuGet\Install-Package Synergy.Platform.Observability -Version 1.0.0-preview.1
                    
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="Synergy.Platform.Observability" Version="1.0.0-preview.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Synergy.Platform.Observability" Version="1.0.0-preview.1" />
                    
Directory.Packages.props
<PackageReference Include="Synergy.Platform.Observability" />
                    
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 Synergy.Platform.Observability --version 1.0.0-preview.1
                    
#r "nuget: Synergy.Platform.Observability, 1.0.0-preview.1"
                    
#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 Synergy.Platform.Observability@1.0.0-preview.1
                    
#: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=Synergy.Platform.Observability&version=1.0.0-preview.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Synergy.Platform.Observability&version=1.0.0-preview.1&prerelease
                    
Install as a Cake Tool

Synergy.Platform.Observability

One dependency puts a Synergy service on Grafana Cloud: OpenTelemetry traces and metrics, Serilog JSON logs carrying TraceId/SpanId, and user/tenant context on both — so a single log line links through to the full distributed trace behind it.

Two packages, one concern:

Package For
src/ Synergy.Platform.Observability (NuGet) .NET APIs
react/ @synergytek/observability (npm, git install) React micro-frontends

.NET

dotnet add package Synergy.Platform.Observability --prerelease
using Synergy.Platform.Observability;

builder.AddSynergyObservability(o =>
{
    o.Marten      = true;   // event-store spans
    o.Wolverine   = true;   // keeps traces alive across Kafka
    o.MassTransit = true;
});

var app = builder.Build();

app.UseAuthentication();
app.UseSynergyObservability();   // after auth - the claims must be populated

That replaces roughly 30 lines and four package references per service.

What it does

  • Serilog → JSON on stdout, enriched with TraceId, SpanId, service, version
  • Traces → ASP.NET Core, HttpClient, EF Core, plus whichever sources you opt into
  • Metrics → ASP.NET Core, HttpClient, .NET runtime
  • Export → OTLP, configured entirely from the environment
  • Middleware → user_id and tenant_id onto every log line and span

Configuration is environment-only, by design

There is no endpoint, protocol or header setting on the options object. That is not an oversight.

The previous per-service code set Endpoint and Protocol in C#. Two consequences: OTEL_EXPORTER_OTLP_PROTOCOL was ignored, and setting Endpoint programmatically suppresses the /v1/traces path the HTTP exporter appends — so requests went to the root and vanished. Both failed silently. Every service was fully instrumented and exporting nothing, for months, with no error in any log.

A package that cannot accept an endpoint cannot have that bug reintroduced.

Variable Required Notes
OTEL_SERVICE_NAME recommended Falls back to the assembly name. Also used as Serilog's service field, so both join on it
OTEL_EXPORTER_OTLP_ENDPOINT to export Unset ⇒ the exporter is not registered (see below)
OTEL_EXPORTER_OTLP_PROTOCOL for Grafana Cloud Must be http/protobuf. Unset, the SDK defaults to gRPC and silently sends nothing to an HTTP gateway
OTEL_EXPORTER_OTLP_HEADERS for Grafana Cloud Authorization=Basic%20<base64> — from a Secret, never a values file
OTEL_TRACES_SAMPLER / _ARG recommended parentbased_traceidratio / 0.1

Unset endpoint means no exporter at all. The SDK would otherwise default to localhost:4317 and retry forever against nothing — noise in local development, and misleading in a half-configured environment where it looks like it is trying. Logging still works; only export is skipped.

Sampling and Marten

o.Marten = true is worth having — it is how a slow request shows you the slow query. But Marten's TrackConnections = TrackLevel.Normal emits a span per database connection. At 100% sampling that alone will exhaust a free-tier allowance in days. Keep OTEL_TRACES_SAMPLER_ARG at 0.1 and raise it only while chasing something specific.

Target frameworks

net8.0 and net10.0. Most Synergy APIs are on 8; Synergy.Base.Workflow is already on 10. A single-target package would force one of them to move before it could adopt this.


React

// package.json
"@synergytek/observability": "git+https://github.com/SynergyTek/Synergy.Platform.Observability.git#main"

Same git-install mechanism as @synergytek/react. Plain ESM, no JSX, no build step.

// src/observability.js
import { initSynergyObservability } from '@synergytek/observability';

initSynergyObservability({
  url:         '__FARO_URL__',
  name:        '__SERVICE_NAME__',
  version:     '__SERVICE_VERSION__',
  environment: '__FARO_ENVIRONMENT__',
  tracePropagationTargets: ['https://api.aitalkx.com'],
});
// src/index.jsx
import { FaroErrorBoundary } from '@synergytek/observability';

<FaroErrorBoundary fallback={<ErrorFallback />}>
  <App />
</FaroErrorBoundary>

Why the placeholders are passed through, not read from the environment

A browser bundle has no environment. These apps are built once and configured per environment by entrypoint.sh, which seds __PLACEHOLDER__ tokens inside the built JavaScript at container start. That is what makes a config change an apply-and-restart rather than a rebuild — verified: the tokens survive rsbuild minification intact.

The package keeps the url.startsWith('__') guard, so an app deployed without FARO_URL stays silent instead of throwing on every page load.

Add any new variable to the for var in ... list in the app's entrypoint.sh, or the substitution will not happen.

tracePropagationTargets is not optional in practice

Without it the browser will not attach trace headers to cross-origin API calls, and frontend sessions never join up with backend traces — which is most of the value. List your API origins.

Where to install it

In the host shell, not each remote. Synergy.BackOffice.Host.UI loads all six micro-frontends via module federation, so one instance there covers every one of them. A federated remote's own entry point may never run when the host consumes it, and several Faro instances would fight over session IDs.

Pass router: false in a remote that does not own a router.


What this replaces

Before, in every API:

builder.Host.UseSerilog((ctx, cfg) => cfg
    .ReadFrom.Configuration(ctx.Configuration)
    .Enrich.FromLogContext()
    .Enrich.WithSpan()
    .Enrich.WithProperty("service", Environment.GetEnvironmentVariable("OTEL_SERVICE_NAME") ?? "unknown")
    .Enrich.WithProperty("version", Assembly.GetExecutingAssembly().GetName().Version?.ToString())
    .WriteTo.Console(new CompactJsonFormatter()));

builder.Services.AddOpenTelemetry()
    .ConfigureResource(r => r.AddService(/* ... */))
    .WithTracing(t => t
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddEntityFrameworkCoreInstrumentation()
        .AddSource("Marten")
        .AddSource("Wolverine")
        .AddSource("MassTransit")
        .AddOtlpExporter())
    .WithMetrics(m => m
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddRuntimeInstrumentation()
        .AddMeter("MassTransit")
        .AddOtlpExporter());

app.Use(async (ctx, next) => { /* ~15 lines of user/tenant plumbing */ });

After: the six lines at the top of this page, and four fewer PackageReference entries.

Multiply by seven APIs and seven UIs. More to the point: when the next thing is wrong, it is wrong in one repo.


Releasing

Versioning follows Synergy.Platform.Marten: <Version> in the csproj, consumers pin exact.

dotnet pack src/Synergy.Platform.Observability/Synergy.Platform.Observability.csproj -c Release -o artifacts
dotnet nuget push artifacts/Synergy.Platform.Observability.1.0.0-preview.1.nupkg \
  --source https://api.nuget.org/v3/index.json --api-key $NUGET_API_KEY

The npm side has no registry step — consumers install from the git URL, so a merge to main is the release. Tag it so apps can pin: #v1.0.0-preview.1 instead of #main.


  • Synergy-Devops/OBSERVABILITY.md — deployment runbook: Grafana Cloud setup, environment variables, rollout order, and troubleshooting for every failure hit during the kptcl-dev rollout
  • Synergy.Base.Template PR #82 — the original hand-rolled implementation this package generalises
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 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. 
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-preview.1 496 8/11/2026