@@ -10,7 +10,7 @@ Although this library is part of the [Utopia Framework](https://github.com/utopi
1010## Features
1111
1212- ** Two Table Architecture** : Separate events and gauges tables optimized for their access patterns
13- - ** Events Table** : Request-level metrics with dedicated columns (path, method, status, resource, country, userAgent )
13+ - ** Events Table** : Request-level metrics with dedicated columns for every dimension we filter on (path/ method/ status, service/ resource ids, team ids, country/region, hostname, parsed UA fields )
1414- ** Gauges Table** : Simple resource snapshots (storage size, user count, etc.)
1515- ** Query-Time Aggregation** : No write-time period fan-out — aggregate by any interval at query time
1616- ** Daily Materialized View** : Pre-aggregated daily SummingMergeTree for fast billing queries
@@ -72,34 +72,56 @@ $usage->setup();
7272
7373Events are request-level metrics like bandwidth, executions, API calls. They are summed when aggregated.
7474
75- Event-specific columns: ` path ` , ` method ` , ` status ` , ` resource ` , ` resourceId ` , ` country ` , ` userAgent `
75+ Event-specific columns (see ` Metric::EVENT_COLUMNS ` ): ` path ` , ` method ` , ` status ` ,
76+ ` service ` , ` resource ` , ` resourceId ` , ` resourceInternalId ` , ` teamId ` ,
77+ ` teamInternalId ` , ` country ` , ` region ` , ` hostname ` , ` osCode ` , ` osName ` ,
78+ ` osVersion ` , ` clientType ` , ` clientCode ` , ` clientName ` , ` clientVersion ` ,
79+ ` clientEngine ` , ` clientEngineVersion ` , ` deviceName ` , ` deviceBrand ` ,
80+ ` deviceModel ` .
7681
7782``` php
7883// Collect events — values accumulate in-memory buffer (summed per metric)
7984$usage->collect('bandwidth', 5000, Usage::TYPE_EVENT, [
8085 'path' => '/v1/storage/files',
8186 'method' => 'POST',
8287 'status' => '201',
88+ 'service' => 'storage',
8389 'resource' => 'bucket',
8490 'resourceId' => 'abc123',
91+ 'resourceInternalId' => '42',
92+ 'teamId' => 'team_x',
93+ 'teamInternalId' => '7',
8594 'country' => 'US',
86- 'userAgent' => 'AppwriteSDK/1.0',
95+ 'region' => 'us-east',
96+ 'hostname' => 'app.example.com',
97+ 'osName' => 'iOS',
98+ 'clientName' => 'Appwrite SDK',
99+ 'deviceName' => 'smartphone',
87100]);
88-
89- // Event columns are auto-extracted from tags into dedicated columns
90- // Remaining tags stay in the JSON tags column
91101```
92102
103+ ### Strict tag keys
104+
105+ All keys passed in the ` tags ` array must map to a known event or gauge
106+ column (see ` Metric::EVENT_COLUMNS ` and ` Metric::GAUGE_COLUMNS ` ). Unknown
107+ keys throw at write time — there is no JSON catch-all. To add a new
108+ dimension, widen the schema and bump the library version.
109+
93110### Gauges (Point-in-Time)
94111
95112Gauges are resource snapshots like storage size, user count, file count. Last-write-wins semantics.
96113
114+ Gauge-specific columns (see ` Metric::GAUGE_COLUMNS ` ): ` teamId ` ,
115+ ` teamInternalId ` , ` resourceId ` , ` resourceInternalId ` .
116+
97117``` php
98118// Collect gauges — last value wins per metric in buffer
99119$usage->collect('users', 1500, Usage::TYPE_GAUGE);
100120$usage->collect('storage.size', 1048576, Usage::TYPE_GAUGE, [
101- 'resource' => 'bucket',
121+ 'teamId' => 'team_x',
122+ 'teamInternalId' => '7',
102123 'resourceId' => 'abc123',
124+ 'resourceInternalId' => '42',
103125]);
104126```
105127
@@ -121,12 +143,12 @@ $usage->setFlushInterval(10); // Flush after 10 seconds (default: 20)
121143``` php
122144// Write directly without buffering
123145$usage->addBatch([
124- ['metric' => 'requests', 'value' => 100, 'tags' => ['path' => '/v1/users']],
125- ['metric' => 'bandwidth', 'value' => 50000, 'tags' => ['country' => 'DE']],
146+ ['metric' => 'requests', 'value' => 100, 'tags' => ['path' => '/v1/users', 'method' => 'GET' ]],
147+ ['metric' => 'bandwidth', 'value' => 50000, 'tags' => ['country' => 'DE', 'region' => 'fra' ]],
126148], Usage::TYPE_EVENT);
127149
128150$usage->addBatch([
129- ['metric' => 'users', 'value' => 42, 'tags' => []],
151+ ['metric' => 'users', 'value' => 42, 'tags' => ['teamId' => 'team_x' ]],
130152], Usage::TYPE_GAUGE);
131153```
132154
@@ -250,11 +272,21 @@ $usage->purge([], Usage::TYPE_GAUGE);
250272| path | Nullable(String) | API endpoint path |
251273| method | Nullable(String) | HTTP method |
252274| status | Nullable(String) | HTTP status code |
253- | resource | Nullable(String) | Resource type |
254- | resourceId | Nullable(String) | Resource ID |
255- | country | LowCardinality(Nullable(String)) | ISO country code |
256- | userAgent | Nullable(String) | User agent string |
257- | tags | Nullable(String) | JSON for extra metadata |
275+ | service | LowCardinality(Nullable(String)) | API service (storage, databases, …) |
276+ | resource | LowCardinality(Nullable(String)) | Resource type (bucket, file, …) |
277+ | resourceId | Nullable(String) | External resource id |
278+ | resourceInternalId | Nullable(String) | Internal resource sequence |
279+ | teamId | Nullable(String) | External team id |
280+ | teamInternalId | Nullable(String) | Internal team sequence |
281+ | country | LowCardinality(Nullable(String)) | ISO country code (lowercased) |
282+ | region | LowCardinality(Nullable(String)) | Region code (lowercased) |
283+ | hostname | Nullable(String) | Caller origin host |
284+ | osCode, osName | LowCardinality(Nullable(String)) | Parsed OS short code / name |
285+ | osVersion | Nullable(String) | Parsed OS version |
286+ | clientType, clientCode, clientName, clientEngine | LowCardinality(Nullable(String)) | Parsed client identity |
287+ | clientVersion, clientEngineVersion | Nullable(String) | Parsed client versions |
288+ | deviceName, deviceBrand | LowCardinality(Nullable(String)) | Parsed device identity |
289+ | deviceModel | Nullable(String) | Parsed device model |
258290| tenant | Nullable(String) | Tenant ID (shared tables) |
259291
260292### Gauges Table Schema
@@ -265,7 +297,10 @@ $usage->purge([], Usage::TYPE_GAUGE);
265297| metric | String | Metric name |
266298| value | Int64 | Current value |
267299| time | DateTime64(3) | Snapshot timestamp |
268- | tags | Nullable(String) | JSON metadata |
300+ | teamId | Nullable(String) | External team id |
301+ | teamInternalId | Nullable(String) | Internal team sequence |
302+ | resourceId | Nullable(String) | External resource id |
303+ | resourceInternalId | Nullable(String) | Internal resource sequence |
269304| tenant | Nullable(String) | Tenant ID (shared tables) |
270305
271306### Daily Table Schema
@@ -275,6 +310,11 @@ $usage->purge([], Usage::TYPE_GAUGE);
275310| metric | String | Metric name |
276311| value | Int64 | Aggregated daily sum |
277312| time | DateTime64(3) | Day start timestamp |
313+ | resource | LowCardinality(Nullable(String)) | Resource type |
314+ | resourceId | Nullable(String) | External resource id |
315+ | resourceInternalId | Nullable(String) | Internal resource sequence |
316+ | teamId | Nullable(String) | External team id |
317+ | teamInternalId | Nullable(String) | Internal team sequence |
278318| tenant | Nullable(String) | Tenant ID (shared tables) |
279319
280320### Creating Custom Adapters
0 commit comments