docs

Mail, queue and Ace commands

The built-in mail, queue and Ace command instrumentations of the AdonisJS agent, what each records and how to configure or turn it off.

The agent ships three AdonisJS-specific instrumentations next to the Node auto-instrumentations: mail, queue and Ace commands. Each turns on by itself when the library is in your app, does nothing when it is absent, and has a top-level key in config/unhandled.ts. Each key accepts false or { enabled: false } to turn it off.

ts
import env from '#start/env'
import { defineConfig } from '@unhandled/agent-adonisjs'

export default defineConfig({
  dsn: env.get('UNHANDLED_DSN'),
  environment: env.get('APP_ENV'),
  mail: true,
  queue: { executionSpanLinkMode: 'link' },
  cli: { exclude: ['make:*', 'generate:*', 'queue:work', 'queue:listen', 'db:seed'] },
})

Turning one off stops only the agent's own spans. Lower-level instrumentations, such as the Redis client a queue uses, still record their calls.

Mail

When @adonisjs/mail is installed, every email becomes a CLIENT span named email send <mailer>, where <mailer> is the mailer name from your mail config. Emails queued with sendLater are traced when they are actually sent.

AttributeValue
email.systemadonisjs-mail
email.operationsend
email.transportTransport class name without Transport, lowercased: smtp, resend, and so on.
email.message.subjectSubject line.
email.message.fromSender address.
email.message.toJSON array of to addresses.
email.message.cc_count, email.message.bcc_countNumber of cc and bcc recipients. Their addresses are not recorded.
email.message.has_attachmentstrue when the message has attachments.
email.recipient_countto, cc and bcc recipients combined.
email.message.idMessage id returned by the transport, set after a successful send.

If sending fails, the span gets an exception event with source lines and the cause chain, its status becomes ERROR, and the error is rethrown to your code. These spans feed the Emails view.

Set mail: false to stop recording emails.

Queue

When @adonisjs/queue (or @boringnode/queue) is installed, the agent traces both sides of a job:

  • Dispatch: a span where your code queues the job, for example in the controller that places an order.
  • Execution: a CONSUMER span around the job's run in the worker, with every query and call the job makes as children.

Spans carry messaging.system=boringqueue. Span names and the full attribute list are on AdonisJS queue. Executions show up in Jobs.

OptionDefaultEffect
queue.enabledtruefalse turns queue spans off.
queue.executionSpanLinkMode'link'How an execution relates to its dispatch.

With 'link', each execution starts its own trace and holds a span link back to the dispatch span. The request that dispatched ProcessOrder stays short, and the job's trace stands alone, which suits jobs that run long after dispatch or retry several times. With 'parent', the execution becomes a child of the dispatch span, so the request and the job appear in one trace.

The queue instrumentation is loaded by otel.ts at startup, so make sure your worker process starts through bin/console.ts with import '../otel.js' first.

To keep only some job traces, use Job sampling. BullMQ is not part of this instrumentation; it has its own package, see BullMQ.

Ace commands

Every Ace command run through bin/console.ts becomes a root span named cli <command>, for example cli orders:reconcile.

AttributeValue
entry_point.typecli
cli.command.nameThe command name.
cli.command.descriptionThe command description, when it has one.
cli.args.<name>Each argument value.
cli.flags.<name>Each flag value.

Strings, numbers and booleans are recorded as they are; arrays become string arrays. Values are not redacted, so do not pass secrets as arguments or flags. If the command fails, its error is reported as an exception on the command's trace, and the span status becomes ERROR. Commands show up in Commands.

OptionDefaultEffect
cli.enabledtruefalse turns command spans off.
cli.include['*']Globs of command names to trace.
cli.exclude['make:*', 'generate:*', 'queue:work', 'queue:listen']Globs of command names never traced.

A command is traced when it matches at least one include glob and no exclude glob, so exclude wins. Setting exclude replaces the default list instead of adding to it; copy the defaults in, as in the example at the top of this page, if you still want scaffolding commands skipped. * in a glob matches any characters, including :.

Commands that stay alive, such as workers and dev servers, are never traced as a single span, whatever the globs say. A span that never ends would never be exported. The jobs a worker runs are traced individually by the queue instrumentation.

bin/console.ts must import otel.ts first. The configure command adds that import for you; if you set up the agent by hand, add it yourself or no command spans appear.

Optional integrations

Other libraries are covered by separate packages registered in integrations: BullMQ, Bentocache, Vercel AI and Mastra. Kysely, Drizzle and the MCP server instrumentation have their own setup pages. See Configuration.