Nest Engineering Docs
Event Logging

Architecture

Request flow and component boundaries for Event Logging

Event Logging is a narrow ConnectRPC service with a write-first execution path: normalize the request, persist one Spanner row, then optionally forward the accepted event to Statsig.

System context

Internal caller
  -> Cloud Run IAM
  -> Event Logging (ConnectRPC / ASGI)
       -> Protovalidate request checks
       -> Request normalization
       -> Spanner NestEventLog write
       -> Optional Statsig forward
       -> Existing export path to BigQuery / Sigma

Request flow

  1. A caller invokes events.v1.EventLoggingService/LogEvent.
  2. Cloud Run IAM authenticates the request before it reaches the app.
  3. AuthInterceptor extracts x-goog-authenticated-user-* headers into caller context for service-managed audit metadata and logs.
  4. ProtoValidationInterceptor enforces protobuf-level rules such as required event name and max top-level metadata keys.
  5. normalize_log_event_request() trims strings, resolves caller-supplied event_type and actor_id, defaults event_ts, generates a service-owned event_id, reserves _eventLog, and enforces the 64 KiB serialized metadata cap.
  6. SpannerEventLogRepository writes a single row to NestEventLog, storing request-backed event_type and actor_id when present.
  7. If forward_to_statsig resolved to true, the service forwards the stored event to Statsig using a stable service principal (service:events by default).
  8. The handler returns the generated event_id only when the whole operation succeeds.

Key design choices

  • event_id is service-generated, not caller-supplied. The current implementation uses UUID strings that fit the STRING(36) table key.
  • event_ts and ingested_at mean different things. event_ts is logical event time; ingested_at is write time.
  • Auth-derived caller identity and request-supplied actor_id are distinct. The request field populates the row column, while the interceptor populates _eventLog.caller for audit context.
  • Statsig forwarding is a side effect after persistence, not part of the write transaction.
  • Internal-only access is enforced outside the app. The interceptor records caller identity, but the auth gate is Cloud Run IAM.

Main components

  • services/events/main.py: ASGI entrypoint and lifespan bridge.
  • services/events/core/runtime.py: managed Spanner and Statsig dependencies.
  • services/events/core/handlers/event_logging_handler.py: thin Connect handler.
  • services/events/core/service.py: write-first orchestration.
  • services/events/core/normalization.py: request-to-domain normalization.
  • services/events/core/repository.py: Spanner repository.
  • services/events/core/adapters/statsig_forwarder.py: Statsig client and payload mapping.

Last updated on

On this page