Metrics and tracing
The library instruments itself with the runtime's own
ActivitySource and Meter. There is nothing to
start, nothing to inject and no adapter: a collector that subscribes to
the two names sees everything.
OpenTelemetry
dotnet add package AceMq.Amqp.Hosting.OpenTelemetry --version 0.2.3builder.Services.AddOpenTelemetry()
.WithTracing(t => t.AddAceMqInstrumentation().AddOtlpExporter())
.WithMetrics(m => m.AddAceMqInstrumentation().AddOtlpExporter());One line each. It is a second package rather than something
AddAceMq does, and the reason is a dependency:
AddAceMq cannot subscribe an OpenTelemetry pipeline it
cannot see, and making AceMq.Amqp.Hosting depend on
OpenTelemetry would put it in the dependency graph of every application
that uses AceMQ and not OpenTelemetry. So the wiring lives in the
package an application adds precisely because it has both — which is how
every other .NET instrumentation library is arranged, and means the line
looks like the ones around it.
It depends on OpenTelemetry.Api only, not the SDK, so
your application keeps its own choice of SDK version.
Without OpenTelemetry
The two names are constants:
using AceMq.Amqp.Hosting;
AceMqTelemetryNames.ActivitySource // "AceMq.Amqp"
AceMqTelemetryNames.Meter // "AceMq.Amqp"Enough for a hand-rolled MeterListener, an
ActivityListener in a test, or a
dotnet-counters session:
dotnet-counters monitor --process-id <pid> --counters AceMq.AmqpWhat is counted
The instruments and their names are the library's, and they match the Java and Go libraries so one dashboard serves a polyglot fleet. The full list is on the library's observability page; the ones that matter most for a worker:
acemq.consume.total |
messages handled, tagged by queue and outcome |
acemq.consume.duration |
how long handlers took |
acemq.consume.in.flight |
messages currently inside a handler — the number a drain waits down to zero |
acemq.consume.attempts |
which attempt a message was handled on |
acemq.messages.retried.total |
retries, tagged by rung |
acemq.messages.dead.lettered.total |
|
acemq.retry.rung.missing |
a retry that had to wait in the process because its rung queue was absent |
acemq.publish.total |
publishes, tagged by exchange and outcome |
acemq.retry.rung.missing is worth an alert on its own
merits, and is also the early warning that the next deployment's drain
is going to be slow — see startup and
shutdown.
Logs
Everything this package does is logged through ILogger,
on the category of the class that did it:
| category | at | what |
|---|---|---|
AceMq.Amqp.Hosting.AceMqConnectionProvider |
Information | connecting, and connected with the transport's capabilities |
AceMq.Amqp.Hosting.AceMqConnectionHost |
Information | the topology plan, when it changed anything |
| Warning | drift between the declared topology and the broker's | |
AceMq.Amqp.Hosting.AceMqConsumerHost |
Information | each consumer starting, with its queue, concurrency and prefetch |
| Information | the drain beginning, how long it took, and how many deliveries it handed back unhandled | |
| Warning | a drain that overran, a drain the host stopped waiting for, and the shutdown-timeout misconfiguration that causes most of them |
Those last two are logged as the separate events they are. A drain
that ran out of its own listener:shutdownTimeout names that
budget; a drain the host cancelled at
HostOptions.ShutdownTimeout says so and names both times,
which is the difference between "the handlers are too slow" and "the
deadline is set wrong". See startup and shutdown.
The connection URL is logged with its password replaced. That is the
library's ToString, not something this package does, and it
is worth knowing it is there rather than discovering it is not.