Connecting to GLog
Acest conținut nu este încă disponibil în limba selectată.
How any service (SaaS/PaaS in the gStack ecosystem, or an external system) sends audit
events to GLog and reads them back. GLog is the centralized, tamper-evident
audit/logging microservice: you POST an event, GLog stores it immutably (SHA-256 hash
chain per (tenant, service)) and makes it searchable.
TL;DR — one HTTP call per event:
POST https://glog.gstack.esempla.systems/api/v1/app/audit-eventswith a small JSON body. No SDK required to start; official SDKs (Java on Maven, PHP on Composer) are available (§7,sdk.md).
Related docs: api.md (full endpoint/field reference), the runnable
glog.postman_collection.json, and live OpenAPI at
/api/v1/openapi.json (Swagger UI /swagger-ui.html).
1. Endpoints & base URL
Section titled “1. Endpoints & base URL”| Demo (live) | https://glog.gstack.esempla.systems |
| Internal (on the shared box, container→container) | http://glog-frontend (nginx → glog-backend) |
| Ingest | POST /api/v1/app/audit-events |
| Get one | GET /api/v1/app/audit-events/{id} |
| Search | GET /api/v1/app/audit-events?tenantId&from&to&objectType&page&size |
| Integrity | GET /api/v1/admin/audit-events/integrity |
| Health / metrics | GET /health/ready, /health/live, /metrics |
All bodies and responses are application/json.
2. Authentication
Section titled “2. Authentication”GLog is an OAuth2 resource server. Two modes:
- Demo deployment — auth is bypassed; send no token. Every request is tenant
demo, so use"tenantId": "demo". This is the mode the live URL above runs in. - Production — send
Authorization: Bearer <jwt>from Keycloak (realminterdictii). The token must carry:- a scope:
audit:write(ingest),audit:read(search/get),audit:admin(integrity/replay); - a
tenantclaim that equals thetenantIdin your request — this enforces tenant isolation (you can only write/read your own tenant).
- a scope:
A service authenticates with the client-credentials grant (machine-to-machine): it gets a token from Keycloak once, caches it, and refreshes before expiry.
3. Sending an event (ingest)
Section titled “3. Sending an event (ingest)”Minimum body — five required fields:
{ "tenantId": "demo", "eventTimestamp": "2026-07-24T09:01:05Z", "service": "gpay", "objectType": "PAYMENT", "actionType": "CREATE"}Everything else is optional but recommended: operation, status (SUCCESS/FAILURE),
actorType, userDetails, ipAddress, objectAffected, idnp, correlationId, and a
free-form details object. Full field table in api.md.
Response: 201 Created, Location: …/{id}, and the stored event (with server-computed
id, receivedAt, entryHash, prevHash).
Two habits that make GLog worth it
Section titled “Two habits that make GLog worth it”- Set a
correlationIdshared across every service touching one business flow (a request id, a payment id, …). GLog then stitches the whole cross-service timeline together — the “why don’t I see X?” investigation. - Send an
Idempotency-Keyheader on events you might retry — a repeated key returns the first-stored event instead of creating a duplicate.
Examples (raw HTTP — no SDK)
Section titled “Examples (raw HTTP — no SDK)”curl
curl -X POST https://glog.gstack.esempla.systems/api/v1/app/audit-events \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: pay-5521-created' \ -d '{ "tenantId":"demo","eventTimestamp":"2026-07-24T09:01:05Z", "service":"gpay","objectType":"PAYMENT","actionType":"CREATE", "operation":"PAYMENT_PROCESS","status":"SUCCESS","objectAffected":"pay-5521", "correlationId":"corr-1001","details":{"amount":1250,"currency":"MDL"} }'Java (JDK 11+ HttpClient, no dependencies)
var body = """ {"tenantId":"demo","eventTimestamp":"%s","service":"gpay", "objectType":"PAYMENT","actionType":"CREATE","status":"SUCCESS", "correlationId":"corr-1001","details":{"amount":1250}} """.formatted(java.time.Instant.now());
var req = java.net.http.HttpRequest.newBuilder() .uri(java.net.URI.create("https://glog.gstack.esempla.systems/api/v1/app/audit-events")) .header("Content-Type", "application/json") // .header("Authorization", "Bearer " + token) // production .POST(java.net.http.HttpRequest.BodyPublishers.ofString(body)) .build();
java.net.http.HttpClient.newHttpClient() .sendAsync(req, java.net.http.HttpResponse.BodyHandlers.ofString()); // fire-and-forgetPHP (Guzzle)
$client = new GuzzleHttp\Client(['base_uri' => 'https://glog.gstack.esempla.systems']);$client->postAsync('/api/v1/app/audit-events', [ // 'headers' => ['Authorization' => "Bearer $token"], // production 'json' => [ 'tenantId' => 'demo', 'eventTimestamp' => gmdate('Y-m-d\TH:i:s\Z'), 'service' => 'gpay', 'objectType' => 'PAYMENT', 'actionType' => 'CREATE', 'status' => 'SUCCESS', 'correlationId' => 'corr-1001', 'details' => ['amount' => 1250], ],]); // async promise — don't block the requestPython (requests)
import requests, datetimerequests.post("https://glog.gstack.esempla.systems/api/v1/app/audit-events", json={ "tenantId": "demo", "eventTimestamp": datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), "service": "gpay", "objectType": "PAYMENT", "actionType": "CREATE", "status": "SUCCESS", "correlationId": "corr-1001", "details": {"amount": 1250},}, timeout=3) # short timeout; swallow errorsNode (fetch)
fetch('https://glog.gstack.esempla.systems/api/v1/app/audit-events', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tenantId: 'demo', eventTimestamp: new Date().toISOString(), service: 'gpay', objectType: 'PAYMENT', actionType: 'CREATE', status: 'SUCCESS', correlationId: 'corr-1001', details: { amount: 1250 }, }),}).catch(() => {}); // best-effort4. Reading events back
Section titled “4. Reading events back”# one eventcurl https://glog.gstack.esempla.systems/api/v1/app/audit-events/<id>
# search a tenant/time window (returns { items, page, size, total })curl 'https://glog.gstack.esempla.systems/api/v1/app/audit-events\?tenantId=demo&from=2026-07-01T00:00:00Z&to=2026-08-01T00:00:00Z&page=0&size=20'
# verify the hash chain wasn't tampered with (audit:admin)curl 'https://glog.gstack.esempla.systems/api/v1/admin/audit-events/integrity\?tenantId=demo&from=2026-07-01T00:00:00Z&to=2026-08-01T00:00:00Z'Or use the GLog web UI (dashboard, search with filters, correlated view, archive).
5. Integration best practices
Section titled “5. Integration best practices”- Never let auditing break the business path. Send asynchronously and best-effort: fire-and-forget, catch every error, use a short timeout. If GLog is down, your service must keep working. (This is exactly how the gNotify reference integration behaves.)
- Buffer if you need guarantees. For events you cannot lose, enqueue locally (in-memory queue, outbox table, or a broker) and drain to GLog with retry — don’t call GLog inline.
- Bind
serviceandtenantIdto identity, not user input — in production they must match the authenticated principal, or the chain partition and correlation become spoofable. - Put queryable values in top-level fields, bulky context in
details. Frequently filtered values (service, status, objectType, correlationId, ipAddress) are indexed columns; everything else goes in the JSONdetails. - Timestamps in UTC ISO-8601 (
2026-07-24T09:01:05Z).eventTimestamp= when the action happened; GLog stampsreceivedAtitself. - Mind PII. GLog is immutable (WORM) — you cannot delete an event later. Log identifiers
(
idnp, ids) deliberately; avoid dumping secrets/tokens intodetails.
6. Reference implementation
Section titled “6. Reference implementation”gNotify already integrates this way — see saas_gnotify:
service/audit/GlogAuditClient.java posts an event on every delivery
(operation=<CHANNEL>_SENT|_FAILED, correlationId = the notification tracking id), driven by
gnotify.glog.* config, @Async + try/catch so it never blocks a send. On the shared box it
reaches GLog over the internal http://glog-frontend address. Copy that pattern for the next
producer.
7. Official SDKs (GitLab package registry)
Section titled “7. Official SDKs (GitLab package registry)”Besides raw HTTP (above), there are official SDKs that hide the transport — auth token
acquisition/refresh, request building, and typed helpers — so a service emits an event in one
call. They live in this repo under sdk/ and are published to the GitLab Package
Registry of this project (git.esempla.systems/govtech/gstack/glog).
➡ Install & usage: sdk.md. Quick reference below.
7.1 Java SDK — systems.esempla.glog:glog-sdk-java (Maven)
Section titled “7.1 Java SDK — systems.esempla.glog:glog-sdk-java (Maven)”Intended API:
GLogClient glog = GLogClient.builder() .baseUrl("https://glog.gstack.esempla.systems") .tenant("demo") // .keycloak(issuer, clientId, clientSecret) // production: auto token + refresh .async(true) // fire-and-forget by default .build();
glog.audit(AuditEvent.of("gpay", "PAYMENT", "CREATE") .operation("PAYMENT_PROCESS").status(Status.SUCCESS) .objectAffected("pay-5521").correlationId("corr-1001") .detail("amount", 1250));
Page<AuditEvent> page = glog.search().tenant("demo") .from(Instant.now().minus(Duration.ofDays(1))).to(Instant.now()).fetch();Consuming it (GitLab Maven registry):
<repository> <id>gitlab-glog</id> <url>https://git.esempla.systems/api/v4/projects/574/packages/maven</url></repository><dependency> <groupId>systems.esempla.glog</groupId> <artifactId>glog-sdk-java</artifactId> <version>0.1.0</version></dependency>(A GitLab Deploy Token / PAT goes in ~/.m2/settings.xml for auth. A Spring Boot
auto-configuration starter — glog-spring-boot-starter — is a natural companion so a
@GlogAudit bean is injected from application.yml.)
7.2 PHP SDK — esempla/glog-sdk (Composer)
Section titled “7.2 PHP SDK — esempla/glog-sdk (Composer)”$glog = new Esempla\GLog\Client(baseUrl: 'https://glog.gstack.esempla.systems', tenant: 'demo');$glog->audit( service: 'gpay', objectType: 'PAYMENT', actionType: 'CREATE', operation: 'PAYMENT_PROCESS', status: 'SUCCESS', correlationId: 'corr-1001', details: ['amount' => 1250],);Consuming it (GitLab Composer registry):
{ "repositories": [ { "type": "composer", "url": "https://git.esempla.systems/api/v4/group/1087/-/packages/composer/packages.json" } ], "require": { "esempla/glog-sdk": "^0.1" }}7.3 Publishing (manual)
Section titled “7.3 Publishing (manual)”The SDKs live in this repo (sdk/java, sdk/php). Releases are published manually by a
maintainer with a GitLab token (write_package_registry) — no CI/CD:
- Java —
mvn -f sdk/java/pom.xml deployto the project’s Maven registry. - PHP — register the tag via the GitLab Composer registry API.
Exact commands are in sdk.md → Versioning & publishing.
Versioning follows the GLog API (0.x while the API is pre-1.0). The SDKs are thin wrappers over
the documented HTTP contract, so anything an SDK does can always be done with a plain request.
The SDKs are built and tested (
sdk/java,sdk/php); seesdk.mdfor install & publish.