Skip to content

Logging ​

Logging is a PSR-3 file log with one line per entry. Each line carries the per-request trace ID, so bootstrap lines, dispatch lines, and error lines for the same request can be grepped together.

Logger vs Log ​

ClassWhat it isWhen to use it
App\Core\LoggerThe file logger instance owned by SystemAlmost never directly - System builds it for you
App\Core\LogStatic holder mirroring the active loggerApp code: Log::info(), Log::error(), and friends

System::__construct() creates the Logger and calls Log::setLogger(), so every later Log::info() call writes to the same correlated stream. Log methods never throw: without a booted System they fall back to error_log(), so logging can never break a request.

php
use App\Core\Log;

Log::info('user created', ['id' => $user->id]);
Log::warning('slow query', ['ms' => 850]);
Log::error('payment failed', ['order' => $orderId]);

Available levels: debug, info, notice, warning, error, critical, alert, emergency, plus Log::log($level, $message, $context).

Configuration ​

KeyEnvDefaultMeaning
logPathLOG_PATHAPP_ROOT/storage/logs/app.logLog file path
logEnabledLOG_ENABLEDfalse (off)Write routine debug/info/notice lines
bash
# Write routine info lines too (default keeps only warning and above)
LOG_ENABLED=true

# Custom log file (relative paths resolve against APP_ROOT)
LOG_PATH=storage/logs/app.log

Notes:

  • Routine logs are off by default: debug, info, and notice are dropped, while warning and above are always written so crash visibility is never lost.
  • LOG_PATH rejects empty values, null bytes, and .. segments and falls back to the default instead of throwing.
  • config/config.php exposes the same values as logPath and logEnabled for post-validation consumers. Setting .env alone takes effect without re-adding the key. See Configuration.

Trace ID correlation ​

Every System instance generates a per-request trace ID. It appears on every log line, in the production 500 message (Internal Server Error (trace: <id>), see Error Handling), and in the fallback ID ErrorHandler generates when boot failed before System existed.

Log line format:

[2026-01-01 12:00:00] [<traceId>] [<LEVEL>] <message> <context JSON>
bash
grep "<traceId>" storage/logs/app.log

Request lifecycle log lines ​

With LOG_ENABLED=true, a normal request writes lines like these (names are the exact messages, so grep for them):

StageMessageLevel
Bootstrap startbootstrap startedinfo
Env syncconfig env syncedinfo
Config checkconfig validatedinfo
Bootstrap donebootstrap completedinfo
Route lintroute validation completed (or ... with errors)info
Dispatch startrouter dispatch started with method plus uriinfo
Dispatch donerouter dispatch completed with codeinfo
Unknown pathroute not found with method plus uriwarning
Dispatch failurerouter dispatch failed with error, class, method, urierror
View renderview rendered with viewinfo
View failureview render failed with view plus errorerror
Bootstrap failurebootstrap failed with errorerror
Unhandled throwableunhandled exception with error, class, file, traceerror

Bad route handlers are logged as invalid route handler warnings at boot without breaking the request. Run php roolith route:list to lint them as a CI gate instead - see Response.

App logging example ​

Five lines, copy-paste ready:

php
use App\Core\Log;

public function store()
{
    Log::info('user store started', ['email' => Request::input('email')]);
    $user = User::create($this->validated());
    Log::info('user created', ['id' => $user->id]);

    return $this->json($user, "success", 201, "User created.");
}

App logging always goes through Log as shown above. See Cache and Events for those helpers.

Released under the MIT License.