Instrumenting PHP Apps with OpenTelemetry Metrics
This article provides a detailed guide on integrating OpenTelemetry metrics into your Laravel application.
It explores key concepts, including instrumenting your application with various metric types, monitoring HTTP request activity, and exporting metrics to visualization tools.
Let's get started!
Prerequisites
- Prior experience with PHP and Laravel, along with a recent version of PHP installed.
- Familiarity with Docker and Docker Compose.
- Basic understanding of how OpenTelemetry works.
Step 1 — Setting up the demo project
To demonstrate OpenTelemetry instrumentation in Laravel applications, let's set up a simple "Hello World" Laravel application along with the Prometheus server for visualizing metrics.
First, create a new Laravel project:
Here's the Laravel route configuration you'll be instrumenting:
This app exposes two endpoints: / returns a simple "Hello world!" message, and
/metrics endpoint that will eventually expose the instrumented metrics.
Create a compose.yaml file in your project root to set up both our application
and Prometheus server in Docker:
The app service is the Laravel application running on port 8000, while
collector configures an OpenTelemetry Collector instance to receive the
metrics from the Laravel application.
Create a Dockerfile for the Laravel application:
Create a otelcol.yaml configuration file:
Before starting the services, make sure your .env file contains the
application's PORT setting:
Launch both services in detached mode with:
You should see output indicating both containers have started successfully.
To confirm that the Laravel application is running, send a request to the root endpoint:
This should return:
To verify that Prometheus can access the exposed /metrics endpoint, visit
http://localhost:9090/targets in your browser.
With everything up and running, you're ready to integrate OpenTelemetry in your Laravel application in the next step.
Step 2 — Installing the OpenTelemetry SDK
Before instrumenting your Laravel application with OpenTelemetry, you need to install the OpenTelemetry SDK for PHP.
Install the OpenTelemetry packages via Composer:
Now, let's create a new service provider to handle OpenTelemetry configuration. In Laravel, service providers are the central place to configure your application's services:
Then edit app/Providers/OpenTelemetryServiceProvider.php:
This provider:
- Creates a singleton instance of
MeterProviderInterface, which is the central registry for all OpenTelemetry metrics in your application - Sets up a resource that identifies your application with metadata
- Configures a Prometheus exporter for metrics
- Builds a meter provider that will collect metrics and make them available to Prometheus
Register the provider in config/app.php:
Now, let's create a controller to handle the metrics endpoint. Run:
Update the controller at app/Http/Controllers/MetricsController.php:
Update your metrics endpoint in routes/web.php:
After implementing these changes, the /metrics endpoint will expose metrics in
a Prometheus-compatible format. Visit http://localhost:8000/metrics in your
browser or use curl to see the response:
Currently, the response will include basic information but no custom metrics since we haven't registered any yet. In the following sections, we'll instrument the application with different metric types.
Step 3 — Implementing a Counter Metric
Let's start with a Counter that tracks the total number of HTTP requests made to your Laravel application. A Counter is a cumulative metric that can only increase or reset to zero and is ideal for tracking total events or operations.
Create a middleware to handle the metrics collection:
Then edit app/Http/Middleware/OpenTelemetryMiddleware.php:
This implementation:
- Creates a meter from our meter provider with a namespace of 'laravel-app'
- Creates a Counter metric named
http.requests.totalwith a description and unit - Increments the counter after each request, adding attributes for status code, path, and HTTP method
Register the middleware in app/Http/Kernel.php:
If you refresh http://localhost:8000/metrics several times, you'll see output
like:
You can view your metrics in the Prometheus UI by heading to
http://localhost:9090. Type http_requests_total into the query box and click
Execute to see the raw values and visualize the counter increasing over time.
Step 4 — Implementing a Gauge Metric
A Gauge is a metric that represents a single numerical value that can arbitrarily go up and down. Gauges are perfect for measuring values like current memory usage, active connections, or queue sizes.
Let's update our middleware to track active requests with a gauge:
In OpenTelemetry, gauges are typically implemented using an UpDownCounter, which allows both incrementing and decrementing the value. Our implementation:
- Creates an UpDownCounter metric named
http.requests.active - Increments it at the start of request processing
- Decrements it when the request completes
To observe the gauge in action, let's add some artificial delay to the root
route. Update your routes/web.php:
Now use a load testing tool like wrk or simply open
multiple browser tabs to generate concurrent requests. Visiting the /metrics
endpoint will show something like:
This indicates that there are currently 8 active requests being processed by your Laravel application.
Tracking absolute values
Gauges are also perfect for tracking absolute values like memory usage. Let's add memory metrics to our middleware:
This will produce metrics showing your application's memory usage over time, which can be visualized in Prometheus's Graph view.
Step 5 — Implementing a Histogram Metric
Histograms are useful for tracking the distribution of measurements, such as request durations. They observe values and count them in configurable buckets, allowing you to understand the distribution of values.
Let's update our middleware to track HTTP request durations with a histogram:
This implementation:
- Creates a Histogram metric named
http.request.duration - Measures the duration of each request using microtime()
- Records the duration in the histogram with the same attributes we use for the counter
After generating some traffic to your application, the /metrics endpoint will
show histogram data like this:
Let's understand this output:
- Each
_bucketline shows how many requests completed within a specific time threshold - For example,
le="1"} 8means 8 requests completed within 1 second - The
_sumvalue is the total of all observed durations - The
_countvalue is the total number of observations
This data allows you to analyze the distribution of request durations. In Prometheus, you can calculate the 95th percentile latency using:
This shows the response time that 95% of requests fall under, which is more useful than averages for understanding real user experience.
Step 6 — Monitoring External API Calls
Let's create a service to monitor external API calls using OpenTelemetry:
Edit app/Services/ExternalApiService.php:
Add a new route to test the external API service:
This service:
- Creates a Histogram to track external API call durations
- Measures the duration of each API call
- Records the duration with metadata about which API and endpoint was called
- Uses a try-finally block to ensure timing is recorded even if errors occur
Final thoughts
In this tutorial, we integrated OpenTelemetry metrics into a Laravel application. We implemented counters for tracking requests, gauges for monitoring active connections and memory usage, and histograms for analyzing response time distributions.
Thanks for reading!