Skip to content

overworks/php-openobserve

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

php-openobserve

CI Latest Version PHP Version License

A PHP client for OpenObserve. Ships logs/metrics/traces, runs SQL searches, manages streams, and exposes the administrative APIs through a small, typed surface built on PSR-18 / PSR-17.

Installation

composer require minhyung/openobserve

The library uses PSR-18 / PSR-17 HTTP abstractions and discovers an implementation at runtime via php-http/discovery. If your project does not already ship one, install Guzzle (or any other implementation) alongside:

composer require guzzlehttp/guzzle

Quick start

Email + password

use Minhyung\OpenObserve\Client;

$client = new Client(
    baseUrl: 'http://localhost:5080',
    email: 'root@example.com',
    password: 'Complexpass#123',
    organization: 'default',
);

$client->logs()->json('app-logs', [
    ['level' => 'info', 'message' => 'hello, world'],
]);

Pre-encoded token

OpenObserve exposes a "Basic auth token" in the UI — a base64 blob of email:password you can copy without re-encoding. Use Client::withToken():

$client = Client::withToken(
    baseUrl: 'http://localhost:5080',
    token: 'YWRtaW46Q29tcGxleHBhc3MjMTIz',
    organization: 'default',
);

The bound organization is the default for every resource accessor; pass an override per call (e.g. $client->logs('other-org')) when you need to target a different one.

Log ingestion

Three endpoints are exposed via the Logs resource:

// _json: array of records into one stream
$client->logs()->json('app-logs', [
    ['_timestamp' => 1_700_000_000_000_000, 'level' => 'info', 'message' => 'one'],
    ['_timestamp' => 1_700_000_001_000_000, 'level' => 'warn', 'message' => 'two'],
]);

// _multi: same shape but sent as NDJSON
$client->logs()->multi('app-logs', $records);

// _bulk: Elasticsearch-compatible, target many streams at once
$client->logs()->bulk([
    'app-logs' => [['level' => 'info', 'message' => 'hi']],
    'audit'    => [['action' => 'login', 'user' => 'alice']],
]);

OpenTelemetry (OTLP/JSON)

$client->otlp()->logs([
    'resourceLogs' => [/* OTLP/JSON */],
], streamName: 'app-logs');

$client->otlp()->metrics(['resourceMetrics' => [/* ... */]]);
$client->otlp()->traces(['resourceSpans' => [/* ... */]]);

Search

$result = $client->search()->sql(
    sql: 'SELECT * FROM "app-logs" WHERE level = \'error\'',
    startTime: 1_700_000_000_000_000, // unix microseconds
    endTime:   1_700_000_060_000_000,
    size: 100,
);

foreach ($result['hits'] ?? [] as $row) {
    // ...
}

Streams management

use Minhyung\OpenObserve\Resource\Streams;

$client->streams()->list(Streams::TYPE_LOGS, fetchSchema: true);
$client->streams()->get('app-logs');
$client->streams()->updateSettings('app-logs', [
    'data_retention' => 30,
    'full_text_search_keys' => ['set' => ['body']],
]);
$client->streams()->delete('app-logs', Streams::TYPE_LOGS, deleteAll: true);

Administrative resources

All follow the same CRUD shape: list, get, create, update, delete.

$client->functions()->create([
    'function' => 'function(row) return row end',
    'order' => 1,
]);

$client->users()->create([
    'email' => 'admin@example.com',
    'first_name' => 'ming', 'last_name' => 'xing',
    'password' => 'complex#pass',
    'role' => 'admin',
]);

$client->alerts()->setEnabled('high-error-rate', false);
$client->alerts()->trigger('high-error-rate');

$client->dashboards()->list(folder: 'production');

$client->organizations()->list();          // server-global
$client->organizations()->summary('acme'); // org-scoped stats

Monolog integration

use Minhyung\OpenObserve\Monolog\Handler;
use Monolog\Level;
use Monolog\Logger;

$logger = new Logger('app');
$logger->pushHandler(new Handler($client, stream: 'app-logs', level: Level::Info));

$logger->info('hello world', ['user_id' => 42]);

handleBatch() is implemented so Monolog's buffering handlers (e.g. BufferHandler, FingersCrossedHandler) send one HTTP request per flush.

Escape hatch

For endpoints not yet covered by a typed resource — or OpenObserve deployments whose URL shapes diverge from this library's defaults — drop down to the raw HTTP layer:

$client->request('POST', '/api/default/_settings', ['retention' => 30]);
$client->requestRaw('POST', '/api/upload', "row1\nrow2\n", 'text/csv');

Both apply the configured base URL and Authorization header, decode JSON responses, and surface non-2xx responses as exceptions.

Errors

All library exceptions implement Minhyung\OpenObserve\Exception\OpenObserveException, so you can catch them uniformly:

Class Trigger
AuthenticationException HTTP 401 / 403
ApiException Any other 4xx / 5xx, with getStatusCode() and getResponseBody()
TransportException PSR-18 client failure (network error, DNS, etc.)
use Minhyung\OpenObserve\Exception\OpenObserveException;

try {
    $client->logs()->json('app-logs', $records);
} catch (OpenObserveException $e) {
    // logging, retry, etc.
}

Requirements

  • PHP 8.3+
  • A PSR-18 HTTP client and PSR-17 message factories (autodiscovered via php-http/discovery)

Development

composer install
composer test

Integration tests (optional)

A handful of integration tests live in tests/Integration/. They skip by default and only run when you point them at a real OpenObserve instance through environment variables:

OPENOBSERVE_URL=http://localhost:5080 \
OPENOBSERVE_EMAIL=root@example.com \
OPENOBSERVE_PASSWORD='Complexpass#123' \
  composer test:integration

Alternatively, copy phpunit.xml.dist to phpunit.xml (gitignored) and set the <env> values there.

License

MIT

About

OpenObserve library for PHP

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages