junixlabs/laravel-observatory

Observability toolkit for Laravel applications - Monitor HTTP requests, outbound calls, queue jobs, and scheduled tasks with Prometheus and SidMonitor support

Maintainers

Package info

github.com/junixlabs/laravel-observatory

pkg:composer/junixlabs/laravel-observatory

Statistics

Installs: 384

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

2.0.0-beta.1 2026-03-19 08:59 UTC

This package is auto-updated.

Last update: 2026-03-19 12:24:36 UTC


README

Latest Version on Packagist GitHub Tests Action Status Total Downloads License PHP Version

A comprehensive observability toolkit for Laravel applications. Monitor HTTP requests, outbound API calls, queue jobs, scheduled tasks, and exceptions with structured logging and optional Prometheus or SidMonitor metrics.

Features

  • Inbound Request Logging - Automatically log all incoming HTTP requests
  • Outbound HTTP Logging - Log external API calls with service detection
  • Queue Job Logging - Track job execution with duration and memory usage
  • Scheduled Task Logging - Monitor artisan scheduled tasks lifecycle (start, finish, fail, skip)
  • Exception Logging - Structured exception logging with stack traces
  • Request ID Tracking - Correlation IDs for distributed tracing
  • Sensitive Data Masking - Automatic masking of passwords, tokens, and PII
  • Dual Exporter Support - Choose between Prometheus (pull) or SidMonitor (push) metrics
  • Circuit Breaker - Resilient push-based exporting with automatic backoff
  • Zero Configuration - Works out of the box with sensible defaults
  • Grafana Dashboards - Pre-built dashboard templates included

Requirements

  • PHP 8.0+
  • Laravel 9.0, 10.0, 11.0, or 12.0

Installation

composer require junixlabs/laravel-observatory

The package auto-registers and works immediately - no configuration needed!

Quick Start

After installation, Observatory automatically:

  1. Logs all incoming HTTP requests to storage/logs/observatory.log
  2. Logs outbound HTTP calls via Laravel's HTTP client
  3. Logs queue job execution
  4. Logs scheduled task execution
  5. Logs exceptions with context

View Your Logs

tail -f storage/logs/observatory.log | jq

Configuration

Publish Config (Optional)

php artisan vendor:publish --tag=observatory-config

Environment Variables

All features are enabled by default. Only set these if you need to change defaults:

# Disable Observatory entirely
OBSERVATORY_ENABLED=false

# Change log channel (default: 'observatory' -> storage/logs/observatory.log)
# Use 'stderr' for Docker/K8s
OBSERVATORY_LOG_CHANNEL=stderr

# Disable specific loggers
OBSERVATORY_INBOUND_ENABLED=false
OBSERVATORY_OUTBOUND_ENABLED=false
OBSERVATORY_JOBS_ENABLED=false
OBSERVATORY_SCHEDULED_TASKS_ENABLED=false
OBSERVATORY_EXCEPTIONS_ENABLED=false

# Log request/response bodies (disabled by default - can be large)
OBSERVATORY_LOG_BODY=true

# Only log slow requests (0 = log all)
OBSERVATORY_SLOW_THRESHOLD_MS=1000

Exporters

Observatory supports two metrics exporters. Set via OBSERVATORY_EXPORTER env var.

Prometheus (default, pull-based)

OBSERVATORY_EXPORTER=prometheus
OBSERVATORY_PROMETHEUS_ENABLED=true
OBSERVATORY_PROMETHEUS_STORAGE=apcu  # or 'redis', 'memory'

Exposes a /metrics endpoint scraped by Prometheus.

SidMonitor (push-based)

OBSERVATORY_EXPORTER=sidmonitor
SIDMONITOR_ENDPOINT=https://api.sidmonitor.com
SIDMONITOR_API_KEY=your-api-key

Buffers data in-memory and flushes in batches to the SidMonitor backend. Includes a circuit breaker that pauses sending after consecutive failures to avoid blocking your application.

# Batch settings
SIDMONITOR_BATCH_SIZE=100
SIDMONITOR_BATCH_INTERVAL=10

# Circuit breaker
SIDMONITOR_CIRCUIT_BREAKER_THRESHOLD=3   # failures before opening
SIDMONITOR_CIRCUIT_BREAKER_COOLDOWN=30   # seconds before retry

Log Channel

Observatory auto-registers the observatory log channel:

  • File: storage/logs/observatory.log
  • Format: JSON (Loki/ELK compatible)
  • Rotation: Daily, 14 days retention

For Docker/Kubernetes, use stderr:

OBSERVATORY_LOG_CHANNEL=stderr

Custom Headers

Extract custom headers into logs (multi-tenant, workspace, etc.):

// config/observatory.php
'inbound' => [
    'custom_headers' => [
        'X-Workspace-Id' => 'workspace_id',
        'X-Tenant-Id' => 'tenant_id',
        'X-Correlation-Id' => 'correlation_id',
    ],
],

Result in logs:

{
  "request_id": "abc-123",
  "method": "POST",
  "path": "/api/users",
  "workspace_id": "ws-456",
  "tenant_id": "tenant-789"
}

Service Detection

Identify external services in outbound logs:

// config/observatory.php
'outbound' => [
    'services' => [
        '*.stripe.com' => 'stripe',
        '*.amazonaws.com' => 'aws',
        '*.sendgrid.com' => 'sendgrid',
        'api.myservice.com' => 'my_service',
    ],
],

Excluding Paths/Jobs/Tasks

// config/observatory.php
'inbound' => [
    'exclude_paths' => [
        'telescope*',
        'horizon*',
        'health',
        'metrics',
    ],
],

'jobs' => [
    'exclude_jobs' => [
        'App\Jobs\InternalHealthCheck',
    ],
],

'scheduled_tasks' => [
    'exclude_commands' => [
        'schedule:run',
    ],
],

Structured Log Examples

Inbound Request

{
  "message": "HTTP_REQUEST",
  "context": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "inbound",
    "method": "POST",
    "url": "https://example.com/api/v1/orders",
    "path": "api/v1/orders",
    "route": "orders.store",
    "status_code": 201,
    "duration_ms": 145.23,
    "ip": "192.168.1.1",
    "user_agent": "Mozilla/5.0...",
    "user_id": 123,
    "memory_mb": 45.2,
    "environment": "production"
  }
}

Outbound Request

{
  "message": "HTTP_OUTBOUND",
  "context": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "outbound",
    "service": "stripe",
    "method": "POST",
    "url": "https://api.stripe.com/v1/charges",
    "host": "api.stripe.com",
    "status_code": 200,
    "duration_ms": 523.45,
    "environment": "production"
  }
}

Job Processed

{
  "message": "JOB_PROCESSED",
  "context": {
    "job_id": "123",
    "job_name": "App\\Jobs\\ProcessOrder",
    "queue": "orders",
    "status": "processed",
    "duration_ms": 1234.56,
    "attempts": 1,
    "memory": {
      "used_mb": 12.5,
      "peak_mb": 45.2
    },
    "environment": "production"
  }
}

Scheduled Task

{
  "message": "SCHEDULED_TASK",
  "context": {
    "command": "reports:generate",
    "description": "Generate daily reports",
    "expression": "0 2 * * *",
    "status": "completed",
    "duration_ms": 4523.12,
    "exit_code": 0,
    "memory": {
      "used_mb": 32.1,
      "peak_mb": 64.5
    },
    "environment": "production"
  }
}

Exception

{
  "message": "EXCEPTION",
  "context": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "exception_class": "App\\Exceptions\\PaymentException",
    "message": "Payment declined",
    "code": 402,
    "file": "/app/Services/PaymentService.php",
    "line": 145,
    "request": {
      "method": "POST",
      "url": "https://example.com/api/orders",
      "path": "api/orders"
    },
    "user": {
      "id": 123
    },
    "trace": ["..."],
    "environment": "production"
  }
}

Prometheus Metrics (Optional)

OBSERVATORY_PROMETHEUS_ENABLED=true
OBSERVATORY_PROMETHEUS_STORAGE=apcu  # or 'redis', 'memory'

Visit http://your-app.test/metrics to see metrics.

Available Metrics

Metric Type Description
{app}_http_requests_total Counter Total HTTP requests
{app}_http_request_duration_seconds Histogram Request latency
{app}_http_outbound_requests_total Counter Outbound HTTP requests
{app}_jobs_processed_total Counter Queue jobs processed
{app}_scheduled_tasks_total Counter Scheduled tasks executed
{app}_exceptions_total Counter Exceptions count

Prometheus Auth

OBSERVATORY_METRICS_AUTH=true
OBSERVATORY_METRICS_USER=prometheus
OBSERVATORY_METRICS_PASS=secret

Grafana Dashboards

Import pre-built dashboards from the dashboards/ directory:

Dashboard Data Source Description
observatory-dashboard.json Loki Request logs, user analytics, exceptions
prometheus-dashboard.json Prometheus Metrics overview, latency percentiles

LogQL Query Examples

# All requests
{job="laravel-observatory"} | json | message="HTTP_REQUEST"

# Errors only
{job="laravel-observatory"} | json | status_code >= 400

# By user
{job="laravel-observatory"} | json | user_id="123"

# Slow requests (>1s)
{job="laravel-observatory"} | json | duration_ms > 1000

# External service calls
{job="laravel-observatory"} | json | type="outbound" | service="stripe"

# Scheduled tasks
{job="laravel-observatory"} | json | message="SCHEDULED_TASK"

# Exceptions
{job="laravel-observatory"} | json | message="EXCEPTION"

Kubernetes Deployment

See k8s/README.md for Kubernetes deployment with Loki stack.

Upgrading from v1.x

Breaking Changes in v2.0

  1. SidMonitor env vars renamed: OBSERVATORY_SIDMONITOR_* is now SIDMONITOR_*
  2. ExporterInterface: New recordScheduledTask() method required for custom exporters

Update your .env file if using SidMonitor:

# Before (v1.x)
OBSERVATORY_SIDMONITOR_ENDPOINT=...
OBSERVATORY_SIDMONITOR_API_KEY=...

# After (v2.0)
SIDMONITOR_ENDPOINT=...
SIDMONITOR_API_KEY=...

Re-publish config if upgrading:

php artisan vendor:publish --tag=observatory-config --force

Testing

composer test

Changelog

See CHANGELOG for recent changes.

Contributing

See CONTRIBUTING for details.

Security

Report security issues to chuongld@canawan.com instead of the issue tracker.

Credits

License

MIT License. See LICENSE for details.