Download the PHP package partner-api/logger without Composer

On this page you can find all versions of the php package partner-api/logger. 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 logger

Partner API Logger SDK for PHP

Sends structured logs and metrics to the Partner API ingest service, or writes the logs to stdout for your own log pipeline to deliver (Pipeline delivery). It mirrors @partner-api/logger 3.3.0; the cross-language contract is packages/logger-spec/spec.md. Changes by version are in CHANGELOG.md.

Requirements

Install

Quick start

The tenant token is one secret for your whole tenant, set once when the Logger is constructed. $apiKey, the first argument of every log and metric call, belongs to the partner app: each app sends its own key in the x-api-key header of its requests to your API. Pass that value through; it is what tells one partner's traffic from another's.

Laravel

The package auto-discovers its service provider and the PartnerLogger facade. Publish the config file and add your token to .env:

Then log through the facade:

Type-hint PartnerApi\Logger\Logger to inject the same singleton. A missing key is not an exception: the entry is dropped and reported to onError.

Standalone

Log calls buffer and return at once; the buffer is delivered after your response has been sent (Delivery and errors).

Logging and context

setContext() merges fields into every later entry:

Recognised keys are partnerId, requestId, correlationId, path, method, statusCode and duration, plus direction ('inbound' or 'outbound'), upstreamIntegration and upstreamBaseUrl for calls your own code makes to a service it uses (an upstream integration), and to ('partner' or 'service') for an outbound call that went to the partner itself; see Logging webhooks. An outbound line is never shown to a partner unless it carries to: 'partner'. from is reserved. Context is copied into an entry when it is buffered. Outside a request scope it is logger-wide, which is right for PHP-FPM and the CLI.

Request scopes

When one logger serves many requests or jobs from one process (an Octane worker with the logger warmed, RoadRunner, Swoole, a queue worker), logger-wide context and upstream calls carry from one into the next. Give each request or job its own scope, opened once at its edge. PHP-FPM and the CLI need none. For draining on these servers, see Delivery and errors.

Request and response logging

Logging webhooks

Incoming webhooks, a partner calling a webhook endpoint of yours, are ordinary inbound traffic: log them with logRequest() / logResponse() like any other partner request.

What differs is how you know the partner. A webhook is usually authenticated by a signature rather than by the partner's app key. Once your handler has verified the signature and knows which partner sent it, pass that partner's app key to the log calls. Do not leave a webhook out of your logs because it carries no key, and do not log it under another partner's key: a partner sees only the lines attributed to it.

Never log the signing secret, and replace the signature header's value yourself, as above: the SDK redacts only authorization, cookie, set-cookie, x-api-key, x-tenant-token, proxy-authorization and, in stdout mode, a header whose value equals the app key, so a header such as x-signature or stripe-signature is logged as given.

Outgoing webhooks, your code delivering an event to a partner's endpoint, are outbound lines marked 'to' => 'partner' (requires partner-api/logger 2.3.0 or later). That marker is what lets the partner see them: in its portal's Events, its assistant and over MCP. An outbound line without it is a call to a service you use, and no partner ever sees it.

Log one line per attempt, under the key of the partner you delivered to, with three conventional fields so retries group under one delivery: event_type, delivery_id (the same on every attempt) and attempt (1 for the first try).

Upstream call trail

Record the calls you make while serving a request, and logResponse() ships them as upstream: [...] on its Outgoing response line, then clears them.

The response line then carries the calls in the order they were made:

upstream($call) takes name (any label), method, url and durationMs, and optionally status, requestId, errorCode, message and attempt. Omit status for a network error. status must be an int (or a whole float): '200' is not a status. A call ingest would reject (no name, method or url, a method other than letters, - and _ up to 16 characters, a status outside 100 to 599, a durationMs that is not a finite non-negative number) is dropped and reported to onError. It never throws.

upstreamMiddleware($name, $options = []) returns a Guzzle middleware that records every request through it: method, URL, status, duration, and the vendor's request id from the first header present in $options['requestIdHeaders'] and then UpstreamTrail::DEFAULT_REQUEST_ID_HEADERS (request-id, x-request-id, x-amzn-requestid, x-amz-request-id, x-ms-request-id, x-github-request-id, cf-ray). Any other option throws LoggerException when the middleware is made.

Where to push it. A HandlerStack nests in push order, first pushed outermost. Pushed onto HandlerStack::create(), it sits inside the redirect middleware, so each redirect hop is its own call; unshift() it for one call per request. Push Guzzle's retry middleware before it to record each attempt as its own call, with attempt taken from the retry counter (0 for the first attempt). Anywhere else attempt is omitted.

Caps. At most 20 calls and 8 KB of trail per line (UpstreamTrail::MAX_CALLS, MAX_BYTES); past either, the oldest calls are dropped and the line gets _upstreamTruncated: true and _upstreamDropped: n. name, url, errorCode and message are cut to 128, 2048, 128 and 1024 UTF-16 code units, and a requestId over 128 is left out. A URL's query string, fragment and userinfo are stripped before the call is stored, and from a transport error's message.

Which line carries it. Only the response line; a response with no calls has no upstream key.

PII redaction helper

redactPII() redacts personal data before you hand it to a system whose logs you do not control. It applies the ingest service's rules: this package, ingest and @partner-api/logger all run the shared corpus in packages/database/src/pii-redaction-corpus.json.

In a string it redacts:

A card, token, JWT, IP or phone number after a JSON escape (\n) goes too.

In an array, a sensitive key (password, apiKey, clientSecret, cookie, ...) keeps its key and loses its value. A key that is itself PII is redacted like a value; keys that redact alike keep both values, the second under #2. A __proto__ key is dropped. Lists are walked item by item.

A redacted copy is returned; the input is never modified. Ints, floats, bools and null pass through. Objects pass through unredacted, including a stdClass from json_decode($json): decode with json_decode($json, true) first.

Fail-closed. If PHP's regex engine reports an error on a string (its backtrack or JIT stack limit), the whole string becomes [REDACTION_FAILED] (RedactPii::REDACTION_FAILED), never unredacted or half redacted. The same goes for an array key. The helper never throws.

Linear time and memory. Every rule is linear in its input, with PCRE's JIT on or off. The test suite holds a 1 MB string of each adversarial shape to under a second and 48 MB of peak memory, at the default 128 MB memory_limit.

Options are booleans: preserveStructure (false drops a sensitive key instead of masking it), redactEmails, redactApiKeys, redactTokens, redactPasswords, redactPhoneNumbers, redactCreditCards, redactIpAddresses and redactUrls, all true by default, and redactUuids, false by default.

Metrics

A point may also carry a series of at most 50 characters. A slug is lowercase letters, digits and hyphens; one call takes at most 1000 points. Metrics are not buffered: each call posts at once and throws PartnerApi\Logger\LoggerException on a validation or delivery failure.

Options

The constructor takes tenantToken, then optional baseUrl (default https://ingest.partnerapi.com), httpClient (any Guzzle ClientInterface), timestampProvider (a callable returning epoch milliseconds) and options. Pass them by name. An unknown option or mode, an unusable stdoutSink, and an empty tenantToken in stdout mode throw LoggerException at construction.

Option Default Laravel env (PARTNER_API_LOG_*) Effect
mode Logger::MODE_BUFFERED MODE Logger::MODE_DIRECT posts on every log call, once, and throws LoggerException on failure; Logger::MODE_STDOUT writes each log call as one line to stdoutSink (Pipeline delivery)
stdoutSink 'php://stdout' STDOUT_SINK Where MODE_STDOUT writes: a stream URI or file path, an open stream, or a callable taking each line
onError one error_log() none Receives a LoggerErrorEvent for every drop
batchSize 100 BATCH_SIZE Buffered entries that trigger a drain inside the log call; 0 leaves only flush() and the end-of-request drain
maxBufferSize 1000 MAX_BUFFER_SIZE Entries held before the oldest are dropped
maxRetries 3 MAX_RETRIES Retries per batch on a network fault, 408, 429 or 5xx, in flush() and the end-of-request drain only
retryBaseDelayMs 200 RETRY_BASE_DELAY_MS First backoff window; it doubles per retry, with jitter
retryMaxDelayMs 5000 RETRY_MAX_DELAY_MS Largest backoff window
requestTimeoutMs 5000 REQUEST_TIMEOUT_MS Deadline for one POST, metrics included; 0 disables it
autoDrainTimeoutMs 1000 AUTO_DRAIN_TIMEOUT_MS Budget for the drain in a log call, and on destroy; 0 disables it (on destroy, requestTimeoutMs per attempt)
drainDeadlineMs 5000 DRAIN_DEADLINE_MS Budget for one flush() or end-of-request drain, retries included; 0 disables it
flushOnShutdown true FLUSH_ON_SHUTDOWN Drain from a register_shutdown_function, and on a long-running worker when a logger is destroyed
finishRequestOnShutdown true FINISH_REQUEST Call fastcgi_finish_request() before that drain

batchSize is capped at maxBufferSize and at 1000. Under Laravel each option is a snake_case key in config/partner-logger.php, read from the variable shown; the token and base URL are PARTNER_API_TENANT_TOKEN and PARTNER_API_BASE_URL.

Delivery and errors

Log calls (info, warn, error, debug, logRequest, logResponse) append to an in-process buffer and never throw. The buffer drains:

  1. when it reaches batchSize, inside the log call that filled it;
  2. when you call flush();
  3. at the end of the request. A register_shutdown_function drains it after fastcgi_finish_request(), so the client already has its response.

A shutdown function runs only when the process ends, so outside Laravel a long-running worker calls flush() at the end of each request or job. Under Laravel the provider drains the logger each HTTP request used, after its response:

Under PHP-FPM, mod_php and the other one-request-per-lifecycle servers the end-of-request drain holds every logger until it runs, as it always has. On a long-running worker (the cli SAPI: Octane, RoadRunner, Swoole, queue:work) it does not keep a logger alive, so a dropped logger is collected; one dropped with entries still buffered drains them as it is destroyed (see What this costs your request). FrankenPHP in classic mode (one request per lifecycle) reports the frankenphp SAPI, so it gets the long-running behaviour too: the destroy-time drain, bounded by autoDrainTimeoutMs. Nothing is lost; only the timing differs from PHP-FPM. NGINX Unit's SAPI is outside the per-request list too, and behaves the same way.

A child forked with pcntl_fork() inherits the parent's buffer and pending end-of-request drain, and delivers the parent's buffered entries again when it exits. Call flush() before forking.

A POST carries at most 1000 entries and 1 MB of log lines.

What this costs your request

PHP has no event loop, so delivery is synchronous and bounded:

On PHP-FPM the end-of-request drain counts against request_terminate_timeout, so keep drainDeadlineMs well under it. max_execution_time does not help: on Unix it does not tick while PHP waits on a socket. On a bare FPM app the drain's fastcgi_finish_request() ends the response, so a shutdown function registered after the first log call can no longer write output; set finishRequestOnShutdown to false if your app needs that.

onError

Every drop goes to onError, which by default writes the message with error_log(). Anything the hook throws is swallowed.

$event->reason Meaning
flush-failed A batch was refused (a 4xx other than 408 or 429, or retries ran out) and discarded
buffer-overflow maxBufferSize was reached and the oldest entries were dropped
invalid-entry The call itself was unusable: no API key, unserialisable data, a malformed request or response array; also a rejected or dropped upstream call
drain-timeout drainDeadlineMs ran out and the rest was dropped
write-failed Stdout mode only: the sink threw, could not be opened, or took less than the whole line. At most one a minute; entryCount is the lines lost
sink-warning Stdout mode only, once per logger: under PHP-FPM with php://stdout or php://stderr as the sink, which FPM discards or splits

The event also carries message, entryCount, droppedTotal, and where they apply status, attempts, retryable and cause.

Counters

upstreamDropped counts upstream calls dropped for want of a response line.

Pipeline delivery (stdout mode)

If you already ship your logs through a pipeline of your own (an OpenTelemetry Collector, Fluent Bit, Vector), the SDK can write each log call as one JSON line to stdout instead of sending it, and your collector forwards the lines to Partner API over OTLP/HTTP. Nothing else changes: the same calls, the same facade, context, request scopes and upstream trail.

Setting up the collector: the pipeline guide has tested configurations for the OpenTelemetry Collector, Fluent Bit and Vector, troubleshooting, the limits, and the line format for writing lines without the SDK.

Choose it when your services already log to stdout at volume and you do not want a second shipper inside the process, with its own buffer, retries and egress. Stay on the default (push) for small or serverless deployments, or wherever no collector runs: push needs nothing besides the SDK.

Switch with configuration, not code:

Sinks

stdoutSink takes:

Each line is a single fwrite() (or a single call), so lines from one process never interleave. An emitter cut short mid-line ends the fragment with one "\n", so the next line keeps its prefix, and reports the line as lost (ingest drops the fragment as unparseable); the SDK does exactly that and does not retry. Between processes sharing one output, a pipe write is atomic only up to 4096 bytes on Linux, and a line with bodies is usually longer: give each process its own output, or a file. Appends to one shared file from several processes do not interleave on a local filesystem; on a network filesystem (NFS, EFS) they can, so use one file per process there.

Rotation. A file sink stays open. At most once a second, the logger checks whether its path still names the file it is writing to. When the file was renamed or deleted (logrotate's default create mode), it opens the path afresh and switches over only if that open succeeds; until then it keeps writing to the file it has, reports nothing, and tries again a second later. Lines written before the switch land in the rotated file. copytruncate keeps the same file and needs no reopen.

PHP-FPM

An FPM worker's stdout is not the container's stdout. FPM discards worker stdout and stderr unless the pool sets catch_workers_output = yes. With it, FPM writes worker output to its own error log, prefixes each line with [pool www] child 12 said into stdout: unless decorate_workers_output = no, and splits any line longer than log_limit (1024 bytes by default). The official Docker images set catch_workers_output = yes, decorate_workers_output = no and log_limit = 8192, and send that log to the master's stderr: there the lines appear on the container's stderr, not stdout, and are still split above 8192 bytes. A split line is lost at ingest.

So under FPM, point the sink at a file your collector tails:

A logger constructed under FPM with php://stdout or php://stderr as its sink reports one sink-warning to onError saying so: once per logger instance, not per line. Under FPM each request builds its own logger, so with the default hook that is one error_log() line per request.

If you want one file per worker, build the logger in your own service provider with getmypid() in the path: a value computed in config/partner-logger.php is frozen by php artisan config:cache.

Octane and other long-running processes

Under php artisan octane:start (Swoole, RoadRunner or FrankenPHP) your workers run as child processes of a server that re-renders what they print: a JSON line on a worker's stdout is decoded, re-encoded and printed behind a label, so it loses its prefix, and repeated stderr lines are merged. Neither reaches ingest, while stats() still counts the lines as written. Under Octane the sink must be a file, by absolute path:

php://stdout is right only for a plain CLI process whose stdout is what your container runtime captures: queue:work run directly (not under Horizon), or a daemon of your own. If several such processes share one stdout, the pipe limit above applies.

Testing

Pass a Guzzle client backed by a MockHandler, a fixed timestampProvider and flushOnShutdown => false. The sleeper, randomizer and clock options replace the backoff sleep, its jitter and the monotonic clock (in milliseconds).


All versions of logger with dependencies

PHP Build Version
Package Version
Requires php Version ^8.1
guzzlehttp/guzzle Version ^7.0
ramsey/uuid Version ^4.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 partner-api/logger contains the following files

Loading the files please wait ...