
# Centralized logging

Centralized logging is **off by default**. Setting it up has two parts: wiring the `ranetrace` log channel into your app and turning it on in your **production** environment, where logging actually runs.

## Wire the channel into your log stack

Route your whole application log to Ranetrace by adding the `ranetrace` channel to your stack in `config/logging.php`. This is code, so it deploys to every environment:

```php
// config/logging.php
'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['single', 'ranetrace'],
        'ignore_exceptions' => false,
    ],
],
```

The package registers the `ranetrace` channel for you, so this stack entry is valid everywhere. Where logging is off the channel is inert: records short-circuit at the handler and nothing is sent, so it is safe in every environment even though it only runs in production.

To forward only specific records instead of your whole log, skip the stack and write to the `ranetrace` channel directly from anywhere in your app:

```php
use Illuminate\Support\Facades\Log;

Log::channel('ranetrace')->error('Webhook payload missing signature', [
    'provider' => 'stripe',
]);
```

## Turn it on in production

Logging only does something once it is enabled, and it belongs in production, not local development. Set these in your **production** environment, not just a local `.env`:

```dotenv
RANETRACE_ENABLED=true
RANETRACE_LOGGING_ENABLED=true
```

Also set `RANETRACE_KEY` from your Ranetrace dashboard if it is not already configured. Records at or above `notice` are forwarded; change the threshold with `RANETRACE_LOGGING_LEVEL`.

To check the wiring before deploying, set the same flags in your local `.env`. That sends your development logs to Ranetrace, so treat it as a one-off check rather than a permanent local setting.

## Configuration options

Every setting for centralized logging lives under the `logging` key in `config/ranetrace.php`. Publish the config with `php artisan vendor:publish --tag=ranetrace-laravel-config` to edit the array, or set the matching env vars:

```php
// config/ranetrace.php
'logging' => [
    'enabled' => env('RANETRACE_LOGGING_ENABLED', false),
    'queue' => env('RANETRACE_LOGGING_QUEUE', true),
    'queue_name' => env('RANETRACE_LOGGING_QUEUE_NAME', 'default'),
    'timeout' => env('RANETRACE_LOGGING_TIMEOUT', 10),
    'level' => env('RANETRACE_LOGGING_LEVEL', 'notice'),
    'excluded_channels' => [
        // Channel names that should never be sent to Ranetrace
    ],
],
```

| Env var | Config key | Default | What it controls |
|---|---|---|---|
| `RANETRACE_LOGGING_ENABLED` | `logging.enabled` | `false` | Turns centralized logging on. While off, the channel stays registered but every record short-circuits at the handler. |
| `RANETRACE_LOGGING_LEVEL` | `logging.level` | `notice` | Minimum level forwarded, from `debug` up to `emergency`. |
| `RANETRACE_LOGGING_QUEUE` | `logging.queue` | `true` | Buffer each captured record and ship it off the request path. Set to `false` to handle it synchronously. |
| `RANETRACE_LOGGING_QUEUE_NAME` | `logging.queue_name` | `default` | Queue the log-capture job is dispatched on. |
| `RANETRACE_LOGGING_TIMEOUT` | `logging.timeout` | `10` | Timeout, in seconds, for the log-capture job. |
| (config only) | `logging.excluded_channels` | `[]` | Monolog channel names never forwarded. See **Excluding channels** below. |

Logging also respects the master `RANETRACE_ENABLED` switch: while it is `false`, nothing is sent regardless of the options above.

When `logging.queue` is `true`, captured records are buffered and shipped by the batch worker. The shared delivery settings (buffer size, buffer TTL, cache store, batch queue name) are tuned by the `RANETRACE_BATCH_*` options documented in [the worker](/docs/the-worker).

## Customizing the channel

The `ranetrace` channel is registered for you only when you have not defined one yourself. To customize it (for example a different minimum level), define `ranetrace` in `config/logging.php` and your definition always wins:

```php
// config/logging.php
'channels' => [
    'ranetrace' => [
        'driver' => 'ranetrace',
        'level' => env('RANETRACE_LOGGING_LEVEL', 'notice'),
    ],
],
```

## Excluding channels

The handler skips records whose Monolog channel name appears in `ranetrace.logging.excluded_channels`. The list is empty by default:

```php
// config/ranetrace.php
'logging' => [
    'excluded_channels' => [
        // Channel names that should never be sent to Ranetrace
    ],
],
```

## Secret scrubbing

Before a record is sent, Ranetrace redacts secrets to `[REDACTED]`: values stored under sensitive keys (`password`, `pwd`, `token`, `api_key`, `secret`, `authorization`, and similar) and `key=value` secrets written into the message string. Two kinds of secret are also caught by their value, in the message and in every string of context and extra, whatever key they sit under: the password in a URL, with the user name and host kept (`pgsql://app:secret@db/app` arrives as `pgsql://app:[REDACTED]@db/app`), and a token whose prefix says it is a credential, such as a Stripe `sk_live_` key, an AWS `AKIA` access key id, a GitHub `ghp_` token or a JWT. The full lists are in [Error tracking](/docs/error-tracking). Extend the sensitive-key list, which is only ever added to and never shrunk, in `config/ranetrace.php`:

```php
// config/ranetrace.php
'scrubbing' => [
    'extra_keys' => [
        'x_internal_signature',
    ],
],
```

Scrubbing is defense-in-depth. Avoid deliberately logging secrets regardless.

## Limits applied by the handler

From `RanetraceLogHandler`:

- Messages over **50 000 characters** are truncated with a `... (truncated)` suffix.
- Context that serializes to more than **50 KB** of JSON is replaced with `{"_truncated":"Context exceeded 50KB limit and was removed"}`.

These keep individual log records well under the API's 5 MB request limit.

## Internal diagnostics channel

Ranetrace writes its own diagnostics to a separate, package-owned `ranetrace_internal` channel (a daily file at `storage/logs/ranetrace-internal.log`), never back through the `ranetrace` channel, so capturing your application log cannot create a feedback loop. It is independent of centralized logging and has its own options in `config/ranetrace.php`:

```php
// config/ranetrace.php
'internal_logging' => [
    'enabled' => env('RANETRACE_INTERNAL_LOGGING_ENABLED', true),
    'level' => env('RANETRACE_INTERNAL_LOGGING_LEVEL', 'debug'),
    'days' => env('RANETRACE_INTERNAL_LOGGING_DAYS', 14),
    'stderr_fallback' => env('RANETRACE_INTERNAL_STDERR_FALLBACK', true),
],
```

| Env var | Default | What it controls |
|---|---|---|
| `RANETRACE_INTERNAL_LOGGING_ENABLED` | `true` | Whether Ranetrace records its own diagnostics at all. |
| `RANETRACE_INTERNAL_LOGGING_LEVEL` | `debug` | Minimum level for the internal channel. |
| `RANETRACE_INTERNAL_LOGGING_DAYS` | `14` | Days of rotated internal logs to keep. |
| `RANETRACE_INTERNAL_STDERR_FALLBACK` | `true` | Fall back to `stderr` when the internal log file cannot be written. |

`ranetrace_internal` is reserved for Ranetrace. Do not route application logs to it or add it to your stack.

## Testing your setup

```bash
php artisan ranetrace:test-logging
```

The command checks that the feature is enabled, the channel is registered, sends test entries at multiple levels, and prints what's missing if anything.

## Disabling

```dotenv
RANETRACE_LOGGING_ENABLED=false
```

Records will short-circuit at the handler.
