A small, dependency-free PHP client for selfwatch — a self-hosted log/event ingestion platform.
It sends events (messages and exceptions) to selfwatch's store endpoint over
HTTP(S) using nothing but curl. The capture methods never throw on transport
errors, so adding logging can't crash your app.
- PHP 8.1+
- No third-party runtime dependencies (uses
ext-curl,ext-json) - PSR-4 namespace
Selfwatch\Sdk
composer require selfwatch/sdkrequire 'vendor/autoload.php';The SDK is three small files with no external dependencies. Copy src/ into your
project and require the classes directly:
require __DIR__ . '/path/to/src/Dsn.php';
require __DIR__ . '/path/to/src/Severity.php';
require __DIR__ . '/path/to/src/Client.php';selfwatch identifies your project with a DSN (Data Source Name):
{scheme}://{TOKEN}@{HOST}/{PROJECT_ID}
Example:
http://cd9ef67832d31a28fe50260fc1c85bc1f278e8b3734b05a9@localhost:8003/1
| Part | Meaning |
|---|---|
scheme |
http or https |
TOKEN |
the project's ingestion key |
HOST |
host, optionally with a port (e.g. localhost:8003) |
PROJECT_ID |
the integer project id |
The SDK derives the store endpoint from the DSN:
POST {scheme}://{HOST}/api/{PROJECT_ID}/store/
httpis only allowed for loopback hosts (127.0.0.1,::1,localhost). A non-loopback host must usehttps, otherwise constructing theDsnthrows anInvalidArgumentException. This mirrors the server, which rejects insecure transport for everything except loopback.
use Selfwatch\Sdk\Client;
use Selfwatch\Sdk\Severity;
$client = new Client(getenv('SELFWATCH_DSN'), [
'environment' => 'production',
'release' => '1.4.2',
'tags' => ['service' => 'checkout'],
]);
// Send a message — returns the event id (string) or null on failure.
$id = $client->captureMessage('Cache warmed', Severity::INFO);
// Capture an exception (walks the getPrevious() chain automatically).
try {
doSomethingRisky();
} catch (\Throwable $e) {
$eventId = $client->captureException($e, Severity::ERROR, [
'order_id' => 1234,
]);
if ($eventId === null) {
// Logging never throws by default; inspect what went wrong:
error_log('selfwatch send failed: ' . $client->getLastError());
}
}new Client(Dsn|string $dsn, array $options = [])
captureMessage(string $message, string $level = 'info', array $extra = []): ?string
captureException(\Throwable $e, string $level = 'error', array $extra = []): ?string
captureEvent(array $event): ?string // low-level: fills defaults, POSTs, parses
getLastError(): ?string // reason the last capture failed, or null
getDsn(): DsnAll capture* methods return the event id on success and null on
failure (unless throw_on_error is enabled).
Level constants: Severity::FATAL, ERROR, WARNING, INFO, DEBUG.
new Dsn(string $dsn) // throws InvalidArgumentException if malformed
Dsn::from(Dsn|string $dsn): Dsn
getScheme(): string
getToken(): string
getHost(): string // includes port, e.g. "localhost:8003"
getProjectId(): int
getStoreUrl(): string // {scheme}://{HOST}/api/{PROJECT_ID}/store/Passed as the second argument to new Client($dsn, [...]).
| Option | Type | Default | Description |
|---|---|---|---|
environment |
string|null |
null |
Sets environment on every event (e.g. production). |
release |
string|null |
null |
Sets release on every event (e.g. a version or git SHA). |
server_name |
string|null |
gethostname() |
Overrides the reported server name. |
tags |
array<string,string> |
[] |
Default tags merged into every event (event tags win on conflict). |
timeout |
int (seconds) |
5 |
Connect + transfer timeout for the HTTP request (minimum 1). |
verify_ssl |
bool |
true |
Verify the TLS peer/host. Leave true in production. |
throw_on_error |
bool |
false |
If true, capture failures throw RuntimeException instead of null. |
When you call any capture* method, the SDK provides sensible defaults for any
field you didn't set:
event_id— 32 lowercase hex chars (fromrandom_bytes(16))timestamp— current time, RFC3339 UTC (gmdate('Y-m-d\TH:i:s\Z'))platform—"php"level—"info"for messages,"error"for exceptionsserver_name—gethostname()(or theserver_nameoption)environment/release— from the matching optionscontexts.runtime—{ "name": "php", "version": PHP_VERSION }tags— the default tags from options, merged with per-event tags
For exceptions, exception.values is built from the whole getPrevious() chain
(innermost cause last, per the wire contract). Each frame carries
filename, function, lineno and an in_app flag — in_app is true
unless the file lives under a /vendor/ directory.
By default, transport and server errors are swallowed: the capture method
returns null and the reason is available via getLastError(). The server may
respond with, among others:
| Status | Meaning |
|---|---|
401 |
missing token |
403 |
invalid / mismatched token |
400 |
insecure transport or bad JSON |
413 |
body larger than 2 MiB |
429 |
rate limited (see the Retry-After response header) |
500 |
server error |
If you'd rather have failures raise exceptions (handy in tests), construct the
client with ['throw_on_error' => true].
SELFWATCH_DSN="http://<token>@localhost:8003/1" php examples/basic.phpWith no SELFWATCH_DSN set it falls back to a local dev DSN.
MIT — see LICENSE.