Skip to main content

PHP

Overview​

The PHP OpenTelemetry SDK exports OTLP/HTTP logs natively. The recommended path is Monolog — the de-facto PHP logging library — bridged via the open-telemetry/opentelemetry-logger-monolog handler.

TopologyWhen to use it
OTel SDK direct (this page)The default. Up to a few hundred events / second / process. Simplest setup — no extra hop, no second process.
OTel SDK → local collectorMany services on a node, multi-backend fan-out, central config / secret management, queue-on-outage durability, sampling or redaction. See the OTel SDKs in production guide.
Log to file + agentLanguages without a stable OTel logs SDK, very high throughput, air-gapped environments, or container runtimes that already capture stdout. See the operating-systems page.

Prerequisites​

You need the following before you start:

  • Data region — United States (us), Canada (ca), Europe (eu), United Kingdom (uk), or Australia (au). The app dashboard displays the region of your workspace.
  • Ingest Key ID — short identifier for the Ingest Key that will send these logs.
  • Ingest Key access token — bearer credential for the Ingest Key.

View or create an Ingest Key in the SparkLogs app under Configure → Ingest Keys. Each Ingest Key has its own ID and access token; revoke or rotate either at any time without restarting your application.

Install​

composer require open-telemetry/sdk:^1.0 \
open-telemetry/exporter-otlp:^1.0 \
open-telemetry/sem-conv:^1.0 \
open-telemetry/opentelemetry-logger-monolog:^1.0 \
monolog/monolog:^3.0 \
php-http/guzzle7-adapter:^1.0 \
guzzlehttp/psr7:^2.5 \
http-interop/http-factory-guzzle:^1.2

The runnable example pins additional PSR HTTP packages for standalone composer install; see its composer.json if resolution fails on your machine.

Configure the OTel exporter​

Most OTel SDKs read exporter configuration from environment variables. Set these before your app starts:

export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT="https://ingest-us.engine.sparklogs.app/v1/logs"
export OTEL_EXPORTER_OTLP_LOGS_HEADERS="Authorization=Bearer <INGEST-KEY-ID>:<INGEST-KEY-ACCESS-TOKEN>"
export OTEL_EXPORTER_OTLP_LOGS_COMPRESSION=gzip
export OTEL_EXPORTER_OTLP_LOGS_TIMEOUT=25000
tip

Set <INGEST-KEY-ID> and <INGEST-KEY-ACCESS-TOKEN> from Configure → Ingest Keys. Choose the region based on the location of your provisioned workspace.

Other OTLP receivers. The variables above are the standard OpenTelemetry logs exporter settings. Point OTEL_EXPORTER_OTLP_LOGS_ENDPOINT and OTEL_EXPORTER_OTLP_LOGS_HEADERS at SparkLogs as shown, at a local OpenTelemetry Collector (for example http://localhost:4318/v1/logs), or at any OTLP/HTTP-compatible receiver. Swap only the URL and auth headers your target expects; keep OTEL_EXPORTER_OTLP_LOGS_PROTOCOL aligned with what that endpoint accepts.

Why set OTEL_EXPORTER_OTLP_LOGS_TIMEOUT? The OTel default is 10s, and on rare occasion our cloud may delay a request up to 12 seconds (p99.99 latency). 25s leaves headroom for rare request latency and network delays.

Compression. gzip is recommended and what most users should use. CPU-constrained workloads can set OTEL_EXPORTER_OTLP_LOGS_COMPRESSION=none to send uncompressed — SparkLogs does not bill for inbound bytes, so the trade-off is purely network-vs-CPU on your side. See the scaling guide for the full list and important SDK-vs-wire-protocol differences.

Batching. The OTel SDK's BatchLogRecordProcessor defaults (max queue 2048, max batch 512, 1s schedule delay, 30s export timeout) are production-appropriate for most workloads. Higher-throughput pipelines may want to tune them — see the scaling guide.

Set up the OTel SDK​

<?php
use OpenTelemetry\API\Common\Time\Clock;
use OpenTelemetry\Contrib\Otlp\LogsExporterFactory;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Logs\LoggerProvider;
use OpenTelemetry\SDK\Logs\Processor\BatchLogRecordProcessor;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SemConv\ResourceAttributes;

$resource = ResourceInfo::create(Attributes::create([
ResourceAttributes::SERVICE_NAME => 'my-service',
ResourceAttributes::SERVICE_VERSION => '1.0.0',
ResourceAttributes::DEPLOYMENT_ENVIRONMENT_NAME => 'production',
]));

$exporter = (new LogsExporterFactory())->create();
$loggerProvider = LoggerProvider::builder()
->setResource($resource)
->addLogRecordProcessor(new BatchLogRecordProcessor($exporter, Clock::getDefault()))
->build();

BatchLogRecordProcessor is the production-correct choice. Don't use the simple processor outside tests.

Set resource attributes​

SparkLogs derives the searchable source, service, and app pivot fields from your OpenTelemetry Resource attributes. Setting these correctly means your events arrive grouped, filterable, and indexed without further configuration:

  • service.name — the logical service identity (e.g. checkout, auth-api). Maps to the service field.
  • service.version — the version / build of the running service (e.g. 1.42.0, abc123def).
  • deployment.environment — the environment label (e.g. production, staging, development). Maps to the app field.

Most OTel SDKs accept these via the OTEL_RESOURCE_ATTRIBUTES environment variable as a comma-separated list:

export OTEL_RESOURCE_ATTRIBUTES="service.name=my-service,service.version=1.0.0,deployment.environment=production"

The SDK setup snippets below show the in-code equivalent for each language.

For the full mapping (including container, host, and Kubernetes attributes that derive source), see OTLP/HTTP API → Resource attributes.

Integrate the OTel SDK with Monolog​

use Monolog\Handler\StreamHandler;
use Monolog\Level;
use Monolog\Logger;
use OpenTelemetry\Contrib\Logs\Monolog\Handler;

$handler = new Handler(
$loggerProvider,
Level::Info,
true,
);

$log = new Logger('my-app');
$log->pushHandler($handler);
$log->pushHandler(new StreamHandler('php://stdout', Level::Info));

$log->info('hello, SparkLogs');
$log->warning('disk usage at 92%', ['mount' => '/dev/sda1', 'pct' => 92]);

try {
throw new RuntimeException('connection refused');
} catch (Throwable $e) {
$log->error('connection refused', ['exception' => $e]);
}

The Monolog context array (['mount' => …]) flows through to SparkLogs as searchable structured fields.

Flush on shutdown​

$loggerProvider->shutdown();

For long-running PHP-FPM workers and CLI scripts, wire shutdown to register_shutdown_function:

register_shutdown_function(static function () use ($loggerProvider) {
$loggerProvider->shutdown();
});

See graceful shutdown.

Runnable examples​

Tested examples (no cloud credentials)

The public sparklogs-ingest-examples repo includes matching projects for this page. In each project directory, run make mock-test to send OTLP batches to a local mock receiver (no SparkLogs agent token required). Use make test with agent credentials when you want to verify against a real workspace.

Frequently asked questions​

Where to next​

  • Production deployments — batching, queue tuning, backpressure, when to add a collector, graceful shutdown: see OTel SDKs in production.
  • OTLP/HTTP transport details — full encoding / compression / auth / retry status code reference: see the OTLP/HTTP API page.
  • Add an OTel Collector — when one of your services on a node should aggregate, sample, redact, or fan out to multiple backends: see the OpenTelemetry Collector guide.
  • Log to a file + agent — for very high throughput, languages without a stable OTel logs SDK, or container runtimes that already capture stdout: see operating-system agents.