Managed automatic instrumentation for .NET 10 applications, including NativeAOT consumers. It
works through compiler-generated Roslyn interceptors, ActivitySource subscription, public
DiagnosticListener hooks, build assets and module-initializer bootstrap. There is no CLR
profiler, no startup hook, no ReJIT, no runtime IL rewriting and no dynamic plugin loading.
Interceptors follow Roslyn's
interceptors.md
contract and need the .NET SDK global.json pins (10.0.402).
The upstream .NET automatic-instrumentation contract is a coverage comparison, not a package
dependency. Qyl.Telemetry.Hosting uses the OpenTelemetry SDK and OTLP exporter for processing and
transport.
Every attribute key, attribute value and scope name the instrumentation writes is a generated
constant from Qyl.Telemetry.SemanticConventions at the Directory.Packages.props pin. Vendor
attributes and deprecated keys pass through to the collector. Two producer representations are
converted before export: Microsoft.Extensions.AI's JSON finish-reason string becomes a string array,
and MassTransit's message-size string becomes a nonnegative Int64. Unparseable values stay as they
are; no duration unit is inferred or converted.
| Package | What it does |
|---|---|
Qyl.Telemetry.Hosting |
One-line onboarding. builder.AddQyl() wires the OpenTelemetry SDK, subscribes the native ActivitySource table and the application scope, adds the two processors, registers the meter inventory and exports OTLP with collector discovery. Its package root carries SKILL.md, the guide for a coding agent in the consuming application. |
Qyl.Telemetry.AutoInstrumentation |
The core runtime: the single qyl ActivitySource, the interceptor declarations and helper bodies, the options surface, the compiler-facing ABI, the build assets and the source generator. |
Qyl.Telemetry.AutoInstrumentation.Hosting |
Process bootstrap: a [ModuleInitializer] activates the qyl listeners when the assembly loads. AddQyl() is the application host entrypoint. |
Qyl.Telemetry.AutoInstrumentation.DiagnosticListeners |
The base DiagnosticListener subscriber and the shared semantics helpers the EF Core and SqlClient packages build on. |
Qyl.Telemetry.AutoInstrumentation.EntityFrameworkCore |
EF Core DiagnosticSource instrumentation. |
Qyl.Telemetry.AutoInstrumentation.SqlClient |
Microsoft.Data.SqlClient DiagnosticSource instrumentation. |
dotnet add package Qyl.Telemetry.Hostingusing Qyl;
builder.AddQyl();The consumer publishes NativeAOT without a trim, AOT or analyzer warning from these packages:
dotnet publish -r <rid> -p:PublishAot=trueAddQyl() activates the qyl listeners; registers the application scope Qyl.Telemetry.Application
behind Qyl.Spans.Source (the generated starters and Spans.Work start on it), the qyl
ActivitySource, the Microsoft.AspNetCore and System.Net.Http sources, the version-pinned GenAI,
MCP, Azure SDK and CoreWCF sources and every enabled row of the native-source table; adds the
ASP.NET Core enrichment middleware, QylNativeSpanProcessor and QylSessionSpanProcessor; registers
the meter inventory; and exports traces, metrics and logs over OTLP to OTEL_EXPORTER_OTLP_ENDPOINT,
else QYL_ENDPOINT (qyl's exporters only), else a qyl collector discovered on localhost or the
host qyl at 4318/4317. There is no per-integration enable step: every [QylIntercept] declaration
compiles into the consumer through the package's analyzer and build assets, and only the
OTEL_DOTNET_AUTO_*_INSTRUMENTATION_ENABLED toggles turn an integration off.
An application that wires the SDK itself references Qyl.Telemetry.AutoInstrumentation.Hosting,
OpenTelemetry.Extensions.Hosting and OpenTelemetry.Exporter.OpenTelemetryProtocol and registers
the sources by hand. The instrumentation packages ship no exporter; subscribing a native source that
qyl also owns exports the same operation twice.
builder.Services.AddOpenTelemetry()
.ConfigureResource(r => r.AddService("my-service"))
.WithTracing(t => t
.AddSource("Qyl.Telemetry.AutoInstrumentation", "Qyl.Telemetry.Application")
.AddOtlpExporter());
builder.Logging.AddOpenTelemetry(o => o.AddOtlpExporter());dotnet add package OpenTelemetry.Exporter.Consolebuilder.AddQyl(o => o.ConfigureTracing = t => t.AddConsoleExporter());One HttpClient call prints one client span under the System.Net.Http scope, carrying the
qyl.instrumentation.domain the processor stamped.
Does
AddSource("<name>")alone deliver spans, without a contrib package and without aDiagnosticListeneradapter?
- Yes: the source name goes in
QylTelemetrySources,AddQyl()subscribes it, andQylNativeSpanProcessorstamps the qyl attributes onto the library's own spans.- No: a Roslyn source interceptor in
Qyl.Telemetry.AutoInstrumentation.
A library that emits only DiagnosticSource events counts as No. A library whose ActivitySource
stays silent until the consumer opts in takes source + processor; qyl never makes the opt-in call.
QylTelemetrySources reads every source name from QylTelemetryNames, except
Experimental.ModelContextProtocol, which the registry does not declare yet.
One row per entry of QylTelemetrySources.NativeSourceRows: the source name, the instrumentation id
whose toggle gates it, and the qyl.instrumentation.domain value the processor stamps. Azure.*
matches by prefix; every other row is an ordinal exact match.
| Library | Pinned | ActivitySource |
Instrumentation id | Domain |
|---|---|---|---|---|
| Azure SDK | Azure.Storage.Blobs 12.30.1 |
Azure.* |
AZURE |
azure.sdk |
| CoreWCF | CoreWCF.Http 1.9.1 |
CoreWCF.Primitives |
WCFCORE |
none — see below |
| HttpClient | System.Net.Http (BCL) |
System.Net.Http |
HTTPCLIENT |
http.client |
| MySql.Data | 26.7.0 |
connector-net |
MYSQLDATA |
db.client |
| Elastic.Transport, Elastic.Clients.Elasticsearch | 1.1.0, 9.5.3 |
Elastic.Transport |
ELASTICTRANSPORT |
elastic.transport |
| GraphQL.NET | 8.8.5 |
GraphQL |
GRAPHQL |
graphql |
| Grpc.Net.Client | 2.84.0 |
Grpc.Net.Client |
GRPCNETCLIENT |
rpc.grpc |
| MassTransit | [8.5.11,9.0.0) |
MassTransit |
MASSTRANSIT |
messaging.masstransit |
| MongoDB.Driver | 3.12.0 |
MongoDB.Driver |
MONGODB |
db.mongodb |
| MySqlConnector | 2.6.2 |
MySqlConnector |
MYSQLCONNECTOR |
db.client |
| Npgsql | 10.0.3 |
Npgsql |
NPGSQL |
db.client |
| NServiceBus | 10.2.9 |
NServiceBus.Core |
NSERVICEBUS |
messaging.nservicebus |
| Oracle.ManagedDataAccess.Core | 23.26.301 |
Oracle.ManagedDataAccess.Core |
ORACLEMDA |
db.client |
| Quartz.NET | 4.4.0 |
Quartz |
QUARTZ |
job.quartz |
| RabbitMQ.Client | 7.2.2 |
RabbitMQ.Client.Publisher |
RABBITMQ |
messaging.rabbitmq |
| RabbitMQ.Client | 7.2.2 |
RabbitMQ.Client.Subscriber |
RABBITMQ |
messaging.rabbitmq |
- Quartz 4 also publishes metrics on its
Quartzmeter;AddQylsubscribes it, controlled byOTEL_DOTNET_AUTO_METRICS_QUARTZ_INSTRUMENTATION_ENABLED(default: the global metrics setting). - The Elasticsearch client enriches Elastic.Transport's source, so the two share one row and domain.
- CoreWCF's spans are the WCF server side and the registry has no domain value for it
(
rpc.wcf.clientis the intercepted client), so its row only adds the source. - Subscribed without a processor row, because the library already writes the conventions:
System.Net.Http,Experimental.Microsoft.Extensions.AI,Experimental.Microsoft.Agents.AI,Microsoft.Agents.AI.WorkflowsandExperimental.ModelContextProtocol. The AI sources need the consumer's opt-in:chatClient.AsBuilder().UseOpenTelemetry().Build()forMicrosoft.Extensions.AI10.10.0,agent.AsBuilder().UseOpenTelemetry().Build()forMicrosoft.Agents.AI1.24.0 andWorkflowBuilder.WithOpenTelemetry()forMicrosoft.Agents.AI.Workflows1.24.0.ModelContextProtocol2.2.0 and CoreWCF emit without one. - MCP metrics are not registered: their instruments carry dynamic tool and resource names as dimensions, which breaks bounded cardinality.
One row per [QylIntercept] declaration in src/Qyl.Telemetry.AutoInstrumentation.
| Integration | Pinned | Intercepted receiver | Instrumentation id | Domain |
|---|---|---|---|---|
| ADO.NET | System.Data.Common |
System.Data.Common.DbCommand |
ADONET, fanned out to SQLCLIENT and SQLITE by the receiver's namespace |
db.client |
| Confluent.Kafka | 2.16.0 |
Confluent.Kafka.IProducer<TKey, TValue> and IConsumer<TKey, TValue> |
KAFKA |
messaging.kafka |
| StackExchange.Redis | 3.4.0 |
StackExchange.Redis.IDatabaseAsync |
STACKEXCHANGEREDIS |
db.redis |
| WCF client | System.ServiceModel.Primitives 10.0.652802 |
System.ServiceModel.ClientBase<TChannel> |
WCFCLIENT |
rpc.wcf.client |
System.Data.Common, Microsoft.Data.SqlClient 7.1.1, Microsoft.Data.Sqlite 10.0.12,
Confluent.Kafka 2.16.0, StackExchange.Redis 3.4.0 and System.ServiceModel.* declare no
ActivitySource at the pinned version. The ADO.NET declaration lists Npgsql, MySqlConnector,
MySql.Data and ODP.NET in NativeSourceReceivers: their call sites get no interceptor and no
QYL1001, because their own sources cover them.
The source generator reports QYL1002 when a compilation uses one of these libraries and never makes
the call: Info for GraphQL.NET and ODP.NET, Warning for the three GenAI libraries.
| Library | The call | Without it |
|---|---|---|
| GraphQL.NET | IGraphQLBuilder.UseTelemetry() |
the GraphQL ActivitySource stays silent: no spans at all |
| Oracle ODP.NET | TracerProviderBuilder.AddOracleDataProviderInstrumentation() (package Oracle.ManagedDataAccess.OpenTelemetry) |
the command spans still arrive, carrying only db.system, db.odp.roundtrip.count, db.odp.roundtrip.duration and db.response.returned_rows; db.name, db.user, db.statement, server.address, server.port, db.odp.connection.id, db.odp.sql_id and exception recording stay off |
| Microsoft.Extensions.AI, Microsoft.Agents.AI, Workflows | UseOpenTelemetry() / WithOpenTelemetry() |
the GenAI sources stay silent |
MySql.Data needs no opt-in (AddConnectorNet() is only AddSource("connector-net")). It always
sends the full command text as db.statement, with no option to turn it off; masking it is the
collector's job.
Microsoft.AspNetCore.Hosting.HttpRequestIn is the one HTTP server span of a request; qyl starts
none. The runtime creates it without tags, so AddQylAspNetCoreInstrumentation() (called by
AddQyl()) registers an IStartupFilter middleware that fills, where absent:
qyl.instrumentation.domain, http.request.method, url.scheme, url.path, url.query under the
redaction control, http.route and the {method} {route} span name after routing,
http.response.status_code, error.type on failure, and the configured request and response
headers. A client that disconnects before a response is Kestrel's 499: no error.type, status unset.
EF Core (.EntityFrameworkCore) and Microsoft.Data.SqlClient (.SqlClient) use the library's
public DiagnosticListener hook; neither declares an ActivitySource. No signal has two qyl lanes.
Every row above is gated per signal by its instrumentation id:
OTEL_DOTNET_AUTO_{TRACES|METRICS|LOGS}_<ID>_INSTRUMENTATION_ENABLED, with
OTEL_DOTNET_AUTO_{TRACES|METRICS|LOGS}_INSTRUMENTATION_ENABLED and
OTEL_DOTNET_AUTO_INSTRUMENTATION_ENABLED taking precedence. A disabled id contributes neither an
AddSource call nor a processor row.
- Query text is opt-in per provider:
OTEL_DOTNET_AUTO_ENTITYFRAMEWORKCORE_SET_DBSTATEMENT_FOR_TEXTandOTEL_DOTNET_AUTO_SQLCLIENT_SET_DBSTATEMENT_FOR_TEXT. - ASP.NET Core header capture, outbound HTTP header capture and gRPC client metadata capture are each opt-in.
- GraphQL documents and ODP.NET statement text belong to the libraries' own options
(
UseTelemetry(o => o.RecordDocument), ODP.NET'sSetDbStatementForText);OTEL_DOTNET_AUTO_GRAPHQL_SET_DOCUMENTandOTEL_DOTNET_AUTO_ORACLEMDA_SET_DBSTATEMENT_FOR_TEXThave no reader here. - qyl never registers a library's own
Meter. ExportNpgsql's orNServiceBus.Core's instruments throughOTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCESorQylTelemetryOptions.AdditionalMeters.
The full 70-row contract, with every environment control, instrumentation option, evidence level and authoritative source, is the generated coverage matrix.
OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS and
OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS take comma-separated names;
absent, empty and whitespace-only lists capture nothing. Selection is case-insensitive, including
Mcp-Param-* names. Keys are http.request.header.<name> / http.response.header.<name>,
lowercase with punctuation kept; values are string arrays of what NonValidated exposes, message
and content headers both.
- The request is captured at the native start callback, before runtime propagation and later handlers add headers. The response is captured before the span stops. Transport failures keep the request fields and omit the absent response. No body is read or buffered.
- Capture follows the HTTP and global controls, sampling, SDK suppression and provider disposal.
- The payload accessors bind .NET 10 private diagnostic payload shapes (verified as NativeAOT on .NET 10.0.12). An incompatible payload logs a bounded warning without values or exception text.
OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_REQUEST_METADATA and
OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_RESPONSE_METADATA take
comma-separated names, independent of HTTP capture and of each other. Matching is case-insensitive,
key suffixes are lowercase with punctuation kept, and values are string arrays. Selected -bin
entries keep the HTTP transport's base64 strings.
- The existing
Grpc.Net.Clientactivity is enriched in its synchronous diagnostic callbacks: request metadata fromHttpRequestMessage.Headersat start, response metadata from the initial headers and then the trailers at completion, one array per name, before the span stops. - Values are what
NonValidatedexposes, duplicates and empty strings included; entry boundaries another layer combined are not reconstructed. Response bodies are never read. Cancellation and failure capture only the metadata that exists, and HTTP child spans get no metadata. - gRPC and global controls, sampling, suppression and provider lifetime apply.
- The accessors bind Grpc.Net.Client 2.84.0's private diagnostic shapes (verified as NativeAOT on .NET 10.0.12). An incompatible payload logs a bounded, value-free warning; native source, operation, kind, status and parentage stay unchanged.
qyl.instrumentation.domain is the only qyl-owned attribute the instrumentation writes.
QylNativeSpanProcessor writes it onto the spans of the native-source table, and the ASP.NET Core
middleware writes it onto the hosting activity. Applications add their own attributes with
span.SetTag(key, value). Its value set is registry-owned (QylAttributes.InstrumentationDomainValues):
app, aspnetcore.server, azure.sdk, db.client, db.efcore, db.elasticsearch, db.mongodb,
db.redis, db.sqlclient, elastic.transport, gen_ai.execute_tool, gen_ai.invoke_agent,
graphql, http.client, job.quartz, messaging.kafka, messaging.masstransit,
messaging.nservicebus, messaging.process, messaging.rabbitmq, rpc.grpc and
rpc.wcf.client. The generated starters and Spans.Work stamp app, messaging.process,
gen_ai.invoke_agent and gen_ai.execute_tool.
session.id: the ASP.NET Core middleware stamps the request's W3C baggage member session.id onto
the server span when it is a valid session id (unreserved URI characters, at most 128, not dots
alone). QylSessionSpanProcessor copies session.id from the nearest tagged in-process ancestor onto
descendants without one, at span end; remote parents propagate nothing. qyl writes no baggage.
tests/Qyl.Telemetry.Integration.Tests/Lanes/SessionPropagationTests.cs verifies this across two
processes.
Scopes: Qyl.Telemetry.AutoInstrumentation (ActivitySource),
Qyl.Telemetry.AutoInstrumentation.Database (Meter, db.client.operation.duration) and
Qyl.Telemetry.Application (application spans). Both sources carry SchemaUrl.Qyl
(https://qyl.at/schemas/<release>). The generated-code ABI anchor is QylGeneratedCodeAbi.V28 in
Qyl.Telemetry.AutoInstrumentation.GeneratedCode; it tracks the package major, so a generated
interceptor from another major fails to compile.
| Id | Severity | Reported when |
|---|---|---|
QYL1001 |
Info | A call site names a declared integration's receiver type and method but its signature does not fit the declared shape, typically because the library changed the signature in a new major. No interceptor is emitted for that call, so it produces no qyl telemetry. Update the declaration or pin the library to a version the shape describes. |
QYL1002 |
Info; Warning for the GenAI rows | The compilation uses a library whose native ActivitySource qyl subscribes but never makes the call that library needs before it emits, or before it emits in full — see the opt-in table. The message names the call and what is lost without it. |
Qyl.Telemetry.AutoInstrumentation depends on Qyl.Telemetry.SemanticConventions.Analyzers with
PrivateAssets="none", so the QYL0xxx rules reach every consumer through Qyl.Telemetry.Hosting,
among them QYL0112 (a constant Spans.Work name), QYL0110 (a rethrow inside a starter's scope
calls RecordFailure) and QYL0120 (no duplicate tracer instrumentation next to AddQyl). A library
that version-locks to the incubating tier sets OtelSemConvInstrumentationLibrary=true (QYL0008).
Fallout targets in eng/build; make <target> wraps ./eng/build.sh <Target>, and
./eng/build.sh --help lists every target and parameter.
./eng/build.sh Check # CI gate: contracts, Release builds, API report, generator snapshots, package layout, consumer behavior incl. NativeAOT
./eng/build.sh CheckFull # + NativeAOT publish matrix, thirty demo lanes, LiveCheck, Smoke, WebApiAotDemo, OtlpEvidence
./eng/build.sh Demos --lane quartz http-client # selected demo lanes (all thirty without --lane)
./eng/build.sh AotPublishGate --aot-set warned # clean, warned or all
./eng/build.sh LiveCheck --live-check-skip mongodb
./eng/build.sh Contracts --write-contracts # regenerate contracts/generated
./eng/build.sh TestbedProbe
./release.sh # pack <Version>, push to nuget.org, tag v<Version>, push the tagCheckrewritessrc/*/PublicAPI.txtand never fails on an API change; it fails unless the topCHANGELOG.mdentry is<Version>.Demos: thirty demo applications underdemos/, run as native MSTest cases intests/Qyl.Telemetry.Integration.Tests/Lanes/<Name>Tests.cs, NativeAOT where the integration supports it. Each run owns aQyl.Telemetry.TestingOTLP receiver, passed asQYL_ENDPOINT; the case compares the application's report with independent expectations and the complete capture. Captures and registry reports stay underartifacts/integration-tests. Ten lanes need a testbed service. Run by hand, a demo exports throughAddQyl()'s discovery to a runningqyl up.- The HTTP-header and gRPC-metadata fixtures use
Qyl.Telemetry.Tests.Capturefrom the submoduleeng/dependencies/Qyl.Telemetry.Tests;./eng/build.shinitializes it, directdotnetcommands needgit submodule update --init -- eng/dependencies/Qyl.Telemetry.Tests. OtlpEvidencedecodes a package consumer's export with the official protobuf messages and checks path, content type, typed values andurl.fullredaction on the wire.LiveCheckrunsweaver registry live-check(themise.tomlweaver) on 4317 against nine native-source demo lanes (azure,corewcf,elastictransport,elasticsearch,masstransit,mongodb,nservicebus,quartz,rabbitmq) with--fail-on violationand no allowlist. The registry is a checkout of Qyl.Telemetry.SemanticConventions atv+ theQyl.Telemetry.SemanticConventionspin, cloned into.semconv; the target passes both--config <registry>/.weaver.tomland--advice-policies <registry>/policies/live_check_adviceand fails when the checkout'sVersionPrefixdiffers from the pin.RabbitMQ.Client.Subscriberhas no consuming demo, so its spans are not judged.- Collector persistence is tested in qyl.
The verifiers' databases and brokers run in the Railway project qyl-testbed (region
europe-west4-drams3a), declared in .railway/railway.ts (partial
autoinstrumentation), each service behind a public TCP proxy. Services sleep after 5 to 10 minutes
without outbound traffic and wake on the next connection, keeping their filesystem until a
redeploy, so clients retry until the service answers and verifiers create their data idempotently.
| Service | Image | Serves | Limit (vCPU / memory) |
|---|---|---|---|
rabbitmq |
rabbitmq:4.1-alpine |
masstransit, rabbitmq | 1 / 512 MiB |
kafka |
apache/kafka:4.1.0 (KRaft, SASL/PLAIN) |
kafka | 1 / 1 GiB |
mongodb |
mongo:8-noble |
mongodb | 1 / 1 GiB |
redis |
redis:8-alpine (requirepass) |
redis | 1 / 512 MiB |
postgres |
postgres:18-alpine |
npgsql | 1 / 512 MiB |
mysql |
mysql:9 |
mysqlconnector, mysqldata | 1 / 1 GiB |
oracle |
gvenzl/oracle-free:23-slim-faststart |
oraclemda | 2 / 3 GiB |
sqlserver |
mcr.microsoft.com/mssql/server:2022-latest |
sqlclient | 2 / 2 GiB |
Connection strings: ~/.config/vitals/qyl-testbed.env (chmod 600, Vitals "qyl testbed connection
strings (Railway qyl-testbed)"), KEY=value lines without quotes or export. Passwords exist only
as Railway variables (preserve()) and in this file.
QYL_TESTBED_RABBITMQ_URL=amqp://USER:PASS@HOST:PORT
QYL_TESTBED_KAFKA_BOOTSTRAP=HOST:PORT
QYL_TESTBED_KAFKA_USER=...
QYL_TESTBED_KAFKA_PASSWORD=...
QYL_TESTBED_MONGODB_URL=mongodb://USER:PASS@HOST:PORT/?authSource=admin
QYL_TESTBED_REDIS_URL=redis://:PASS@HOST:PORT
QYL_TESTBED_POSTGRES_URL=Host=HOST;Port=PORT;Username=USER;Password=PASS;Database=qyl
QYL_TESTBED_MYSQL_URL=Server=HOST;Port=PORT;User ID=USER;Password=PASS;Database=qyl
QYL_TESTBED_ORACLE_URL=User Id=system;Password=PASS;Data Source=HOST:PORT/FREEPDB1
QYL_TESTBED_SQLSERVER_URL=Server=HOST,PORT;User ID=sa;Password=PASS;TrustServerCertificate=True;Encrypt=True
QYL_TESTBED_WAKE_SECONDS_MAX=SECONDS
QYL_TESTBED_WAKE_SECONDS_MAX (120) is how long a client retries a sleeping service.
bun install --cwd eng/testbed && bun eng/testbed/probe.ts # wake all eight, one line per service; --verbose
railway link --project qyl-testbed # once per checkout
eng/testbed/provision.sh # create missing services and secrets, apply, write the env file
eng/testbed/provision.sh env # rewrite the env file only
railway config plan --detailed-exit-code # 0 when Railway matches .railway/railway.ts<Version> in Directory.Build.props versions all six packages; the package major is the
compile-time ABI (QylGeneratedCodeAbi.V<X>). The Qyl.Telemetry.SemanticConventions
PackageVersion in Directory.Packages.props names the semantic-convention release: the code
compiles against its constants, LiveCheck derives its registry tag from it, and Contracts fails
when a tracked file hard-codes another SEMCONV_REF or registry tag. A package exposes that release
as the nuspec dependency's lower bound and as
[assembly: AssemblyMetadata("QylSemanticConventionsVersion", "<version>")] on
Qyl.Telemetry.AutoInstrumentation.dll, which PackageLayout checks against the pin.
- Only source-visible call sites can be intercepted. Calls hidden in compiled dependencies, reflection or dynamic dispatch need a public runtime hook or remain unsupported.
- Some integrations are managed-only because the instrumented library requires runtime code generation. There is no parity claim with the CLR-profiler OpenTelemetry .NET automatic instrumentor, and no claim that every third-party library publishes warning-free under NativeAOT.
- Query text and other sensitive or high-cardinality values remain opt-in or redacted according to the package options and the upstream OpenTelemetry controls.
- Generator snapshots prove emitted source shape; protocol interoperability needs a real OTLP receiver and structural decoding of the official protobuf messages.
- MassTransit 9 is commercially licensed and is neither referenced nor redistributed here; the demo
pin is the Apache-2.0 range
[8.5.11,9.0.0).
Apache-2.0