PHP centralized logging

Centralized logging is off by default in the PHP SDK. Turning it on has two parts: adding the Ranetrace handler to your Monolog logger, and setting the enable flag in the environment where logging actually runs.

Using Laravel? See Centralized Logging instead. There the channel is registered for you.

Add the handler to your logger

monologHandler() returns a Monolog handler you push onto your own logger, alongside whatever handlers you already have:

use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$logger = new Logger('app');
$logger->pushHandler(new StreamHandler(__DIR__.'/../storage/app.log'));
$logger->pushHandler($ranetrace->monologHandler());

The handler bubbles by default, so adding it does not stop your other handlers from seeing the record.

The handler is safe to leave in place before the feature is on: while logging is disabled every record short-circuits inside the handler and nothing is captured. That means you can commit this wiring and switch it on per environment.

To send only specific records rather than your whole application log, give Ranetrace a logger of its own:

$ranetraceLogger = new Logger('webhooks');
$ranetraceLogger->pushHandler($ranetrace->monologHandler());

$ranetraceLogger->error('Webhook payload missing signature', ['provider' => 'stripe']);

Turn it on

RANETRACE_ENABLED=true
RANETRACE_LOGGING_ENABLED=true

Or in the config array, if you configure the SDK in code:

$ranetrace = Ranetrace::init([
    'key' => getenv('RANETRACE_KEY') ?: '',
    'logging' => ['enabled' => true],
]);

Logging belongs in production rather than local development. To check the wiring before deploying, set the same flags locally and treat it as a one-off check.

The minimum level

Records at or above notice are forwarded. That is the handler's default rather than Monolog's usual debug, so a handler mounted with no arguments gets the level your Ranetrace configuration names instead of your entire debug stream.

Change it for every logger through configuration:

RANETRACE_LOGGING_LEVEL=warning

Or for one handler, by passing a level. Anything Monolog accepts works, and an unusable value is rejected loudly by Monolog rather than quietly downgraded:

use Monolog\Level;

$logger->pushHandler($ranetrace->monologHandler(Level::Warning));

The PSR-3 order is debug, info, notice, warning, error, critical, alert, emergency.

Channels

Each record arrives with the channel name of the logger that produced it, which is the name you passed to new Logger('app'). Ranetrace records that name as the log's channel, so give your loggers names that mean something in a dashboard.

Channels you never want forwarded go on the exclusion list. The handler drops those records before anything else happens:

$ranetrace = Ranetrace::init([
    'key' => getenv('RANETRACE_KEY') ?: '',
    'logging' => [
        'enabled' => true,
        'excluded_channels' => ['deploy', 'audit'],
    ],
]);

Or as a comma-separated list in the environment:

RANETRACE_LOGGING_EXCLUDED_CHANNELS=deploy,audit

What is added and what is capped

Alongside the level, message, context and channel, every record carries an extra object. Your own Monolog extra is kept, and Ranetrace adds your environment name and the PHP version to it, plus the framework name and version when you configured them.

Three size limits keep a single record small enough that a batch of them stays well under the API's 5 MB request limit. They are not configurable:

  • Messages longer than 50 000 characters are truncated, with a ... (truncated) suffix that counts inside the limit.
  • Context that serializes to more than 50 KB of JSON is replaced wholesale with {"_truncated":"Context exceeded 50KB limit and was removed"}. It is replaced rather than trimmed, because half a JSON structure is not a JSON structure.
  • Your own extra is replaced the same way past 10 KB, with "Extra data exceeded 10KB limit and was removed". Only your part is capped: the environment and PHP version are added afterwards, so that triage information survives even when the rest was dropped.

Secret scrubbing

Log messages, context and extra are scrubbed before they are buffered: values under sensitive keys and key=value secrets in the message string become [REDACTED]. The message is scrubbed before it is truncated, so a secret cannot survive by sitting across the length boundary. The key fragments and how to extend them are described in PHP error tracking.

The SDK's own diagnostics

Ranetrace never routes its own diagnostics through your logger. It writes them to a daily file, internal-YYYY-MM-DD.log, inside the buffer directory. That isolation is deliberate: if SDK diagnostics went through a logger you had pointed at Ranetrace, a failing send would log a failure that gets captured, buffered and sent, which fails again.

Because of that, you do not need to exclude anything to prevent a loop.

Environment variable Config key Default What it controls
RANETRACE_INTERNAL_LOGGING_ENABLED internal_logging.enabled true Whether the SDK records its own diagnostics.
RANETRACE_INTERNAL_LOGGING_LEVEL internal_logging.level debug Minimum level for those diagnostics.
RANETRACE_INTERNAL_LOGGING_DAYS internal_logging.days 14 Days of daily files to keep.
RANETRACE_INTERNAL_STDERR_FALLBACK internal_logging.stderr_fallback true Fall back to stderr when the file cannot be written.

Configuration

Environment variable Config key Default What it controls
RANETRACE_LOGGING_ENABLED logging.enabled false Turns centralized logging on. While off, records short-circuit at the handler.
RANETRACE_LOGGING_LEVEL logging.level notice Minimum level forwarded when the handler is mounted without one.
RANETRACE_LOGGING_EXCLUDED_CHANNELS logging.excluded_channels [] Channel names never forwarded.
RANETRACE_LOGGING_TIMEOUT logging.timeout 10 Seconds to wait for the API when a batch of logs is sent.

Captured records reach the API on the next flush. See PHP installation.