Skip to content

Logging and Debug

Muhammet Şafak edited this page May 24, 2026 · 1 revision

Logging and Debug

InitORM\Database exposes two complementary observability features:

Feature What it captures Configured via
Failure log Per-failure messages — SQL + (optionally) bound parameters log + debug credentials
Query profiler Every executed query with timing and arguments enableQueryLog() / disableQueryLog()

This page covers the failure log. For the profiler, see Query Profiler.

The log credential — three accepted shapes

The log key accepts a file path, a callable, or any object with a critical(string) method.

1. File path

DB::createImmutable([
    'dsn' => 'mysql:host=localhost;dbname=app;charset=utf8mb4',
    'log' => __DIR__ . '/var/log/db.log',
]);

The Logger writes via file_put_contents() with append mode. The path may contain {year} / {month} / {day} placeholders — they're replaced at write time, so daily-rotated logs work out of the box:

'log' => __DIR__ . '/var/log/db-{year}-{month}-{day}.log',
// → var/log/db-2026-05-24.log

2. Callable

Any callable that accepts a single string works:

DB::createImmutable([
    'dsn' => 'mysql:host=localhost;dbname=app;charset=utf8mb4',
    'log' => function (string $message): void {
        error_log($message);
    },
]);

[$object, 'method'] arrays count as callables too — perfect for adapting third-party loggers:

$monolog = new \Monolog\Logger('db');
$monolog->pushHandler(new \Monolog\Handler\StreamHandler('php://stderr'));

DB::createImmutable([
    'dsn' => 'mysql:host=localhost;dbname=app;charset=utf8mb4',
    'log' => [$monolog, 'critical'],
]);

3. Object with critical() (PSR-3 friendly)

Any object that exposes a critical(string $message) method is accepted. This is the contract \Psr\Log\LoggerInterface already satisfies, so you can pass a PSR-3 logger directly:

DB::createImmutable([
    'dsn' => 'mysql:host=localhost;dbname=app;charset=utf8mb4',
    'log' => $psr3Logger, // any PSR-3 LoggerInterface
]);

If your logger uses a different method name, wrap it in a closure:

'log' => fn (string $msg) => $myLogger->error($msg),

What gets logged

Only query failures. Successful queries do not produce log entries — that's what the Query Profiler is for. The two systems are independent.

A failure log message looks like:

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'app.usrs' doesn't exist
SQL : SELECT id, name FROM usrs WHERE active = :active

Debug mode

When debug: true, the failure message is augmented with the bound parameters (JSON-encoded):

DB::createImmutable([
    'dsn'   => 'mysql:host=localhost;dbname=app;charset=utf8mb4',
    'debug' => true,
]);

Then the same failure logs as:

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'app.usrs' doesn't exist
SQL : SELECT id, name FROM usrs WHERE active = :active
PARAMS : {":active":1}

Enable debug in development only. Parameter dumps include user-supplied values — possibly credentials, emails, PII. Treat them as sensitive.

Combining log + debug

The two settings compose freely:

DB::createImmutable([
    'dsn'   => 'mysql:host=localhost;dbname=app;charset=utf8mb4',
    'log'   => $psr3Logger,
    'debug' => true,
]);

That gives you full failure messages (with params) routed through your PSR-3 pipeline.

Wiring Monolog end-to-end

A complete production-style setup:

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

$logger = new Logger('db');
$logger->pushHandler(new RotatingFileHandler(__DIR__ . '/var/log/db.log', 14, Logger::WARNING));
$logger->pushHandler(new StreamHandler('php://stderr', Logger::ERROR));

DB::createImmutable([
    'dsn'      => $_ENV['DB_DSN'],
    'username' => $_ENV['DB_USER'],
    'password' => $_ENV['DB_PASS'],
    'log'      => $logger,
    'debug'    => $_ENV['APP_ENV'] === 'dev',
]);

Custom logger with extra context

For per-message structured logging (e.g. correlating with request IDs), use a closure:

$requestId = bin2hex(random_bytes(8));

DB::createImmutable([
    'dsn' => $_ENV['DB_DSN'],
    'log' => function (string $msg) use ($requestId, $logger): void {
        $logger->critical($msg, ['request_id' => $requestId]);
    },
]);

Turning logging on/off at runtime

The Logger is initialised once from the credentials. To change it mid-flight (rare), reach into the underlying Connection:

$db->getConnection()->createLog('manual entry');

…but typically you wire the right sink at boot and forget about it.

See also

  • Query Profiler — for "what queries did I just run?" rather than "what failed?"
  • Configuration — every credential
  • FAQ — "how do I integrate with X logger?"

Clone this wiki locally