SigNoz + no Jaeger in StarConflictsRevolt
Policies that keep the org coherent
name: Dogfood SigNoz Aspire overview: Wire StarConflictsRevolt AppHost to in-graph SigNoz via Novolis.Aspire.Hosting.Signoz, remove Jaeger entirely, and dual-export OTLP to both the Aspire dashboard and SigNoz without replacing default Aspire telemetry. todos:
- id: fix-signoz-exporter-env
content: "novolis-aspire: WithSignozOtlpExporter uses SIGNOZ_OTEL_ only (not OTEL_EXPORTER_OTLP_); README + unit test" status: completed
- id: dual-otlp-exporters
content: "StarConflicts: shared dual OTLP helper in ServiceDefaults; use from WorkerOpenTelemetry + ConfigureOpenTelemetry" status: completed
- id: apphost-signoz-no-jaeger
content: "StarConflicts AppHost: Add Novolis.Aspire.Hosting.Signoz, AddSignoz, remove Jaeger + simplify LocalObservability" status: completed
- id: docs-scripts-manifest
content: Update aspire.md, aspire-run.ps1, local-observability READMEs, manifest, troubleshooting, cursor-agent-setup status: completed
- id: validate-stack
content: "Manual: aspire run, dashboard traces + SigNoz UI, no jaeger resource" status: cancelled isProject: false
Target repo: `D:\github\StarConflictsRevolt\aspire` (not novolis-dogfooding).
Goals:
- Remove Jaeger completely — delete
AddJaegerIfEnabled,LocalObservabilityStack.Jaeger, default-to-jaeger behavior, manifestjaegerbindings, and all docs/scripts referencing Jaeger. - Use SigNoz — in-graph via `Novolis.Aspire.Hosting.Signoz` (
AddSignoz/WithSignozOtlpExporter), dogfooding the novolis-aspire package. - Keep Aspire default telemetry — do not overwrite
OTEL_EXPORTER_OTLP_ENDPOINT(Aspire dashboard OTLP). Also export to SigNoz via secondary env vars + named OTLP exporters.
flowchart LR
subgraph apps [Web-api + Desktop-launcher]
OTel[OpenTelemetry SDK]
end
subgraph apphost [AppHost]
SignozStack["AddSignoz('signoz')"]
end
Dash[Aspire Dashboard OTLP]
SigNoz[SigNoz collector :4317]
OTel -->|"OTEL_EXPORTER_OTLP_ENDPOINT"| Dash
OTel -->|"SIGNOZ_OTEL_*"| SigNoz
SignozStack --> SigNozPrerequisite: novolis-aspire package fix
`WithSignozOtlpExporter` currently sets OTEL_EXPORTER_OTLP_ENDPOINT, which replaces the dashboard sink. Fix in novolis-aspire first (or in same PR if both repos change):
| Env var | Purpose |
|---|---|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | **Untouched** — Aspire injects dashboard OTLP |
| `SIGNOZ_OTEL_EXPORTER_OTLP_ENDPOINT` | SigNoz collector gRPC URI from `signoz.Resource.OtlpGrpcUriExpression` |
| `SIGNOZ_OTEL_EXPORTER_OTLP_PROTOCOL` | `grpc` |
Keep .WithReference(signoz) for dashboard connection strings.
Add unit test: WithSignozOtlpExporter must not add OTEL_EXPORTER_OTLP_ENDPOINT env annotation.
Package consumption in StarConflicts: Add Novolis.Aspire.Hosting.Signoz to `Directory.Packages.props` and `StarConflictsRevolt.Aspire.AppHost.csproj`. Use published NuGet 0.1.0-preview.1 (or local feed / dotnet pack from d:\novolis\novolis-aspire until published).
1. Simplify local observability (AppHost)
File: `LocalObservabilityAppHostExtensions.cs`
| Before | After |
|---|---|
| `LocalObservabilityStack`: `None`, `Jaeger`, `Signoz` | `None`, `Signoz` only |
| Unset env → **Jaeger** | Unset env → **Signoz** (in-graph stack) |
| `AddJaegerIfEnabled` | **Delete** |
| `Signoz` → hard-coded `http://127.0.0.1:4318` | `WithSignozOtlpExporter(signoz)` from novolis package |
| `WithLocalObservabilityOtlp(..., jaeger)` | `WithLocalObservabilityOtlp(..., signoz)` — `none` = no-op; `signoz` = `.WithSignozOtlpExporter(signoz)` |
File: `AppHost.cs`
var localObsStack = LocalObservabilityAppHostExtensions.ParseLocalObservabilityStack();
var signoz = builder.AddSignozIfEnabled(localObsStack); // wraps AddSignoz when stack != None
// Web-api / Desktop-launcher:
.WithLocalObservabilityOtlp(localObsStack, signoz);- Remove
jaegervariable andApplyGameClientEnvsjaeger parameter. AddSignozIfEnabled: when stack isSignoz, callbuilder.AddSignoz("signoz"); returnnullforNone.- Do not
WaitFor(signoz)on projects (same resilience as old Jaeger — slow/broken observability must not block Web-api / launcher).
Deprecate external compose workflow: `scripts/local-obs-signoz.ps1` and `tools/local-observability/signoz/` — remove or mark archived; primary path is AppHost AddSignoz.
2. Dual OTLP export (ServiceDefaults + worker)
OpenTelemetry .NET cannot call UseOtlpExporter() twice (issue #5538). When SIGNOZ_OTEL_EXPORTER_OTLP_ENDPOINT is set, use named AddOtlpExporter for dashboard + SigNoz; otherwise keep existing UseOtlpExporter() for dashboard-only.
New shared helper (e.g. OpenTelemetryOtlpExporterExtensions.cs in `StarConflictsRevolt.Aspire.ServiceDefaults`):
AddDualOtlpExporters(IOpenTelemetryBuilder, IConfiguration)— registers:- dashboard exporter when
OTEL_EXPORTER_OTLP_ENDPOINTis set (reads standard env/protocol). - signoz exporter when
SIGNOZ_OTEL_EXPORTER_OTLP_ENDPOINTis set (protocol fromSIGNOZ_OTEL_EXPORTER_OTLP_PROTOCOL, defaultgrpc). - Applies to logging, metrics, and tracing (mirror current
UseOtlpExportercoverage).
Update:
- `Extensions.cs` — replace
if (useOtlpExporter) openTelemetry.UseOtlpExporter()with dual helper. - `WorkerOpenTelemetryHostBuilderExtensions.cs` — same (reference ServiceDefaults or duplicate minimal helper in Observability.Hosting to avoid circular refs; prefer shared static helper in ServiceDefaults referenced by worker project).
Adjust useOtlpExporter / console-exporter gating: treat “any OTLP configured” as OTEL_EXPORTER_OTLP_ENDPOINT or SIGNOZ_OTEL_EXPORTER_OTLP_ENDPOINT.
3. Scripts, manifest, docs
| File | Change |
|---|---|
| [`scripts/aspire-run.ps1`](D:\github\StarConflictsRevolt\scripts\aspire-run.ps1) | `-LocalObsStack` values: `none`, `signoz` only; default comment = SigNoz in-graph |
| [`aspire-manifest.json`](D:\github\StarConflictsRevolt\aspire\StarConflictsRevolt.Aspire.AppHost\aspire-manifest.json) | Remove `jaeger` resource and `{jaeger.bindings.otlp.url}`; add SigNoz OTLP connection refs if manifest is hand-maintained (regenerate via `aspire manifest` if that is the repo norm) |
| [`docs/operations/aspire.md`](D:\github\StarConflictsRevolt\docs\operations\aspire.md) | Local APM section: SigNoz default, no Jaeger, dual export behavior |
| [`tools/local-observability/README.md`](D:\github\StarConflictsRevolt\tools\local-observability\README.md) | Replace Jaeger table with SigNoz in-graph + `none` |
| [`docs/operations/troubleshooting.md`](D:\github\StarConflictsRevolt\docs\operations\troubleshooting.md) | Jaeger troubleshooting → SigNoz container health |
| [`docs/operations/cursor-agent-setup.md`](D:\github\StarConflictsRevolt\docs\operations\cursor-agent-setup.md) | Point agents at **signoz** UI endpoint, not jaeger |
4. Validation
dotnet pack/ publishNovolis.Aspire.Hosting.Signozif not on feed; restore AppHost.pwsh ./scripts/aspire-run.ps1(default SigNoz stack starts with AppHost).- Aspire dashboard: structured logs and traces for Web-api / Desktop-launcher still appear.
- SigNoz UI: open
signoz-signozHTTP endpoint from dashboard; traces/metrics from apps visible. - No
jaegerresource inaspire describe. pwsh ./scripts/aspire-run.ps1 -LocalObsStack none— no SigNoz containers; dashboard OTLP only.
Out of scope
- novolis-dogfooding AppHost (wrong repo for this task).
- CI running full SigNoz stack (container-heavy).
- Changing Garnet / Raven / Azurite orchestration.
Dependency order
- novolis-aspire:
WithSignozOtlpExporterenv-var fix + test - StarConflicts: dual OTLP helper
- StarConflicts: AppHost + observability refactor + docs
- Manual validation