Download the PHP package cofa/performance-tracking-times without Composer

On this page you can find all versions of the php package cofa/performance-tracking-times. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package performance-tracking-times

Performance Tracking Times

A Laravel package that breaks every request's response time into its parts and shows each one as a waterfall diagram:

Segment What it is Measured by
Calculating Time the client spends preparing the request (building the payload, serialising, signing) Client
Network out The request travelling to the server, including connection setup (browser queue, DNS, TCP, TLS) Client + server
Delay Time the request waits in the web server or PHP-FPM queue before a worker picks it up Web server + PHP
Service Time the server works on the request: framework boot, middleware, controller, and the database queries inside it Server
Network return The response travelling back and downloading Client + server
Response time The sum of the segments above: from the client starting the work to the last byte arriving. Browser rendering afterwards is not included.

The dashboard at /performance-tracking lists recent requests with a stacked bar for each one, plus server-side and end-to-end percentiles. Open a request to see its waterfall, a table of the values, and the SQL of its queries placed on the timeline.

Requirements

Laravel PHP
10 8.1 – 8.4
11, 12 8.2 – 8.4
13 8.3 – 8.4

Tested on SQLite, MySQL 5.7 and 8.0, and PostgreSQL 16.

Installation

The service provider is auto-discovered. It prepends the recording middleware to the global middleware stack, so it wraps every other middleware and route.

Recording is on in the local environment only. To record elsewhere, turn it on and sample:

Every recorded request is one row (plus up to 50 query texts), so keep the sample rate low on busy servers and keep the timings in their own database if you like (PERFORMANCE_TRACKING_DB_CONNECTION).

To publish the config:

1. Server side: delay and service time

This part works as soon as recording is on. Service time (framework boot plus middleware and controller) and database time are measured for every recorded request.

To measure delay (queue time), the web server has to stamp the time it received the request, from a source the client cannot forge. With nginx in front of PHP-FPM, use a FastCGI param (clients can only set HTTP_* params):

If nginx is a reverse proxy instead (Octane, another host), use the header:

Because any client can send X-Request-Start itself, the header is honoured only on requests that came through a proxy your application trusts (TrustProxies). Set request_start_header_trust to always (PERFORMANCE_TRACKING_TRUST_HEADER=always) if you cannot configure trusted proxies and your web server always overwrites the header.

With Apache or LiteSpeed (cPanel and most shared hosting), add this to public_html/.htaccess and set PERFORMANCE_TRACKING_TRUST_HEADER=always, since the web server itself replaces any value a visitor sends:

Stamps more than max_delay_ms (60 s) old are ignored. Without a stamp the dashboard shows delay as "not measured", and any queueing time is counted in network out instead.

Every recorded response carries a header the client script reads:

n identifies the server (a hash of the hostname, or PERFORMANCE_TRACKING_NODE), so clients keep a separate clock offset for each server behind a load balancer.

In local, responses also carry Server-Timing, which browser devtools show in the Network tab:

Elsewhere it is off, because it tells anyone how long each request took on the server and how many queries it ran, which makes timing attacks (user enumeration on a login form, for example) far easier. Turn it on with PERFORMANCE_TRACKING_SERVER_TIMING=true only where that is acceptable.

2. Client side: calculating and network time

The server cannot measure the network legs on its own. Only the client knows when the request left and when the response finished arriving, so the client reports those timestamps back.

Browser (Blade, Inertia, Livewire, any JS frontend)

Add the script to your layout's <head>, before any other script:

It wraps fetch and XMLHttpRequest (so axios and jQuery work too), reads precise timestamps from the Resource Timing API, and sends them in batches with navigator.sendBeacon. It does not change your requests or their responses. The tag is a small synchronous script (so it is in place before your own scripts send requests), versioned so browsers cache it until the package changes.

On a server-rendered page, the tag also carries that page's timing id, so the page load itself gets network times (from the Navigation Timing API), not only the AJAX calls made after it. The first page of a visit splits network time evenly; once the browser has made an AJAX call to the same server it knows the clock offset and splits it precisely.

Pages and responses served from a cache (a CDN, a full-page cache, Cache-Control: public) carry the timing id of the request that produced them. The server rejects reports whose send time is far from that request when it can tell, but send tracked pages with Cache-Control: private if they could be cached.

For a frontend on another origin (React, Vue, Next.js, …) or a mobile app, see section 3.

To also measure calculating time, wrap the work that comes before a request:

When the browser provides no Resource Timing entry for a fetch (some cross-origin setups), the script falls back to the moment the response headers arrived; such requests are marked and their network return does not include downloading the body.

3. API with a separate frontend or mobile app

When Laravel serves only an API and the frontend lives elsewhere (a React/Vue/Angular/Next.js app on another domain, or a Flutter/React Native/Swift/Kotlin app), three people each do one part. A printable version of this section for your developers is in docs/frontend-setup.pdf.

On the API (API owner, once)

  1. Name the frontend's address in the API's .env, so browsers there may read the timing header. Separate several frontends with commas. Mobile apps need nothing here.

  2. Reload the configuration with php artisan config:clear.
  3. The frontend must already be allowed by the API's CORS settings (config/cors.php, allowed_origins). If it can call the API today, it is.

Web frontend (web developer)

Add one tag to the HTML template's <head>, before the app's own scripts. It wraps fetch and XMLHttpRequest, so axios, ky, jQuery and plain fetch are all covered, and the app's API calls stay as they are.

With Next.js, add it to the root layout with next/script:

Page loads of a separate frontend are not reported, only its API calls: the page itself does not come from Laravel, so it has no timing id.

Mobile app (mobile developer)

There is no script to load, so the app reports the timestamps itself with a small wrapper around its HTTP client:

  1. Before the call, note the time in epoch milliseconds (t0).
  2. After the response, note the time again (t3) and read the X-Request-Timing header: id=…;t1=…;t2=…;n=…. Not every response has it (the server samples), so skip those without.
  3. Per server n, keep the clock offset from the fastest exchange: ((t1 - t0) + (t2 - t3)) / 2, where fastest means the smallest (t3 - t0) - (t2 - t1). Refresh it every 10 minutes.
  4. Every few seconds (or at 20 entries), and when the app goes to the background, POST up to 50 entries as JSON to /{path}/api/client (/performance-tracking/api/client by default).

Ready-made wrappers: React Native / JavaScript without a page. Swift and Kotlin follow the same four steps.

The report format:

Check that it works

  1. The header is there. In the browser's devtools (Network tab), an API response has an X-Request-Timing header, and for a web frontend also Access-Control-Expose-Headers: X-Request-Timing. Only sampled requests carry it, so check a few. From a terminal:

  2. Reports are sent. About two seconds after a call, a request to /performance-tracking/api/client answers with accepted above 0.
  3. The dashboard fills in. Rows without a * have client timings.

Don't cache API responses publicly (Cache-Control: public, a CDN cache): a cached response carries another request's timing id, and its reports are rejected.

How the network split works

Dashboard

The dashboard updates itself while it is open: every 5 seconds (PERFORMANCE_TRACKING_REFRESH_SECONDS, 0 turns it off) it fetches the latest requests, new rows appear at the top, and a row is highlighted again when its client report arrives. It pauses while the tab is in the background and has a Pause button. Older pages (?page=2 and on) stay still, so rows do not shift while you read them. A request's page refreshes until its client report arrives, then stops.

The top tiles show the median and the 95th percentile (p95) of the response time (end to end, from client reports) and of the server time, with how many times slower the p95 is than the median. A typical request is the median; the p95 is the slowest 5%. When the two are close, performance is consistent; when the p95 is many times the median, some requests are much slower than the rest and worth investigating:

p95 ÷ median Shown as
below 3× green, "steady"
3× to 5× amber, "some slow requests"
above 5× red, "long tail"

Change the multiples with p95_ratio.warning and p95_ratio.critical in the config.

It polls instead of keeping a connection open, so it works on any hosting, shared hosting included, without a WebSocket server.

Access

Its own login page (recommended for APIs and apps without an admin login). Set a username and password in .env, and the dashboard asks for them on a sign-in page of its own, in every environment, independent of your application's users:

Generate the password line with the command below. It asks for the password twice and prints the hashed line to paste; the single quotes keep the $ signs intact. A plain-text password works too, but keep it out of version control.

Then run php artisan config:clear. Visiting /performance-tracking now redirects to /performance-tracking/login, and a Sign out button appears in the dashboard's header. Sign-in attempts are limited to 5 per minute per IP address, each sign-in starts a new session, and changing the username or password signs everyone out.

Your application's login. Alternatively, or as well, let signed-in users of your application in through a gate:

Without a username and password, the dashboard is open in the local environment, and everywhere else only the gate lets people in.

Stored data and privacy

Each row holds the method, the path, the route name, the status, the timings and, unless disabled, the authenticated user's id and the query SQL.

Configuration highlights

Key Default
enabled true in local only PERFORMANCE_TRACKING_ENABLED
sample_rate 1.0 Record this fraction of requests; lower it on busy servers
path performance-tracking Dashboard URL prefix
dashboard_auth.username / dashboard_auth.password null PERFORMANCE_TRACKING_USERNAME / PERFORMANCE_TRACKING_PASSWORD: the dashboard's own login page
p95_ratio.warning / p95_ratio.critical 3 / 5 When the p95 ÷ median badge turns amber and red
dashboard_refresh_seconds 5 PERFORMANCE_TRACKING_REFRESH_SECONDS; 0 turns live updates off
expose_server_timing true in local only PERFORMANCE_TRACKING_SERVER_TIMING
request_start_server_param / request_start_header REQUEST_START_TIME / X-Request-Start Where the web server's receive stamp comes from
request_start_header_trust proxies PERFORMANCE_TRACKING_TRUST_HEADER: proxies, always or never
ignore_paths / ignore_methods telescope, horizon, … / OPTIONS, HEAD
path_mode / store_ip / store_user_id route / false / true See above
record_queries / query_limit true / 50
timing_allow_origin null Set it when the frontend runs on another origin, so the browser exposes the header
client_rate_limit 120 per IP, 10 000 total, per minute
connection / table default / performance_timings Keep timings in a separate database if you like
retention_hours / prune_schedule 48 / 17 * * * *

Pruning

The prune command is scheduled hourly (prune_schedule), so old rows are deleted as long as your application runs php artisan schedule:run. It deletes in batches of 5 000 rows to keep each statement short. Set prune_schedule to null to schedule it yourself:

Notes

Upgrading

From 1.x:

Testing

License

MIT


All versions of performance-tracking-times with dependencies

PHP Build Version
Package Version
Requires php Version ^8.1
laravel/framework Version ^10.0|^11.0|^12.0|^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package cofa/performance-tracking-times contains the following files

Loading the files please wait ...