How to Monitor Laravel, Django, Rails, NestJS and Next.js Applications in Production
Application monitoring is not the same thing as checking whether your website is online.
Laravel, for example, provides a built-in /up health route and dispatches Illuminate\Foundation\Events\DiagnosingHealth whenever that route is requested. You can use this event to test application dependencies and throw an exception when something is unhealthy. The event itself is part of Laravel’s public API, while the underlying failure mechanism relies on PHP’s exception model and Throwable interface.
But a successful /up response only proves that the Laravel application booted successfully unless you explicitly add deeper diagnostics. It does not prove that an invoice job was processed, a scheduled command ran, an external payment API accepted a request, or that customers can successfully complete checkout.
The same problem exists in Django, Ruby on Rails, NestJS and Next.js.
Your application can be accepting HTTP requests while an entirely different execution path is failing.
That is the part we need to monitor.
What Should You Monitor in a Production Application?
Google’s Site Reliability Engineering documentation defines four primary signals for monitoring a user-facing system: latency, traffic, errors and saturation. It also recommends separating successful-request latency from failed-request latency because a very fast HTTP 500 response can otherwise make overall latency look healthier than it really is.
Those four signals give us the infrastructure and request-level foundation, but modern framework applications usually have more than one execution path.
For a typical Laravel, Django, Rails, NestJS or Next.js system, you should therefore think about monitoring across six layers: incoming requests, background work, scheduled work, data dependencies, outgoing integrations and business outcomes.
A checkout request illustrates why.
The browser can receive HTTP 200 OK, but the application may have only queued the order for later processing. If the worker has stopped, your request metric says “success” while the customer never gets an invoice.
Monitoring needs to follow the work, not just the request.
Why Is an Uptime Check Not Enough?
An uptime check usually answers a narrow question:
Can I connect to this application and receive the expected HTTP response?
That is useful, but it is only black-box monitoring of one execution path.
Consider an application where a user submits an order:
Browser
↓
HTTP Application
↓
Database
↓
Queue
↓
Worker
↓
Payment / Email / ERP API
The HTTP application may be healthy while the queue worker is dead.
The worker may be healthy while the payment provider is rejecting requests.
The payment call may succeed while your database transaction fails.
Or every technical operation may succeed while the actual business result, for example orders completed per hour, unexpectedly drops to zero.
Google’s SRE guidance makes the same distinction between symptoms and causes. Monitoring should tell you what users are experiencing while deeper telemetry helps determine why it happened.
That distinction becomes especially important once we look at what each framework actually provides.
How Do You Monitor a Laravel Application?
Laravel 13 includes a built-in health endpoint that defaults to /up.
If Laravel boots successfully, it returns HTTP 200. If bootstrapping fails, it returns HTTP 500. The path can be configured in bootstrap/app.php.
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
The interesting part is what happens when /up is requested.
Laravel dispatches:
Illuminate\Foundation\Events\DiagnosingHealth
That gives you an extension point for application-specific checks.
For example:
use Illuminate\Foundation\Events\DiagnosingHealth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(DiagnosingHealth::class, function (): void {
DB::select('select 1');
});
}
If the database operation throws an exception, Laravel can return a failed health response rather than reporting that the application is healthy. Laravel’s documentation explicitly suggests using the event to check services such as your database or cache and throwing an exception when a problem is detected.
This makes the health endpoint more useful, but there is an important architectural decision here.
A dependency check is not necessarily the same thing as a process-liveness check. If your application can degrade gracefully when a non-critical external service is unavailable, treating that dependency as grounds for killing the entire application instance may make the incident worse.
So you need to decide what “healthy” means for your application rather than adding every dependency you can find.
How Do You Monitor Laravel Queues?
Laravel provides queue monitoring directly through Artisan.
The queue:monitor command checks queue size against a threshold:
use Illuminate\Support\Facades\Schedule;
Schedule::command(
'queue:monitor redis:default --max=100'
)->everyMinute();
When the configured queue exceeds the threshold, Laravel dispatches:
Illuminate\Queue\Events\QueueBusy
You can listen for this event and send it to whichever notification or observability system your application uses.
use Illuminate\Queue\Events\QueueBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
Event::listen(function (QueueBusy $event): void {
Log::warning('Queue threshold exceeded', [
'connection' => $event->connectionName,
'queue' => $event->queue,
'size' => $event->size,
]);
});
The important metric here is not only the number of failed jobs.
You should also care about queue depth and queue wait time.
A queue can have zero failed jobs and still be unhealthy if work is arriving faster than workers can process it.
If your Laravel application uses Redis queues, Laravel Horizon adds a dashboard and metrics around job throughput, runtime and failures. Horizon is specifically built around Redis-backed Laravel queues.
How Do You Know the Laravel Scheduler Is Still Running?
Monitoring individual scheduled commands is different from proving that the scheduler itself is alive.
Laravel provides onSuccess() and onFailure() callbacks for scheduled commands. It also provides pingOnSuccess() and pingOnFailure() for calling an external monitoring endpoint after execution. A scheduled Artisan or system command is considered failed when it exits with a non-zero status.
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schedule;
Schedule::command('invoices:generate')
->hourly()
->onFailure(function (): void {
Log::critical('Invoice generation failed');
})
->pingOnSuccess(
config('services.monitoring.invoice_success')
)
->pingOnFailure(
config('services.monitoring.invoice_failure')
);
Laravel also dispatches scheduler-specific events including Illuminate\Console\Events\ScheduledTaskStarting, ScheduledTaskFinished, ScheduledTaskSkipped and ScheduledTaskFailed.
That means you can monitor scheduling without scattering logging code through every command.
There is still one edge case.
If schedule:run itself never executes, none of those task callbacks can fire.
That is why an externally observed scheduler heartbeat is valuable: you are checking for the absence of an expected event rather than waiting for an exception that can never occur because the scheduler never started.
How Do You Monitor Laravel Database Performance?
A database being reachable and a database performing well are two different states.
Laravel exposes mechanisms for query timing and connection monitoring. The framework can monitor cumulative query time during a request, and the db:monitor Artisan command can dispatch Illuminate\Database\Events\DatabaseBusy when open database connections exceed a configured threshold.
Application monitoring should therefore capture both dependency health and database behavior.
A healthy Laravel database layer should let you answer questions such as whether query latency has increased, whether connection usage is approaching capacity, whether one endpoint suddenly performs far more queries than before, and whether a deployment introduced an N+1 query.
Laravel Pulse and Nightwatch can both help at different levels.
How Do You Monitor Laravel Cache Performance?
A Redis or Memcached server can technically be online while your application’s caching strategy has stopped being effective.
That is why cache hit/miss behavior matters.
Laravel Pulse has a Cache card that records cache hits and misses globally and for individual keys. Pulse also tracks slow requests, slow jobs, slow database queries, slow outgoing requests and queue throughput.
A sudden decrease in cache hit rate can explain rising database load and request latency even when neither Redis nor the database is technically unavailable.
This is a good example of why monitoring individual components as simply “up” or “down” does not give you enough information.
How Do You Monitor External API Calls in Laravel?
There is a subtle Laravel behavior worth monitoring carefully.
Laravel’s HTTP client does not throw an exception automatically for HTTP 4xx or 5xx responses.
You need to inspect methods such as failed(), clientError() or serverError(), or explicitly call throw().
$response = Http::timeout(5)
->retry(2, 200)
->post($url, $payload)
->throw();
throw() converts an unsuccessful HTTP response into an Illuminate\Http\Client\RequestException. Connection problems can raise an Illuminate\Http\Client\ConnectionException.
Laravel also dispatches RequestSending, ResponseReceived and ConnectionFailed events around its HTTP client. These provide useful instrumentation points if you want to calculate outgoing API latency or error rates centrally.
This matters because a third-party API failure is not necessarily an application exception.
If your code accepts the error response and still returns HTTP 200 to your own client, generic exception monitoring may never see the actual failure.
Which Laravel Monitoring Tool Should You Use?
Laravel currently has several first-party tools, but they solve different problems.
Laravel Telescope gives you detailed visibility into individual requests, exceptions, logs, database queries, queued jobs, mail, notifications, cache operations and scheduled tasks. Laravel itself describes Telescope primarily as a companion for local development, although it can be configured and secured for other environments.
Laravel Horizon focuses on Redis queues and exposes queue throughput, runtime and job failures.
Laravel Pulse aggregates application-level performance and usage. Its current recorders cover exceptions, queues, slow requests, slow jobs, slow queries, slow outgoing HTTP calls, cache interactions and server resources.
Laravel Nightwatch is Laravel’s first-party production monitoring platform. It correlates requests, outgoing requests, jobs, queries, mail, notifications, commands, cache operations and scheduled tasks. Laravel describes Nightwatch as the production-oriented layer, while Telescope is positioned around detailed debugging and Pulse around aggregated metrics.
You do not necessarily need all of them.
The tool should follow the monitoring problem rather than the other way around.
How Does Laravel Application Monitoring Work Behind the Scenes?
If we trace a Laravel application instead of looking only at dashboards, the execution flow becomes much easier to understand:
- An HTTP request enters Laravel. A health request to
/upcan dispatchIlluminate\Foundation\Events\DiagnosingHealth, while normal application requests continue through middleware, routing, controllers and their dependencies. - A controller may execute SQL, access cache or make an outbound HTTP request. Laravel Pulse can aggregate slow requests, slow queries, slow outgoing calls and cache hit/miss behavior, while Telescope can retain detailed events for debugging.
- If the request dispatches asynchronous work, the job enters the configured queue.
queue:monitorcan detect excessive queue depth and dispatchIlluminate\Queue\Events\QueueBusy. Horizon adds throughput, runtime and failure visibility for Redis queues. - A worker eventually reserves the queued job and executes it. This execution path exists independently of the original HTTP request, so successful request monitoring alone cannot prove that the job completed.
- Scheduled work starts through Laravel’s scheduler. During execution Laravel can dispatch
ScheduledTaskStarting,ScheduledTaskFinished,ScheduledTaskSkippedorScheduledTaskFailed. - If your application calls an external service using Laravel’s HTTP client,
RequestSending,ResponseReceivedandConnectionFailedgive you instrumentation hooks. HTTP 4xx and 5xx responses must still be interpreted correctly because they do not throw automatically. - Finally, production observability can correlate these execution paths. Nightwatch is designed to connect Laravel requests, queries, jobs, cache operations and other framework events into a production view.
The important point is that there is no single “application process” to watch.
A Laravel application is a collection of cooperating execution paths.
Now let’s apply exactly the same model to other frameworks.
How Do You Monitor a Django Application?
Django gives you low-level hooks at the request lifecycle through its signal system.
Current Django 6.1 exposes django.core.signals.request_started, request_finished and got_request_exception. The first is sent when Django starts processing an HTTP request, the second when response delivery completes, and the third when Django encounters an exception while processing an incoming request.
Django’s own documentation warns that request/response signals can make application code harder to maintain and suggests considering middleware for request processing.
For request timing, middleware is therefore usually a cleaner place:
import logging
import time
logger = logging.getLogger(__name__)
class RequestTimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
started = time.perf_counter()
try:
return self.get_response(request)
finally:
duration_ms = (
time.perf_counter() - started
) * 1000
logger.info(
"request_finished",
extra={
"path": request.path,
"duration_ms": duration_ms,
},
)
You can then send those measurements into your metrics or tracing backend rather than treating application logs as the monitoring system themselves.
How Do You Monitor Django Background Tasks?
This area changed substantially in modern Django.
Django 6.0 introduced Django’s Tasks framework.
Django can define, enqueue and track background Tasks, but the framework deliberately does not provide the production worker that executes them. Actual execution is delegated to external task infrastructure through a task backend. Django’s built-in ImmediateBackend and DummyBackend are intended for development or testing scenarios rather than providing the durable production worker layer.
This creates a monitoring boundary you should make explicit:
Django
↓
Task Backend
↓
Queue Store
↓
Worker
↓
Task Execution
Django 6.0 also introduced task lifecycle signals, which are available in Django 6.1:
django.tasks.signals.task_enqueued
django.tasks.signals.task_started
django.tasks.signals.task_finished
task_finished fires whether execution succeeded or failed, and the signal includes the associated TaskResult.
A small monitoring receiver might look like this:
import logging
from django.dispatch import receiver
from django.tasks.signals import task_finished
logger = logging.getLogger(__name__)
@receiver(task_finished)
def task_completed(sender, task_result, **kwargs):
logger.info(
"task_finished",
extra={
"task_id": task_result.id,
"status": str(task_result.status),
},
)
A Django TaskResult exposes information including its id, status, enqueue time, start time and finish time. Those timestamps make queue waiting time and execution duration observable if your configured backend persists the necessary result information.
But remember the important boundary:
Django’s signal tells you what Django knows about a task.
You still need infrastructure-level monitoring for the worker process and whatever durable queue implementation your production backend uses.
How Do You Monitor Django Database Queries?
Django provides a database instrumentation hook through connection.execute_wrapper().
A wrapper can inspect query execution, measure duration, log queries or enforce application-specific behavior around database access. The wrapper is installed as a context manager and receives the SQL, parameters and execution callable.
This is useful when you need precise custom instrumentation.
For broad production observability, however, collecting query duration through your tracing/APM layer is usually easier than maintaining bespoke instrumentation throughout the application.
The metrics you need in the end are framework-independent: query duration, query rate, errors, connection saturation and unusually expensive query patterns.
How Do You Monitor a Ruby on Rails Application?
Rails has its own error-reporting abstraction through Rails.error.
Rails wraps HTTP requests, background jobs and rails runner executions in its error reporter. Unhandled errors can therefore be delivered to registered subscribers without requiring each controller or job to implement its own rescue-and-report logic.
You can register your own subscriber:
class ErrorSubscriber
def report(error, handled:, severity:, context:, source: nil)
MonitoringService.report(
error,
handled: handled,
severity: severity,
context: context
)
end
end
Rails.error.subscribe(ErrorSubscriber.new)
Rails also provides a built-in /up health endpoint. It returns HTTP 200 when the application boots without exceptions and 500 otherwise. Rails explicitly notes that the default endpoint does not represent the health of every dependency, such as the database or Redis.
That behavior is conceptually very similar to Laravel’s default /up route.
How Do You Measure Request Performance in Rails?
Rails exposes extensive framework instrumentation through ActiveSupport::Notifications.
The process_action.action_controller event includes information such as controller, action, request method, path, HTTP status, view runtime and database runtime.
A small subscriber can hook directly into it:
ActiveSupport::Notifications.subscribe(
"process_action.action_controller"
) do |event|
Rails.logger.info(
event: event.name,
duration_ms: event.duration,
status: event.payload[:status]
)
end
Active Support instrumentation is much broader than controller requests.
Rails also exposes cache-related notifications such as cache_read.active_support, where the payload contains whether the cache operation was a hit.
That gives you the same type of cache-effectiveness signal Laravel Pulse exposes, although the instrumentation API itself leaves aggregation and visualization up to your monitoring system.
How Do You Monitor Rails Background Jobs?
Rails applications use Active Job as the framework abstraction around asynchronous jobs.
For modern Rails applications, Solid Queue has been the default Active Job backend since Rails 8.0. Solid Queue is database-backed and includes separate operational roles for dispatching scheduled work, executing ready jobs and managing recurring jobs.
The architecture is important:
Active Job
↓
Solid Queue
↓
Dispatcher
↓
Ready Executions
↓
Worker
Solid Queue also has a scheduler for recurring tasks and a supervisor that manages workers and dispatchers.
That means Rails monitoring should not stop at “Did perform_later return successfully?”
You need to know whether jobs are becoming ready, whether workers are consuming them, how long ready jobs have been waiting, whether executions are failing and whether the scheduler is still producing recurring jobs.
This is the Rails equivalent of watching Laravel’s queue workers and scheduler rather than relying only on web request monitoring.
How Do You Monitor a NestJS Application?
NestJS provides a dedicated health-check integration through @nestjs/terminus.
Terminus supports readiness and liveness checks and includes first-party health indicators for HTTP services, TypeORM, Mongoose, Sequelize, MikroORM, Prisma, microservices, gRPC, memory and disk.
For example, a TypeORM health check can verify database connectivity:
@Controller('health')
export class HealthController {
constructor(
private readonly health: HealthCheckService,
private readonly db: TypeOrmHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() =>
this.db
.pingCheck('database')
.withTimeout(1000),
]);
}
}
The current Terminus implementation uses a simple database ping check and can report response time along with health status.
As with Laravel and Rails, however, dependency health is still not complete application observability.
You also need request performance, exceptions, asynchronous work and runtime signals.
How Do You Monitor BullMQ Queues in NestJS?
NestJS integrates with BullMQ through @nestjs/bullmq.
Nest gives queue consumers access to worker events using @OnWorkerEvent() and queue-level events through @QueueEventsListener() with @OnQueueEvent().
For example:
@Processor('emails')
export class EmailConsumer extends WorkerHost {
@OnWorkerEvent('failed')
onFailed(job: Job, error: Error) {
console.error(
`Job ${job.id} failed`,
error,
);
}
}
Do not restrict queue monitoring to the failed event.
You should also measure how long a job waits before becoming active, how long execution takes, how often retries occur and whether queue depth is increasing faster than your workers can drain it.
A failed-job counter tells you about explicit failures.
Queue age tells you about capacity failure.
Those are different incidents.
How Do You Monitor NestJS Without Building Every Metric Yourself?
NestJS now has an official observability product called NestJS Observe.
The current NestJS documentation describes Observe as an auto-instrumented platform built around Nest’s own request lifecycle, including controllers, guards, interceptors, pipes, resolvers and queue consumers. It collects requests, background jobs, errors, logs and traces.
This is particularly interesting architecturally because generic Node.js instrumentation tends to see HTTP servers and database drivers.
Nest-aware instrumentation can additionally tell you which controller, service, guard, interceptor or queue consumer consumed the time.
That turns a generic trace such as:
POST /orders → PostgreSQL
into a much more framework-aware execution path:
OrdersController
↓
AuthGuard
↓
OrdersService.create()
↓
Repository
↓
PostgreSQL
For debugging production NestJS systems, that framework-level context can be more actionable than infrastructure telemetry alone.
How Do You Monitor a Next.js Application?
Next.js needs a slightly different monitoring model because one application may contain server-side rendering, React Server Components, route handlers, server actions and browser-side code.
Next.js provides a first-class instrumentation.ts convention specifically for integrating observability and logging systems.
The file belongs in the project root, or in src when you use a src directory, and can export a register() function. Next.js calls that function when a server instance starts and before it begins serving requests.
Next.js recommends OpenTelemetry for application instrumentation and provides @vercel/otel as a convenient integration. The framework itself already creates OpenTelemetry spans for supported framework operations.
A minimal setup looks like this:
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel({
serviceName: 'contrive-next-app',
})
}
OpenTelemetry is useful here because it gives you a vendor-neutral telemetry model. Your application instrumentation does not have to be rewritten merely because you change the backend used to store or visualize traces.
How Do You Capture Server Errors in Next.js?
The same Next.js instrumentation convention can export onRequestError.
Next.js invokes this hook when its server captures a request error, giving you a central place to forward server-side failures to your observability provider.
export async function onRequestError(
error,
request,
context,
) {
await monitoring.captureException(error, {
request,
context,
})
}
This is preferable to adding repetitive try/catch reporting around every route handler.
But Next.js has another monitoring surface that backend frameworks do not have to the same extent: the browser.
How Do You Monitor Core Web Vitals in Next.js?
Next.js provides useReportWebVitals() for collecting browser performance metrics.
The hook can report Core Web Vitals into your analytics or observability pipeline.
'use client'
import { useReportWebVitals } from 'next/web-vitals'
export function WebVitals() {
useReportWebVitals((metric) => {
sendMetric(metric)
})
return null
}
Next.js also supports instrumentation-client.ts, which runs before your frontend application code and is intended for global analytics, error tracking and browser-side performance monitoring.
A production Next.js monitoring strategy therefore needs to connect two worlds:
Browser
↓
Next.js Rendering / Navigation
↓
Server Components / Route Handlers
↓
Database / APIs / Services
Server latency cannot tell you whether users experience slow rendering.
Core Web Vitals cannot tell you why a database query took three seconds.
You need both ends.
What Metrics Should Actually Trigger an Alert?
Not every metric deserves an alert.
Google’s SRE guidance distinguishes between information useful for diagnosis and conditions serious enough to interrupt a human. It recommends focusing on user-visible symptoms and keeping monitoring systems as simple as practical.
An application dashboard might contain hundreds of measurements.
A production paging policy should be much smaller.
For request-driven systems, error rate and tail latency are usually more actionable than a single isolated exception.
For queues, queue age and backlog growth can be more important than an arbitrary job count.
For schedulers, absence of an expected heartbeat can be more meaningful than server CPU.
For databases, connection saturation and sustained query latency matter more than knowing that PostgreSQL’s process exists.
For third-party APIs, track error rate and latency by dependency rather than mixing every provider into one aggregate number.
Then add at least one metric describing what the application is actually supposed to accomplish.
For an e-commerce application that may be successful checkouts.
For a SaaS product it may be successful account creation or subscription activation.
For an invoicing platform it may be invoices generated and delivered.
Infrastructure telemetry can tell you that every server is green.
A business-level signal can tell you that the application nevertheless stopped doing its job.
Should You Use Logs, Metrics or Traces?
You normally need all three, but they answer different questions.
Metrics tell you that something changed.
Request P95 latency went from 220ms to 1.8 seconds.
Queue wait time is rising.
The failure rate increased.
Traces tell you where the time or failure travelled.
A request spent 30ms in middleware, 40ms in application logic, 1.4 seconds waiting for an external API and 200ms querying PostgreSQL.
Logs give you event-specific context.
Which account was involved?
Which provider response was received?
What application state existed when the failure happened?
Treating logs alone as monitoring usually makes detection too dependent on humans searching text after an incident has already happened.
Treating metrics alone as debugging leaves you knowing that something broke without knowing where.
The useful production model connects them.
What Should You Monitor Across All Five Frameworks?
Despite very different implementation details, the important signals map surprisingly well across these frameworks.
| Layer | Laravel | Django | Rails | NestJS | Next.js |
|---|---|---|---|---|---|
| Health | /up, DiagnosingHealth |
custom health endpoint / middleware | /up |
Terminus | hosting/server health |
| Requests | framework events, Pulse, Nightwatch | middleware/signals | ActiveSupport Notifications | interceptors/Observe | OpenTelemetry |
| Errors | exception handler / Telescope / Nightwatch | logging + request exception signal | Rails.error |
exception filters/Observe | onRequestError |
| Background jobs | Laravel Queue/Horizon | Tasks + external backend | Active Job/Solid Queue | BullMQ | external worker architecture |
| Scheduled work | Laravel Scheduler | deployment/task infrastructure | Solid Queue scheduler | scheduler/queue infrastructure | platform/external scheduling |
| Database | query events/Pulse/Nightwatch | DB instrumentation | ActiveSupport instrumentation | ORM + tracing | ORM/database tracing |
| Cache | cache events/Pulse | cache backend telemetry | ActiveSupport cache events | Redis/cache telemetry | cache/platform telemetry |
| External APIs | Laravel HTTP client events | HTTP-client instrumentation | HTTP-client instrumentation | HttpService/tracing | fetch/OpenTelemetry |
| Browser UX | separate frontend monitoring | separate frontend monitoring | separate frontend monitoring | separate frontend monitoring | Web Vitals + client instrumentation |
The framework APIs differ.
The failure model does not.
Where Should Monitoring Code Be Placed?
Monitoring code works best when it is centralized around framework lifecycle boundaries instead of being scattered throughout controllers and business services.
In Laravel, configure the health route in bootstrap/app.php. Put listeners for DiagnosingHealth, QueueBusy, scheduler events and other global monitoring hooks in App\Providers\AppServiceProvider or, once monitoring becomes substantial, create something like App\Providers\MonitoringServiceProvider. Place scheduled monitoring commands such as queue:monitor alongside the rest of your scheduler configuration. Laravel’s current documentation explicitly demonstrates QueueBusy listeners inside AppServiceProvider.
In Django, keep request instrumentation in middleware when it concerns the request/response lifecycle. Register application-wide signal receivers during application initialization where appropriate, and keep logging configuration centralized through Django’s LOGGING settings. Task monitoring can subscribe to the Django Tasks signals, while actual worker-health monitoring belongs to whichever production task backend runs those Tasks.
In Ruby on Rails, framework-wide subscribers fit naturally under config/initializers/. Register your Rails.error subscriber there, and put ActiveSupport::Notifications subscriptions in a dedicated monitoring initializer rather than duplicating instrumentation inside controllers. Solid Queue’s worker configuration belongs in config/queue.yml, with its execution infrastructure monitored separately from the Rails web process.
In NestJS, isolate health checks into a HealthModule, make application-wide exception or performance instrumentation global through filters/interceptors where appropriate, and keep BullMQ worker/queue event handlers with the consumers responsible for those queues. Terminus is designed around dedicated health controllers and reusable health indicators.
In Next.js, server instrumentation belongs in the root instrumentation.ts, including your OpenTelemetry registration and onRequestError implementation. Browser monitoring belongs in instrumentation-client.ts, while useReportWebVitals() can live inside a small Client Component mounted from your application layout.
Keeping these hooks at framework boundaries gives you one place to observe the application without mixing observability code into the business logic you are actually trying to monitor.
- Founded
- 2014
- Years in business
- 12
- People
- 66
- Headquarters
- Lahore, Pakistan
- Projects delivered
- 250+
- AI systems in production
- 10

