PHP logging: error_log, exceptions, and PSR-3
Configure PHP error reporting, distinguish SAPI and FPM destinations, preserve exception details, and move application logs toward PSR-3 and Monolog.
PHP's built-in error reporting and error_log() function can cover basic diagnostics. PSR-3 gives application code a consistent logging interface, while Monolog adds handlers, levels, and structured context. A dependable setup needs both the application records and the PHP runtime errors that may happen outside that logger.
Start by identifying which PHP configuration the process actually loads and where each destination writes. CLI, web-server, and PHP-FPM processes can use different settings. This guide explains the documented behavior; the PHP and FPM examples were not executed in a live application for this article.
Configure error reporting without exposing details to users
A production configuration can keep error details out of responses while retaining them for operators:
; Production: record errors without displaying them to users.
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
error_reporting = E_ALL
The directory must exist and be writable by the PHP process. Apply the settings to the correct SAPI and reload the relevant service when necessary. A configured path does not guarantee that every write succeeds, so generate a controlled error and inspect the destination.
E_ALL preserves visibility into notices and deprecations as well as more severe errors. If deprecation volume needs a separate policy, route or handle it deliberately rather than suppressing it everywhere by default. Development can enable display_errors, but production responses should show a suitable generic error message rather than paths, stack traces, or configuration values. PHP error configuration
Use error_log with the correct destination
Within application code where a payment callback payload is already available:
// Type 0 uses PHP's configured logging destination.
error_log('Order creation failed order_id=1024');
// Type 3 appends to this file; add the newline yourself.
$record = [
'event' => 'payment_callback',
'order_id' => $payload['order_id'] ?? null,
'status' => $payload['status'] ?? null,
];
error_log(
json_encode($record, JSON_THROW_ON_ERROR) . PHP_EOL,
3,
'/var/log/php/payments.log'
);
// Type 4 delegates to the SAPI logging handler.
error_log('Queue processing failed', 4);
Type 0 follows PHP's configured system logging mechanism. Type 3 appends to the supplied destination and does not add a newline. Type 4 sends the message to the SAPI handler; it is not a universal promise of stderr output in every deployment. For explicit stderr in a CLI script, use fwrite(STDERR, "Queue processing failed\n"). Whether container tooling captures that stream depends on process and runtime configuration. PHP error_log reference
Select callback fields rather than writing an entire payment payload. JSON_THROW_ON_ERROR makes serialization failure visible to the caller, so handle that failure at the application's appropriate error boundary. error_log() itself has no standard severity or context model; manually encoding JSON does not add PSR-3 semantics.
Understand PHP's error categories
| Constant | Meaning |
|---|---|
E_ERROR |
Fatal runtime error that terminates execution |
E_WARNING |
Runtime warning; execution generally continues |
E_PARSE |
Parse error |
E_NOTICE |
Runtime notice about a potentially unintended condition |
E_DEPRECATED |
Use of a deprecated feature |
E_CORE_ERROR / E_COMPILE_ERROR |
Fatal errors during engine startup or compilation |
An expression such as error_reporting(E_ALL & ~E_DEPRECATED) changes the reporting mask. That can be useful for a deliberate policy, but it removes those diagnostics from ordinary reporting rather than fixing their cause.
Framework handlers can convert supported PHP errors into exceptions, subject to the reporting mask and handler rules. They do not turn every startup, parse, or fatal condition into a catchable application exception. Configure engine-level logging even when a framework provides its own exception reporting.
Preserve exception context and the failure result
When application code catches an exception to add useful context, log the exception and preserve the intended failure behavior. This fragment assumes $payment and $order are supplied by the application:
try {
$payment->charge($order);
} catch (Throwable $e) {
error_log('Payment failed order_id=' . $order->id . ': ' . (string) $e);
throw $e;
}
Casting a Throwable to a string includes its type, message, location, and stack details. Check whether the framework will report the rethrown exception again; choose one reporting owner or accept duplicates intentionally. Stack traces and messages may contain sensitive values, so apply the application's redaction and access policy.
For a standalone CLI program without framework exception handling, a final handler can report an uncaught exception and return failure to the scheduler:
set_exception_handler(function (Throwable $e): void {
error_log('Uncaught exception: ' . (string) $e);
exit(1);
});
Register it before running the work. It does not resume execution at the throw site and cannot catch every startup or parse failure. In a web framework, customize the framework's reporting path instead of replacing it with this CLI handler; the framework also owns the HTTP response. PHP exception-handler reference
Send records through syslog where available
On a Unix-like host with an appropriate syslog service, this routes a warning using the local0 facility:
openlog('myapp', LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog(LOG_WARNING, 'Configuration missing; using the default');
closelog();
LOG_PID adds the process ID. LOG_PERROR also writes to the standard error stream where supported, which may create a second collected copy. Facilities such as LOG_LOCAL0 can help route events in the system's logging configuration. The actual destination depends on that host's syslog or journal setup and platform support. Validate it before assuming the messages appear in a particular file. PHP openlog reference
Give application code a PSR-3 logger
PSR-3 defines Psr\Log\LoggerInterface, with eight named level methods—emergency, alert, critical, error, warning, notice, info, and debug—and a general log method. Each call can carry a context array. With a PSR-3 logger and a caught exception:
$logger->info('Item purchased', ['item' => 'Orange Soda', 'price' => 100]);
$logger->error('Payment failed', [
'order_id' => 1024,
'exception' => $e,
]);
Use the exception context key for the exception. PSR-3 defines the interface and context conventions; it does not require a particular JSON representation or storage destination. Monolog supplies an implementation with handlers and formatters. Application code can depend on the interface while configuration selects those details. See the PSR-3 specification and the Monolog guide.
Validate engine, FPM, and application logs separately
Distinguish three possible destinations: the application's Monolog handlers, PHP engine error logging controlled by PHP settings, and the FPM master process's own error_log. FPM pool overrides can change PHP error settings, and catch_workers_output controls whether worker stdout/stderr is redirected into the main FPM error log. These are related but separate configuration paths. PHP-FPM configuration
In DataKit log collection, configure the actual files or streams and matching parsing rules. Keep source identities distinct enough to separate business exceptions from runtime or process-manager failures. Generate an application exception and a controlled PHP warning or error; verify timestamp, severity, and complete exception details for each.
Create log detection rules against those parsed fields and the relevant source. Only collected events can participate in detection. Revalidate after path, permissions, service-account, or rotation changes.
Common questions
Should display_errors be disabled in production?
Yes for user-facing production applications. Keep diagnostic details in controlled logs and return a suitable generic response. Turning off display does not turn on logging, so verify both settings and the destination.
Are FPM errors and Monolog records the same thing?
No. Monolog records application events. PHP engine errors follow PHP configuration, while the FPM master log records FPM activity and can also receive worker output when configured. Inspect the active pool and PHP settings instead of assuming one file contains everything.
Can error_log and Monolog coexist?
Yes. Existing calls can remain during a gradual migration to PSR-3. Keep their formats and sources identifiable, avoid double-reporting the same exception, and maintain parsing for both until migration is complete. Engine errors may still need separate collection afterward.
How should scheduled CLI jobs log?
Explicit stderr or a configured Monolog stream works well when the scheduler captures it. PHP's type-4 handler typically uses stderr in CLI, but use an explicit stream when that is the required contract. Verify the scheduler's redirection, retention, collection, and failure exit status; writing stdout alone does not make a record reach DataKit.
Related reading
Continue with Laravel logging. The Java logging library comparison and log-level guide are currently in Chinese.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan