Exception Reporting
Exception reporting in @eienjs/adonisjs-sentry is powered by the reportException function. It captures unhandled exceptions while automatically extracting contextual information from the current AdonisJS HttpContext.
Global Exception Handler
The standard way to report errors in AdonisJS is within app/exceptions/handler.ts:
import type { HttpContext } from '@adonisjs/core/http';
import { ExceptionHandler } from '@adonisjs/core/http';
import { reportException } from '@eienjs/adonisjs-sentry';
export default class HttpExceptionHandler extends ExceptionHandler {
async report(error: unknown, ctx: HttpContext) {
reportException(error, ctx);
await super.report(error, ctx);
}
}
reportException returns the generated Sentry event ID string, or undefined if Sentry is disabled or the error was ignored.Manual Exception Reporting
You can also call reportException manually in services, background queues, or scheduled jobs without an HttpContext:
import { reportException } from '@eienjs/adonisjs-sentry';
export class BillingService {
async processPayment(orderId: string) {
try {
await this.chargeGateway(orderId);
} catch (error) {
// Captures the exception without HTTP context
const eventId = reportException(error);
// ... handle fallback
}
}
private async chargeGateway(orderId: string): Promise<void> {
// Process payment gateway charge
}
}
How Errors Are Filtered
Before delivering any event to Sentry, reportException runs filtering checks against your rules defined in config/sentry.ts:
- Initialization check: If
Sentry.isInitialized()is false, reporting is immediately skipped. - HTTP Status filtering: If
error.statusexists (or is treated as500if absent) and matchesignoreStatuses, the error is discarded. - Error Code filtering: If
error.codematches any string inignoreCodes, it is ignored. - Exception Class filtering: If
error instanceof exceptionmatches any constructor inignoreExceptions, it is ignored.
export default defineConfig({
// ...
report: {
ignoreStatuses: [401, 403, 404, 422],
ignoreCodes: ['E_UNAUTHORIZED_ACCESS', 'E_VALIDATION_ERROR'],
ignoreExceptions: [],
},
});
Context and Tags Attached
When an HttpContext is supplied, reportException sets up an isolated Sentry scope with comprehensive diagnostic data:
Tags
error.code: The error code (e.g.E_ROW_NOT_FOUND).http.status_code: The HTTP response code (e.g.500,400).route: The matched route pattern (e.g./api/v1/users/:id), falling back toctx.routeKeyor'unknown'.http.method: HTTP method in uppercase (e.g.GET,POST).request_id: The unique request identifier fromctx.request.id().
Request Context
A structured context block named request is added to the event:
{
"url": "https://api.example.com/users/42?include=profile",
"method": "GET",
"route": "/users/:id",
"params": {
"id": "42"
}
}
Smart Fingerprinting by Error Code
In AdonisJS, built-in and custom exceptions typically define a unique code string such as E_ROW_NOT_FOUND or E_INVALID_CREDENTIALS.
By default (fingerprintByCode: true), reportException groups issues in Sentry using:
scope.setFingerprint(['{{ default }}', code]);
This prevents issues with dynamic error messages (such as "Cannot find user with ID 42" and "Cannot find user with ID 99") from creating hundreds of separate issues in your Sentry dashboard.
To disable this behavior and use Sentry's default grouping algorithms, set:
export default defineConfig({
// ...
report: {
fingerprintByCode: false,
},
});
User Context and PII Protection
When @adonisjs/auth is installed and a user is authenticated, reportException automatically extracts ctx.auth.user and attaches it to the Sentry scope.
Privacy Default: User ID Only
By default, dataCollection.userInfo is set to false in config/sentry.ts. In this mode, only the user's id is transmitted:
{
"user": {
"id": "123"
}
}
Enabling Full User Information
If your privacy policy and compliance requirements allow transmitting user emails and IP addresses, set dataCollection.userInfo: true:
export default defineConfig({
// ...
dataCollection: {
userInfo: true,
},
});
With userInfo: true, the scope includes:
id:String(ctx.auth.user.id)email:ctx.auth.user.email(if string)ip_address:ctx.request.ip()
To completely disable user capturing, set captureUser: false inside report:
export default defineConfig({
// ...
report: {
captureUser: false,
},
});
