惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Martin Fowler
Martin Fowler
Y
Y Combinator Blog
F
Full Disclosure
Help Net Security
Help Net Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Hacker News
The Hacker News
大猫的无限游戏
大猫的无限游戏
T
Threat Research - Cisco Blogs
L
LINUX DO - 热门话题
Know Your Adversary
Know Your Adversary
U
Unit 42
P
Palo Alto Networks Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
Spread Privacy
Spread Privacy
T
Tor Project blog
Simon Willison's Weblog
Simon Willison's Weblog
C
Cyber Attacks, Cyber Crime and Cyber Security
Cyberwarzone
Cyberwarzone
MongoDB | Blog
MongoDB | Blog
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
雷峰网
雷峰网
N
News and Events Feed by Topic
Microsoft Security Blog
Microsoft Security Blog
人人都是产品经理
人人都是产品经理
C
Check Point Blog
P
Proofpoint News Feed
D
DataBreaches.Net
Security Archives - TechRepublic
Security Archives - TechRepublic
aimingoo的专栏
aimingoo的专栏
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
F
Fortinet All Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
SegmentFault 最新的问题
L
LINUX DO - 最新话题
Security Latest
Security Latest
T
Threatpost
博客园 - 【当耐特】
Cloudbric
Cloudbric
P
Privacy International News Feed
博客园 - 聂微东
M
MIT News - Artificial intelligence
S
Security @ Cisco Blogs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Secure Thoughts

Datadog | The Monitor blog

Introducing our open source AI-native SAST Instrument and monitor Boomi integration flows with OpenTelemetry and Datadog Not all index scans are equal: How we cut query latency by over 99% Platform engineering metrics: What to measure and what to ignore Integrate Recorded Future threat intelligence with Datadog Cloud SIEM CI/CD security: threat modeling using a MITRE-style threat matrix CI/CD security: How to secure your GitHub ecosystem Ingress NGINX is EOL: A practical guide for migrating to Kubernetes Gateway API Operating agentic AI with Amazon Bedrock AgentCore and Datadog LLM Observability: Lessons from NTT DATA Introducing the Datadog Code Security MCP Capture and analyze custom heatmaps in Session Replay Understand session replays faster with AI summaries and smart chapters Monitor ClickHouse query performance with Datadog Database Monitoring How we designed empathetic alert sounds for on-call engineers Search and act across Datadog to resolve issues faster with Bits Assistant Measure the business impact of every product change with Datadog Experiments Analyzing round trip query latency Configuring JavaScript caches for better performance Introducing Bits AI Dev Agent for Code Security Datadog achieves ISO 42001 certification for responsible AI Monitor Nutanix clusters, hosts, and VMs with Datadog Monitor Juniper Mist in Datadog A new Host Map for modern infrastructure Annotate traces to improve LLM quality with Datadog LLM Observability What’s new in Cloud SIEM: AI-powered investigations, enhanced threat intelligence, and scalable security operations Explore Kubernetes with native OpenTelemetry data Monitor Oracle Fusion Cloud Applications with Datadog Announcing the Datadog Terraform provider v4.0.0 Scaling Kubernetes workloads on custom metrics How to design cloud environments for AI-powered threat analysis Monitor Aruba Central in Datadog How we centralize and remediate risks with Datadog Case Management Accelerate incident response with Datadog and ServiceNow Monitor your application and network load balancer logs Understanding Karpenter architecture for Kubernetes autoscaling Tools for collecting metrics and logs from Karpenter Monitor Karpenter with Datadog What your product data is actually saying Key metrics for monitoring Karpenter Securing Datadog’s platform in the AI age: The role of observability data Four ways engineering teams use the Datadog MCP Server to power AI agents Approaching your observability migration with the right mindset Meet the new Bits AI SRE: Deeper reasoning, twice as fast Key learnings from the 2026 State of DevSecOps study Use plain English to query your multi-cloud infrastructure in Resource Catalog Simplifying troubleshooting across the user journey with Datadog Synthetic Monitoring Protect your OCI resources with Datadog Cloud Security This Month in Datadog - February 2026 Amazon EC2 security: How misconfigured and public AMIs expand your cloud attack surface Enable end-to-end visibility into your Java apps with a single command Measure and improve mobile app startup performance with Datadog RUM Evaluating our AI Guard application to improve quality and control cost Identify untested code across every level of your codebase Make use of guardrail metrics and stop babysitting your releases Monitor Versa Networks SD-WAN performance in Datadog Improve performance and reliability with APM Recommendations Remediate transitive vulnerabilities faster with Datadog Software Composition Analysis Generate audit-ready vulnerability and compliance reports with Datadog Sheets Monitor Fortinet FortiManager performance in Datadog Improve test coverage across codebases with Datadog Code Coverage Move fast, don’t break things: Consistent testing standards at scale Enrich logs with ServiceNow CMDB context before routing to any SIEM or logging tool Monitor Lustre with Datadog Make faster, better product decisions with Datadog Product Analytics Surface and remediate runtime posture issues with Workload Protection Findings Protect agentic AI applications with Datadog AI Guard How to optimize JavaScript code with CSS Trace Google Pub/Sub workloads in Cloud Run with Datadog Detect human names in logs with ML in Sensitive Data Scanner How we cut our NLQ agent debugging time from hours to minutes with LLM Observability Debug PostgreSQL query latency faster with EXPLAIN ANALYZE in Datadog Database Monitoring Datadog acquires Propolis Unify and correlate frontend and backend data with retention filters Scale compliance across global frameworks with Datadog Cloud Security Monitor Arista VeloCloud SD-WAN performance with Datadog Building reliable dashboard agents with Datadog LLM Observability Simplify log collection and aggregation for MSSPs with Datadog Observability Pipelines Mitigation for Node.js denial-of-service vulnerability affecting Datadog APM Automate flaky test fixes with the Bits AI Dev Agent and Test Optimization How we built an AI SRE agent that investigates like a team of engineers Datadog integrations 2025 recap: Observability for AI, security, and hybrid cloud Design effective executive dashboards with Datadog Implement dbt data quality checks with dbt-expectations Bring faster visibility into AWS Lambda functions with remote instrumentation Troubleshoot faster with the GitLab Source Code integration in Datadog How Cambia Health Solutions saved $30,000 monthly with Cloud Cost Management and the Datadog Resource Catalog Normalize any logs for Cloud SIEM with Datadog's OCSF processor Optimizing Datadog at scale: Cost-efficient observability at Zendesk Detect, diagnose, and resolve network issues easily with CNM Network Health Connect engineering errors to user impact in early-stage products Cilium configuration for Kubernetes operations at scale Designing feedback loops for progressive delivery Ship features faster and safer with Datadog Feature Flags Choosing the right OpenTelemetry Collector distribution Route your monitor alerts with Datadog monitor notification rules Automate Cloud SIEM investigations with Bits AI Security Analyst Cloud threat detection: How to identify risky activity across control and data planes Collecting Kafka performance metrics Monitoring Kafka with Datadog Monitoring Kafka performance metrics
PHP logging: How to collect, customize, and analyze PHP logs
2019-04-29 · via Datadog | The Monitor blog
Nils Bunge

Nils Bunge

David M. Lentz

David M. Lentz

PHP logs are not just about errors. You can use logs to track the performance of API calls and function calls, or to count the occurrence of significant events in your applications (e.g., logins, signups, and downloads). Whether you’re operating a microservices architecture or a monolith, implementing a comprehensive PHP logging strategy will allow you to track critical changes in your applications and optimize their performance.

PHP and its available logging libraries give you many options for where to send and store your logs. As you’ll see in this post, storing your PHP logs in a central file is simple and gives you the greatest flexibility for processing and analyzing your logs later on. When you use a specialized tool to tail your log file and forward your logs to a central log management solution, your application code isn’t burdened with the overhead of buffering logs and handling network errors.

In this post, you’ll learn how to:

How PHP creates logs

The PHP system logger creates logs automatically when the execution of your code produces an error. Additionally, you can create logs by calling PHP’s logging functions as you need to log custom errors and arbitrary events in your application. In this section, we’ll look at how logs are created and routed by each of these mechanisms.

The PHP system logger

You can configure the PHP system logger by using the error_reporting directive in PHP’s configuration file, php.ini, to designate the types of errors PHP will automatically log. This directive uses a set of predefined constants and bitwise operators to express what types of events to include and exclude from logs. For example, you would use this directive to log all errors:

error_reporting = E_ALL

PHP’s display_errors configuration directive gives you the option of displaying log messages in the browser. In a production environment, you should always set display_errors to Off for security reasons. However, in a development environment, you might want to display warnings and errors directly in the browser so developers can easily see information about the application’s status.

PHP displays warning messages in the browser.

The PHP system logger routes logs in different ways depending on the value of the error_log configuration directive in php.ini:

  • If error_log names a file, PHP writes its logs to that file.
  • If error_log is set to syslog, PHP sends logs to the OS logger. This is usually syslog or the newer rsyslog (which implements the syslog protocol) on Linux, or Event Log on Windows.
  • If error_log is unset, PHP creates logs using the Server API (SAPI). The SAPI used depends on your platform. As an example, a LAMP setup uses apache as a SAPI, and logs are written to Apache’s error log.

To maximize the logging data available and to give yourself options for centralizing, processing, and analyzing your logs later, add the following configuration to your php.ini. (In PHP .ini files, a semicolon indicates the start of a comment.)

; Log all errors

error_reporting = E_ALL

; Don't display any errors in the browser

display_errors = Off

; Write all logs to this file:

error_log = my_file.log

Now your PHP logs are written to the my_file.log file we specified in the error_log directive above.

PHP’s logging functions

You can log any event you choose by explicitly calling PHP’s error_log() or syslog() function within your code. These functions create logs containing the message string you provide. The syslog() function will use the configuration in your rsyslog.conf file to write log messages. The error_log() function routes it to the file specified by the error_log configuration directive. The following example sends a message to the PHP system logger:

<?php

error_log("An error has occurred.");

The PHP system logger automatically adds a timestamp to each log, so each time this code runs, a line like the one below will be appended to our my_file.log file:

[15-Apr-2019 20:25:11 UTC] An error has occurred.

If no value is provided for the error_log configuration item in php.ini, logs are generated by the SAPI, and their format depends on the SAPI in use. For example, on a LAMP server with Apache’s default logging configuration, the example code shown above adds the following line to Apache’s error log (e.g., /var/log/apache2/error.log):

[Mon Apr 15 20:25:11.950260 2019] [php7:notice] [pid 26154] [client 123.123.123.123:57728] An error has occurred.

PHP’s error_log() and syslog() functions provide more options for configuring where your logs are sent. For example, when you call error_log(), you can provide a path to the file where the message should be logged that is different from the one defined by the error_log directive. For information about the advanced routing capabilities of PHP’s error_log() and syslog() functions, see the PHP documentation. In this article, we will focus on logging to a file, since this gives you the ability to forward and process your logs, as we described above.

Centralizing and storing your logs

So far, we’ve looked at PHP’s system logger and native logging functions. These mechanisms don’t provide much flexibility when you want to customize how your logs are formatted or routed, but they make it easy to get started writing logs to a local file. You can also process your logs with an external service. Consider a strategy that combines writing logs to a local file and forwarding them to an external service to aggregate, analyze, and monitor your logs. This way, you can offload log processing and long-term storage and aggregate logs from all your hosts in a single platform. You can troubleshoot an incident much more efficiently if you don’t have to manually log into each of your servers to view logs.

When you use a log management and analytics platform like Datadog, we recommend using JSON-formatted logs. This makes it easy to process, search, filter, and monitor your logs. To make it easy to create JSON logs and route them to a file, we recommend that you use the Monolog logging library. In the next section we will cover how to use the Monolog library to format your logs as JSON and automatically add metadata to all your logs.

filter-php-logs-by-channel

The Monolog logging library

Monolog is one of the most widely used PHP logging libraries. It provides all the functionality of PHP’s native logging functions, and makes it easy to create PHP logs in different formats. You can easily differentiate logs within a single application by categorizing them in channels, and you can send your logs to databases, message queues, and external collaboration tools.

Monolog is available in the Packagist repository, and the examples in this section assume you’ve installed Monolog using Composer. If you already have Composer installed, all you need to do is issue this command to add Monolog to your project:

composer require monolog/monolog

In this section, we’ll look at some of the Monolog features that you can use to enhance your PHP logging. We’ll show you how to:

Loggers and channels

To start using Monolog, you need to create a logger—an instance of Monolog’s Logger class:

<?php

// Load dependencies required by Composer (including Monolog):

require_once "vendor/autoload.php";

// Use Monolog's `Logger` namespace:

use Monolog\Logger;

\$logger = new Logger('transactions');

This code creates a logger object named \$logger and gives it a channel name of transactions.

Monolog uses channels to differentiate logs that have been routed to the same destination but that contain data about different categories of events. Each time you create a logger, you need to provide a channel name. You can create multiple loggers within your application and use each one to log events related to a category of activity, such as purchases or user accounts. Because each logger’s channel value is associated with the logs it creates (as an object within a JSON-formatted log, for example), channels give you more latitude to use metadata to differentiate your logs.

Handlers

Monolog’s handlers determine how PHP will act on the log messages sent to each logger. The StreamHandler is Monolog’s basic means of writing logs to a file. Numerous other handlers are available so you can easily send logs to the service of your choice.

Once you’ve created a logger, you use it by defining one or more handlers and pushing them onto the Logger object. For each handler you create, you provide information about how it should route the log (e.g., a filename), and a minimum log level at which the handler should be triggered. By pushing multiple handlers onto a logger, you can use it to log different types of events to different destinations.

The following code illustrates pushing a handler on to the logger (\$logger) we created above. It then calls Monolog’s info method to trigger the handler and log a message to the file /var/log/monolog/php.log:

<?php

require_once "vendor/autoload.php";

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

\$logger = new Logger('transactions');

// Declare a new handler and store it in the \$logstream variable

// This handler will be triggered by events of log level INFO and above

\$logstream = new StreamHandler('/var/log/monolog/php.log', Logger::INFO);

// Push the \$logstream handler onto the Logger object

\$logger->pushHandler(\$logstream);

\$logger->info('A notable event has occurred.');

This logger creates logs in Monolog’s default format, but it’s easy to make Monolog structure your logs in a useful format. In the next sections of this post, we’ll look at the benefits you gain when you use the JsonFormatter to create your logs.

Formatters

Monolog allows you to define a custom log format, or you can choose an existing formatter to determine how your log messages appear. Monolog formatters are available to meet different logging requirements, and you can choose the one that best suits your needs.

Monolog’s JSONFormatter helps you structure your log data and lets you include any arbitrary data you require. This can make it easy to store multi-line errors in a single log line. You can also store information unique to each session by logging the PHP session array. JSON-formatted logs are easy for log management solutions to parse, so you can search, filter, and analyze your application’s data to track errors, usage, and performance trends.

The sample code below creates JSON logs with a channel value of transactions.

<?php

require_once "vendor/autoload.php";

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Formatter\JsonFormatter;

\$logger = new Logger('transactions');

\$logstream = new StreamHandler('/var/log/monolog/php.log', Logger::INFO);

// Apply Monolog's built-in JsonFormatter

\$logstream->setFormatter(new JsonFormatter());

\$logger->pushHandler(\$logstream);

\$logger->info('Transaction complete');

When PHP executes this code, a log is added to the specified file—/var/log/monolog/php.log—that looks like this:

{

"message": "Transaction complete",

"context": [],

"level": 200,

"level_name": "INFO",

"channel": "transactions",

"datetime": {

"date": "2019-02-14 17:19:11.332526",

"timezone_type": 3,

"timezone": "UTC"

},

"extra": []

}

To isolate these logs from those created by other loggers in your application, you can use a log management solution to filter your data and view only logs from the transactions channel.

Notice that Monolog automatically adds two arrays to this log—context and extra. You can use these arrays to enrich your logs and provide more information about the activity you’re logging. In the next section, we’ll look at how to create and populate these arrays.

Processors

The context and extra arrays give you options for easily adding metadata to each log. You can use them to store any data that’s useful to you. We recommend using context to log the high-cardinality data that varies between sessions, and extra to log global metadata that’s common to all requests. In this section we’ll illustrate how you can use the two arrays to store different kinds of data.

You can use a Monolog processor to define metadata to be added to each log’s context and extra arrays. Processors make it easy to include the same information consistently across all the logs created by a single logger. The following example defines a Monolog processor to include context and extra data:

<?php

require_once "vendor/autoload.php";

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Formatter\JsonFormatter;

\$logger = new Logger('transactions');

\$logstream = new StreamHandler('/var/log/monolog/php.log', Logger::INFO);

\$logstream->setFormatter(new JsonFormatter());

\$logger->pushHandler(\$logstream);

\$logger->pushProcessor(function (\$record) {

\$record['extra']['env'] = 'staging';

\$record['extra']['version'] = '1.1';

\$record['context'] = array('user' => \$_SESSION["user"], 'customerID' => \$_SESSION["customerID"], 'checkoutValue' => \$_SESSION["checkoutValue"], 'sku_array' => \$_SESSION["sku"]);

return \$record;

});

\$logger->info('Transaction complete');

In the resulting log, both the context and extra arrays are populated.

{

"message": "Transaction complete",

"context": {

"user": "user@example.com",

"customerID": 12102,

"checkoutValue": "17.39",

"sku_array": [468, 116]

},

"level": 200,

"level_name": "INFO",

"channel": "transactions",

"datetime": {

"date": "2019-04-16 15:46:16.531986",

"timezone_type": 3,

"timezone": "UTC"

},

"extra": {

"env": "staging",

"version": "1.1"

}

}

You can also pass context array data as an argument to the method you use to create the log. The example below illustrates passing the log message and context data in a single call:

\$logger->info('Transaction complete', array('user' => \$_SESSION["user"], 'customerID' => \$_SESSION["customerID"], 'checkoutValue' => \$_SESSION["checkoutValue"], 'sku_array' => \$_SESSION["sku"]));

Log levels

PHP’s error_log() function assumes all messages describe errors within your application, but Monolog allows you to log other types of PHP events as well. Monolog supports eight different log levels—the same ones defined in the syslog protocol—so that each log carries metadata that conveys the severity of the event being logged.

When you call the Monolog function to create a log, you specify the log’s level. This way, you can log the types of events (e.g., debug, error, or alert) you need to know about. For example, to log an event whose log level is error, you would call the logger’s error method as shown below:

\$logger->error('Transaction failed');

Of course, you don’t want to have to revise your code during an outage to log debug messages. Instead you can configure your application to log events of all levels, and use a log management solution to filter logs downstream to isolate certain kinds of events.

Expanding your logging coverage

Because PHP logging is flexible, you have options in how much to log and how to handle your logs. In this section, we’ll look at how PHP exceptions work and how to capture them. We’ll also show you how to expand your logging to capture useful information about different types of events—not just errors.

Catch and log exceptions

Like many other languages, PHP uses exceptions to accommodate unintended behavior by your application. An exception is an object PHP creates (or throws) when the execution of your PHP script reaches an unintended state.

Exceptions should be caught when they occur—governed by code that addresses the exceptional case. The exception handler—the code that catches the exception—defines PHP’s behavior and output when faced with an exception. PHP does not automatically log exceptions when they are thrown, so you should create exception handlers that log useful information about the exception.

The code below shows an example of a basic exception handling strategy. The checkUsername function validates the length of the string passed to it, then throws exceptions under certain conditions. The function is called from within a try block, and a catch block handles any exceptions and logs the details.

<?php

require_once "vendor/autoload.php";

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Formatter\JsonFormatter;

\$logger = new Logger('signups');

\$logstream = new StreamHandler('/var/log/monolog/php.log', Logger::INFO);

\$logstream->setFormatter(new JsonFormatter());

\$logger->pushHandler(\$logstream);

function checkUsername(\$username) {

if (strlen(\$username) < 4) {

throw new Exception("Username \$username is not long enough.");

} else if (strlen(\$username) > 12) {

throw new Exception("Username \$username is too long.");

}

// \$username is OK

}

try {

checkUsername('me');

} catch (exception \$e) {

\$message_string = "{\$e->getMessage()} (file: {\$e->getFile()}, line: {\$e->getLine()})";

\$logger->error(\$message_string);

}

When PHP throws an exception, it creates an exception object (named \$e in the example above) that is available for the exception handler to use. The exception object contains properties, such as the file and lines of code that have caused the unintended state, that describe the state of the application. It also provides methods you can use to access those properties (such as getMessage() in the example above). You can use an exception handler to access the data contained in the exception object and log details of the exception.

The code above will append a line like this one to the file /var/log/monolog/php.log:

{

"message": "Username me is not long enough. (file: /var/www/html/checkUsername.php, line: 17)",

"context": [],

"level": 400,

"level_name": "ERROR",

"channel": "signups",

"datetime": {

"date": "2019-04-11 20:33:45.500634",

"timezone_type": 3,

"timezone": "UTC"

},

"extra": []

}

Better than logging only the message returned by the exception’s getMessage() method, you should log the exception object itself. The PHP logging standard that Monolog implements, PSR-3, states that a logged exception must be in the exception element of the context array. To log the whole exception object, change the \$logger->error() call in the previous example to look like this instead:

\$logger->error("checkUsername failed", array('exception' => \$e));

When you log the exception object, all the information it contains is recorded in the log as JSON, as shown in this example log:

{

"message": "checkUsername failed",

"context": {

"exception": {

"class": "Exception",

"message": "Username me is not long enough.",

"code": 0,

"file": "/var/www/html/checkUsername.php:16"

}

},

"level": 400,

"level_name": "ERROR",

"channel": "signups",

"datetime": {

"date": "2019-04-24 15:01:17.656613",

"timezone_type": 3,

"timezone": "UTC"

},

"extra": []

}

If you use a log management service, you can use the exception object’s data to view, filter, and analyze your logs.

Datadog's Log Explorer shows a parsed PHP exception.

Catch unhandled exceptions

If your code doesn’t include a handler for a particular exception, PHP will generate a fatal error and halt execution. To prevent this, you can use PHP’s set_exception_handler() function to define your own default exception handler. This way you can avoid the fatal error caused by an unhandled exception, and you can capture the exception in your logs. The example below uses set_exception_handler() to catch and log any unhandled exceptions.

<?php

require_once "vendor/autoload.php";

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Formatter\JsonFormatter;

\$logger = new Logger('signups');

\$logstream = new StreamHandler('/var/log/monolog/php.log', Logger::INFO);

\$logstream->setFormatter(new JsonFormatter());

\$logger->pushHandler(\$logstream);

// Define default behavior if an exception isn't caught:

set_exception_handler( function(\$e) {

\$uncaught_log = new Logger('uncaught');

\$uncaught_logstream = new StreamHandler('/var/log/monolog/php.log', Logger::ERROR);

\$uncaught_logstream->setFormatter(new JsonFormatter());

\$uncaught_log->pushHandler(\$uncaught_logstream);

\$uncaught_log->error("Uncaught exception", array('exception' => \$e));

});

// Declare an empty class

class myClass {

// empty

}

// Try to call a non-existent function

try {

myClass::myFunction();

} catch (Exception \$e) {

\$logger->error("Call to myFunction failed", array('exception' => \$e));

}

In this code, set_exception_handler() processes the exception thrown when the nonexistent myFunction() is called. It serves as the default exception handler, and will process any uncaught exceptions throughout the script (any of which would otherwise have caused a PHP fatal error).

When this code is executed, it logs an exception like the one below:

{

"message": "Uncaught exception",

"context": {

"exception": {

"class": "Error",

"message": "Call to undefined method myClass::myFunction()",

"Code": 0,

"file": "/var/www/html/test_exception_handler.php:30"

}

},

"level": 400,

"level_name": "ERROR",

"channel": "uncaught",

"datetime": {

"date": "2019-04-11 19:20:20.241717",

"timezone_type": 3,

"timezone": "UTC"

},

"extra": []

}

Note that it does not log the Call to myFunction failed error from the catch block. The code in the catch block would execute if myFunction() threw an exception, but in this case PHP throws an exception when we try to call the nonexistent myFunction(). Since that exception is uncaught, it gets processed by the function defined in set_exception_handler().

Log events (not just errors)

In addition to the errors the PHP system logger records automatically, you can log custom events such as API calls to and from your application. Logging these events allows you to monitor your application’s performance and usage trends. In an application made up of microservices, pretty much everything will be an API call, and you can add custom logging code around any calls worthy of attention. The example below calculates the response time of an API call, then uses Monolog to log the result.

<?php

require_once "vendor/autoload.php";

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Formatter\JsonFormatter;

\$logger = new Logger('APIperformance');

\$logstream = new StreamHandler('/var/log/monolog/php.log', Logger::INFO);

\$logstream->setFormatter(new JsonFormatter());

\$logger->pushHandler(\$logstream);

function myAPIcall() {

\$curl = curl_init();

\$url = 'http://dummy.restapiexample.com/api/v1/employees';

curl_setopt(\$curl, CURLOPT_URL, \$url);

curl_setopt(\$curl, CURLOPT_RETURNTRANSFER, 1);

\$result = curl_exec(\$curl);

curl_close(\$curl);

return \$result;

}

\$logger->pushProcessor(function (\$record) {

\$record['extra']['env'] = 'staging';

\$record['extra']['version'] = '1.1';

return \$record;

});

\$start = microtime(TRUE); // A timestamp before the call

\$result = myAPIcall();

\$end = microtime(TRUE); // Another timestamp after the call

// Log the call duration as a readable string

// and include the context array

\$logger->info("myAPIcall took " . (\$end - \$start) . " seconds.", array('duration' => (\$end - \$start)), array('user' => \$_SESSION["user"], 'customerID' => \$_SESSION["customerID"], 'checkoutValue' => \$_SESSION["checkoutValue"], 'sku_array' => \$_SESSION["sku"]));

Each time this function runs, your logs collect data on the performance of the API call, which you can visualize in a service like Datadog:

Datadog's Log Explorer graphs duration of API calls from a PHP application.

In addition to logging API calls, you can expand your logging coverage to capture logins and logouts, as well as other user activity such as signups and transactions.

With all your logs aggregated in one place, Datadog’s Log Analytics makes it easy for you to visualize log data. For example, you can see your aggregated log volume, grouped by channel to understand the amount of activity across the different areas of your application.

A Datadog Log Analytics graph shows the volume of logs created in the signups, transactions, and APIperformance channels.

From this view, you can export the graph to a dashboard or click to see individual logs. You can even create a monitor to alert on your log data, so you can automatically be notified of any unusual activity captured in your application logs.

For further information about using Monolog and Datadog, see our documentation.

Do more with your PHP logs

PHP logging offers a lot of flexibility that enables you to capture the right information and make it available for troubleshooting and monitoring. Once you’ve configured your applications to log all the information that might be useful to you, you can send your logs to a monitoring platform for in-depth analysis and collaborative troubleshooting. If you’re not already using Datadog to collect and analyze your logs, you can start with a free, full-featured 14-day trial.