Observability · how it works
Follow one name all the way
A name is written once in the contract. obs turns it into a typed constant per language. Your code passes that constant to a port. The adapter sends it to Alloy, Alloy stores it, and Grafana queries, alerts and draws it.
- Pick a signal and a language below; every box and code sample redraws. PROPOSED · this site
- The code is the shape from the design record. None of it is built yet. DECIDED · A193 tab 3
Pick a signal and a language
Language
Log · Bun
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
events: checkout.charge.failed: { level: error, description: A card charge did not succeed }2 · Generated name
as constkeeps the exact string type; one list cannot drift. DECIDED · A193 tab 3// src/observability/signals.g.ts export const Event = { CheckoutChargeFailed: 'checkout.charge.failed', } as const; export type Event = (typeof Event)[keyof typeof Event];3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
if (!r.ok) this.t.logger.error( { event: Event.CheckoutChargeFailed, 'error.type': r.error.code }, 'charge failed', );4 · Adapter
sdk-logsLoggerProvider + OTLP exporter. Addsdurian.and the OTLPcloud/ event EventNamefield, the trace and span ids, and the LPSM keys. pino stays for the console. DECIDED · A193 tab 45 · Transport
OTLP → Alloy →
loki:3100/TESTED · Q15otlp 6 · Store
Loki. The event key is stored as
durian_cloud_event="checkout.. PROPOSED · A193 tab 1, jaeda call 10charge. failed" 7 · Query, alert, dashboard
LogQL. Alert query B in
charge-errors/. DECIDED · A193 tab 2critical. yaml sum(count_over_time({service_name="checkout"} | durian_cloud_event="checkout.charge.failed" [5m])) > 10
Log · Go
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
events: checkout.charge.failed: { level: error, description: A card charge did not succeed }2 · Generated name
A struct with a hidden field, so no other package can make one. TESTED · Q7
// internal/signals/signals_gen.go type Event struct{ name string } // hidden field: closed func (e Event) EventName() string { return e.name } var EventCheckoutChargeFailed = Event{"checkout.charge.failed"}3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
log.Error(ctx, signals.EventCheckoutChargeFailed, "charge failed", interfaces.Attr("error.type", code)) // ctx first: the trace rides along4 · Adapter
sdk/+log otlploghttp, record built withctx, so it carries the trace. DECIDED · A193 tab 45 · Transport
OTLP → Alloy →
loki:3100/TESTED · Q15otlp 6 · Store
Loki. The event key is stored as
durian_cloud_event="checkout.. PROPOSED · A193 tab 1, jaeda call 10charge. failed" 7 · Query, alert, dashboard
LogQL. Alert query B in
charge-errors/. DECIDED · A193 tab 2critical. yaml sum(count_over_time({service_name="checkout"} | durian_cloud_event="checkout.charge.failed" [5m])) > 10
Log · .NET
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
events: checkout.charge.failed: { level: error, description: A card charge did not succeed }2 · Generated name
A sealed class with a private constructor; a C#
enumis not closed. DECIDED · A193 tab 3// Lib/Observability/Signals.g.cs public sealed class Event : IEventName { private Event(string n) => Name = n; // private: closed public string Name { get; } public static readonly Event CheckoutChargeFailed = new("checkout.charge.failed"); }3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
log.Error(Event.CheckoutChargeFailed, "charge failed", attrs); // Error is an extension of // Log(LogLevel level, TEvent evt, string message, Attributes? a = null, Exception? error = null)4 · Adapter
ILogger+ OpenTelemetry logging, as today; it already stamps the current span. DECIDED · A193 tab 45 · Transport
OTLP → Alloy →
loki:3100/TESTED · Q15otlp 6 · Store
Loki. The event key is stored as
durian_cloud_event="checkout.. PROPOSED · A193 tab 1, jaeda call 10charge. failed" 7 · Query, alert, dashboard
LogQL. Alert query B in
charge-errors/. DECIDED · A193 tab 2critical. yaml sum(count_over_time({service_name="checkout"} | durian_cloud_event="checkout.charge.failed" [5m])) > 10
Log · Flutter
1 · Contract
Flutter uses its own names in the same contract shape. DECIDED · A193 tab 3
events: checkout.pay.tapped: { level: info }2 · Generated name
Dart enums are closed and carry the wire name. DECIDED · A193 tab 3
// lib/src/observability/signals.g.dart enum Event implements EventName { checkoutPayTapped('checkout.pay.tapped'); const Event(this.wireName); @override final String wireName; }3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
log.info(Event.checkoutPayTapped, 'pay tapped'); // with an error: log.error(event, message, error: e, stack: s)4 · Adapter
Faro adapter:
pushLog(msg, level, context: {durian.; with an error →cloud/ event: …}) pushError. Context values must be strings. DECIDED · A193 tab 3 TESTED · Q17, wire only5 · Transport
Faro POST
…/→ Alloycollect faro.→ Loki TESTED · Q17receiver 6 · Store
Loki. Faro fields arrive renamed (log context as
context_*, identity asapp_name); obs maps them. TESTED · Q177 · Query, alert, dashboard
LogQL (field name per spike). Exact field spelling is for obs to map. TESTED · Q17 OPEN · mapping
{app_name="…"} | context_durian_cloud_event="checkout.pay.tapped"
Metric · Bun
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
metrics: checkout.charges: { kind: counter, unit: "{charge}", budget: 30 }2 · Generated name
as constkeeps the exact string type; one list cannot drift. DECIDED · A193 tab 3export const Counter = { CheckoutCharges: 'checkout.charges', } as const; export type Counter = (typeof Counter)[keyof typeof Counter];3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
this.t.meter.increment(Counter.CheckoutCharges, 1, { 'payment.provider': provider, outcome: r.outcome, // free-form label });4 · Adapter
sdk-metrics; every instrument is made at start from the contract. A View caps label sets at budget × 4. DECIDED · A193 tab 45 · Transport
OTLP → Alloy →
vm:8428/TESTED · Q15opentelemetry 6 · Store
VictoriaMetrics.
checkout.is stored ascharges checkout_charges_total. PROPOSED · A193 tab 1, jaeda call 107 · Query, alert, dashboard
PromQL. Alert query A in
charge-errors/. DECIDED · A193 tab 2critical. yaml sum(rate(checkout_charges_total{outcome="error"}[5m])) > 1
Metric · Go
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
metrics: checkout.charges: { kind: counter, unit: "{charge}", budget: 30 }2 · Generated name
A struct with a hidden field, so no other package can make one. TESTED · Q7
type Counter struct{ name string } var CounterCheckoutCharges = Counter{"checkout.charges"}3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
meter.Increment(ctx, signals.CounterCheckoutCharges, 1, interfaces.Attr("payment.provider", provider), interfaces.Attr("outcome", outcome))4 · Adapter
sdk/; instruments frommetric signals.; a View caps label sets. DECIDED · A193 tab 4Contract 5 · Transport
OTLP → Alloy →
vm:8428/TESTED · Q15opentelemetry 6 · Store
VictoriaMetrics.
checkout.is stored ascharges checkout_charges_total. PROPOSED · A193 tab 1, jaeda call 107 · Query, alert, dashboard
PromQL. Alert query A in
charge-errors/. DECIDED · A193 tab 2critical. yaml sum(rate(checkout_charges_total{outcome="error"}[5m])) > 1
Metric · .NET
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
metrics: checkout.charges: { kind: counter, unit: "{charge}", budget: 30 }2 · Generated name
A sealed class with a private constructor; a C#
enumis not closed. DECIDED · A193 tab 3public sealed class Counter : ICounterName { private Counter(string n) => Name = n; public string Name { get; } public static readonly Counter CheckoutCharges = new("checkout.charges"); }3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
meter.Increment(Counter.CheckoutCharges, 1, attrs); // method name: shape, not final4 · Adapter
System.; instruments fromDiagnostics. Metrics. Meter SignalContract.; a View caps label sets. DECIDED · A193 tab 4Data 5 · Transport
OTLP → Alloy →
vm:8428/TESTED · Q15opentelemetry 6 · Store
VictoriaMetrics.
checkout.is stored ascharges checkout_charges_total. PROPOSED · A193 tab 1, jaeda call 107 · Query, alert, dashboard
PromQL. Alert query A in
charge-errors/. DECIDED · A193 tab 2critical. yaml sum(rate(checkout_charges_total{outcome="error"}[5m])) > 1
Metric · Flutter
1 · Contract
Flutter uses its own names in the same contract shape. DECIDED · A193 tab 3
metrics: checkout.screen.load.duration: { kind: histogram, unit: s }2 · Generated name
Dart enums are closed and carry the wire name. DECIDED · A193 tab 3
enum Histogram implements HistogramName { checkoutScreenLoadDuration('checkout.screen.load.duration'); const Histogram(this.wireName); @override final String wireName; }3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
meter.record(Histogram.checkoutScreenLoadDuration, 0.42);4 · Adapter
Faro adapter:
pushMeasurement({'value': v}, name). It becomes a Loki line, not a metric. No gauges. DECIDED · A193 tab 35 · Transport
Faro POST
…/→ Alloycollect faro.→ Loki (as a log line) TESTED · Q17receiver 6 · Store
Loki, as measurement lines. Dashboards read them with LogQL
unwrap, not PromQL. DECIDED · A193 tab 37 · Query, alert, dashboard
LogQL. Flutter has no PromQL metrics. DECIDED · A193 tab 3
a LogQL query that `unwrap`s the measurement value
Span · Bun
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
spans: checkout.charge: { kind: internal }2 · Generated name
as constkeeps the exact string type; one list cannot drift. DECIDED · A193 tab 3export const Span = { CheckoutCharge: 'checkout.charge', } as const; export type Span = (typeof Span)[keyof typeof Span];3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
return this.t.tracer.run(Span.CheckoutCharge, { 'payment.provider': provider }, async () => { const r = await this.gateway.charge(order, provider); // auto http.client span nests here return r; // Err → span status Error });4 · Adapter
tracer.. OnstartActiveSpan(name, {kind, attributes}, fn) Err,recordProblemsets status Error and aproblemevent. DECIDED · A193 tab 4 TESTED · Q55 · Transport
OTLP → Alloy →
tempo:4318TESTED · Q156 · Store
Tempo. Tempo's metrics generator also writes
traces_spanmetrics_calls_totalto VictoriaMetrics. DECIDED · A193 tab 5 NOT TESTED · generator not in the spike config7 · Query, alert, dashboard
PromQL on span metrics. Alerts on traces use span metrics, not TraceQL (experimental for alerts). DECIDED · A193 tab 2
traces_spanmetrics_calls_total{ service_name="checkout", status_code="STATUS_CODE_ERROR"}
Span · Go
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
spans: checkout.charge: { kind: internal }2 · Generated name
A struct with a hidden field, so no other package can make one. TESTED · Q7
type Span struct{ name string } var SpanCheckoutCharge = Span{"checkout.charge"}3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
err := tracer.Run(ctx, signals.SpanCheckoutCharge, attrs, func(ctx context.Context, span interfaces.Span[signals.Event]) error { return gateway.Charge(ctx, order, provider) // error → span status Error })4 · Adapter
tracer.+Start(ctx, name) defer span.insideEnd() Run; an error sets status Error. DECIDED · A193 tab 4 TESTED · Q115 · Transport
OTLP → Alloy →
tempo:4318TESTED · Q156 · Store
Tempo. Tempo's metrics generator also writes
traces_spanmetrics_calls_totalto VictoriaMetrics. DECIDED · A193 tab 5 NOT TESTED · generator not in the spike config7 · Query, alert, dashboard
PromQL on span metrics. Alerts on traces use span metrics, not TraceQL (experimental for alerts). DECIDED · A193 tab 2
traces_spanmetrics_calls_total{ service_name="checkout", status_code="STATUS_CODE_ERROR"}
Span · .NET
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
spans: checkout.charge: { kind: internal }2 · Generated name
A sealed class with a private constructor; a C#
enumis not closed. DECIDED · A193 tab 3public sealed class Span : ISpanName { private Span(string n) => Name = n; public string Name { get; } public static readonly Span CheckoutCharge = new("checkout.charge"); }3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
return await tracer.Run(Span.CheckoutCharge, attrs, async span => await gateway.Charge(order, provider)); // Err → span status Error4 · Adapter
ActivitySource.;StartActivity(name, kind) Err→ status Error +problemevent. DECIDED · A193 tab 4 TESTED · Q115 · Transport
OTLP → Alloy →
tempo:4318TESTED · Q156 · Store
Tempo. Tempo's metrics generator also writes
traces_spanmetrics_calls_totalto VictoriaMetrics. DECIDED · A193 tab 5 NOT TESTED · generator not in the spike config7 · Query, alert, dashboard
PromQL on span metrics. Alerts on traces use span metrics, not TraceQL (experimental for alerts). DECIDED · A193 tab 2
traces_spanmetrics_calls_total{ service_name="checkout", status_code="STATUS_CODE_ERROR"}
Span · Flutter
1 · Contract
Flutter uses its own names in the same contract shape. DECIDED · A193 tab 3
spans: checkout.pay: { kind: client } # example name: the record names no Flutter span2 · Generated name
Dart enums are closed and carry the wire name. DECIDED · A193 tab 3
enum Span implements SpanName { checkoutPay('checkout.pay'); // example name const Span(this.wireName); @override final String wireName; }3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
await tracer.run(Span.checkoutPay, attrs, () => api.pay(order)); // outgoing HTTP carries traceparent to the backend4 · Adapter
Faro adapter:
startSpan(name, fn, attributes). DECIDED · A193 tab 3 TESTED · Q17, wire only5 · Transport
Faro POST
…/→ Alloycollect faro.→ Tempo TESTED · Q17receiver 6 · Store
Tempo, linked to the backend span by
traceparent. TESTED · Q177 · Query, alert, dashboard
Tempo. One trace across phone and server. TESTED · Q17, wire
the app span, then the backend span under it
Profile · Bun
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
profiles: types: [cpu, alloc, inuse] scopes: [checkout.fraud.score]2 · Generated name
as constkeeps the exact string type; one list cannot drift. DECIDED · A193 tab 3export const ProfileScope = { CheckoutFraudScore: 'checkout.fraud.score', } as const; export type ProfileScope = (typeof ProfileScope)[keyof typeof ProfileScope];3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
const score = await this.t.profiler.scope( ProfileScope.CheckoutFraudScore, { 'payment.provider': provider }, () => this.fraud.score(order), );4 · Adapter
node:inspectorCPU profile → our ~80-line pprof converter. CPU only, no span link: samples are matched by time window. TESTED · Q3; Q45 · Transport
pprof → Alloy
pyroscope.→receive_http pyroscope.→ Pyroscope DECIDED · A193 tab 1 #21 TESTED · Q14write 6 · Store
Pyroscope. Samples carry
service_nameand the labeldurian.. DECIDED · A193 tab 4cloud/ profile. scope=checkout. fraud. score 7 · Query, alert, dashboard
Pyroscope. No alerts on profiles. You open a flame graph from a dashboard panel or from a span. DECIDED · A193 tab 5
flame graph: service_name="checkout" label durian.cloud/profile.scope="checkout.fraud.score"
Profile · Go
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
profiles: types: [cpu, alloc, inuse] scopes: [checkout.fraud.score]2 · Generated name
A struct with a hidden field, so no other package can make one. TESTED · Q7
type ProfileScope struct{ name string } var ProfileScopeCheckoutFraudScore = ProfileScope{"checkout.fraud.score"}3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
profiler.Scope(ctx, signals.ProfileScopeCheckoutFraudScore, attrs, func(ctx context.Context) { score = fraud.Score(ctx, order) }) // inside: pprof.Do(ctx, pprof.Labels(…))4 · Adapter
pyroscope-go+otel-profiling-go: each sample carries the span id, so a span gets its own flame graph. TESTED · Q85 · Transport
pprof → Alloy
pyroscope.→receive_http pyroscope.→ Pyroscope DECIDED · A193 tab 1 #21 TESTED · Q14write 6 · Store
Pyroscope. Samples carry
service_nameand the labeldurian.. DECIDED · A193 tab 4cloud/ profile. scope=checkout. fraud. score 7 · Query, alert, dashboard
Pyroscope. No alerts on profiles. You open a flame graph from a dashboard panel or from a span. DECIDED · A193 tab 5
flame graph: service_name="checkout" label durian.cloud/profile.scope="checkout.fraud.score"
Profile · .NET
1 · Contract
Written by hand, once per service. DECIDED · A193 tab 3
profiles: types: [cpu, alloc, inuse] scopes: [checkout.fraud.score]2 · Generated name
A sealed class with a private constructor; a C#
enumis not closed. DECIDED · A193 tab 3public sealed class ProfileScope : IProfileScopeName { private ProfileScope(string n) => Name = n; public string Name { get; } public static readonly ProfileScope CheckoutFraudScore = new("checkout.fraud.score"); }3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
var score = profiler.Scope(ProfileScope.CheckoutFraudScore, attrs, () => fraud.Score(order)); // Pyroscope label API (name to check)4 · Adapter
Pyroscope .NET profiler + span processor: span-filtered flame graphs. Needs the profiler files in the image. TESTED · Q9
5 · Transport
pprof → Alloy
pyroscope.→receive_http pyroscope.→ Pyroscope DECIDED · A193 tab 1 #21 TESTED · Q14write 6 · Store
Pyroscope. Samples carry
service_nameand the labeldurian.. DECIDED · A193 tab 4cloud/ profile. scope=checkout. fraud. score 7 · Query, alert, dashboard
Pyroscope. No alerts on profiles. You open a flame graph from a dashboard panel or from a span. DECIDED · A193 tab 5
flame graph: service_name="checkout" label durian.cloud/profile.scope="checkout.fraud.score"
Profile · Flutter
1 · Contract
Flutter uses its own names in the same contract shape. DECIDED · A193 tab 3
# profiles: refused for Dart # obs: "Faro has no profiling"2 · Generated name
Dart enums are closed and carry the wire name. DECIDED · A193 tab 3
// nothing generated: obs refuses profiles for Dart3 · Your code calls the port
Only typed names are accepted; labels are free-form. DECIDED · A193 tab 1 #5; #7
// The Profiler port is a no-op in Flutter: it just runs fn()4 · Adapter
No adapter: Faro has no profiling. DECIDED · A193 tab 3
5 · Transport
— nothing is sent
6 · Store
—
7 · Query, alert, dashboard
No profiles from Flutter. DECIDED · A193 tab 5
The 7 stops, in words
- Contract. You write the name once, by hand. Names are lowercase and dotted, and unique across kinds. Example:
checkout.cannot be both a counter and an event. DECIDED · A193 tab 3 PROPOSED · A193 tab 1, jaeda call 1charges - Generated names.
obs genwrites one file of constants per service. Same contract → same bytes. Constants only, never functions. DECIDED · A193 tab 1 #7 - Port call. Your code calls a port with the constant. Labels are free-form. The trace context travels by itself. DECIDED · A193 tab 3
- Adapter. The
*-otellibrary turns the call into OpenTelemetry. It adds the event key, the 4 LPSM keys (where the signal came from: landscape, platform, service, module) and the error details. DECIDED · A193 tab 4 - Transport. Logs, metrics and spans go as OTLP to Alloy. Profiles go as pprof to Alloy. Flutter goes through Faro to Alloy. DECIDED · A193 tab 5; A193 tab 1 #21
- Store. Loki, VictoriaMetrics, Tempo or Pyroscope. Each store spells names its own way. Example:
checkout.is stored ascharges checkout_charges_total. PROPOSED · A193 tab 1, jaeda call 10 - Query, alert, dashboard. Grafana reads the store. Alerts and dashboards live in
observability/and obs checks them. DECIDED · A193 tab 2
How a wrong name fails
- Bun: a log without an event →
TS2345: Property 'event' is missing. DECIDED · A193 tab 3 - Bun: a counter passed where a histogram is needed →
TS2345: '"checkout.. DECIDED · A193 tab 3charges"' is not assignable to parameter of type 'Histogram' - Go: building a name outside the generated file →
implicit assignment to unexported field name in struct literal. TESTED · Q7, evidence.txt - Go: a plain string →
cannot use "…" (untyped string constant) as signals.. DECIDED · A193 tab 3 NOT TESTED · not in the spike evidenceEvent value - Go's one hole: an empty
signals.compiles. AEvent{} forbidigolint rule or a short analyzer catches it; the port also errors on an empty name. TESTED · Q7 - Only Go's closed names were spiked. The Bun, .NET and Dart error texts come from the design record, not a run. DECIDED · A193 tab 3 NOT TESTED · not run
- .NET:
new Event("x")→CS0122: inaccessible. DECIDED · A193 tab 3 - Dart: a histogram passed as an event →
The argument type 'Histogram' can't be assigned to the parameter type 'Event'. DECIDED · A193 tab 3
Errors in a Result mark the span
- When your function returns
Err(problem),tracer.marks the span. No exception needed. DECIDED · A193 tab 1 #15 TESTED · Q5; Q11run - It sets span status Error, adds a
problemevent with the RFC 9457 fields, and setserror.. TESTED · Q5; Q11type - Example:
fetchUser(42)returnsErr({ type: "…/→ spansuspended", status: 403 }) Error 403, aproblemevent,error.. TESTED · Q5type=…/ suspended - The helper is small in each of Bun, Go and .NET (19 lines in Bun, 12 in Go). TESTED · Q5; Q11
- A telemetry failure never changes your result. PROPOSED · A193 tab 1, jaeda call 5
Spans you get for free (auto spans)
The contract opts in with a list. Example: auto: [http.. DECIDED · A193 tab 1 #18
| Auto span | Bun | Go | .NET |
|---|---|---|---|
| HTTP in | our instrumentServe wrapper | otelhttp. | ASP.NET Core instrumentation |
| HTTP out | our instrumentedFetch | otelhttp. | HttpClient instrumentation |
| Postgres | instrumentation-pg ✓ | otelpgx ✓ (2 spans per query) | Npgsql built in ✓ |
| Redis | our wrapper (pick 5, decided) | redisotel ✓ (old attribute names) | contrib package ✓, errors not marked: we fix |
- Bun's Redis client and Bun SQL make no spans at all, so
lib/gets thin wrappers. Example:bun/ otel redis.→ spanget("cart:42") redis GET. TESTED · Q6 DECIDED · A193 tab 1 #20 - Example trace:
GET /(auto) →checkout checkout.(yours) →charge POST api.(auto). The logstripe. com checkout.sits undercharge. failed checkout.. DECIDED · A193 tab 4charge
What changes in each library
- New major of each
*-interfaces(Bun, Go, .NET, Dart): the 4 ports, name interfaces, in-memory test doubles, the banned-label policy. DECIDED · A193 tab 6 round 1a - The tracer moves from
*-otelinto*-interfaces. Today it isTraceEmitter. DECIDED · A193 tab 1 #6 - New major of each
*-otel(Bun, Go, .NET): Result helper, DB spans, profiling, LPSM keys. DECIDED · A193 tab 6 round 2a - New
lib/behind the Dart ports. DECIDED · A193 tab 1 #12dart/ faro - Go ports take
ctxfirst. Today'sEmit(record)usescontext.and loses the trace. PROPOSED · A193 tab 1, jaeda call 3Background() - C# ports are named
ISignal*, so they do not clash withILogger. PROPOSED · A193 tab 1, jaeda call 4 - Bun:
NodeSDK0.301 sent 0 spans under Bun, so the adapter builds the providers by hand. TESTED · A193 tab 4, spike js - .NET: the template must first move to
Host.. DECIDED · A193 tab 4CreateApplicationBuilder