Skip to content

Architecture

Akari is split into two cooperating components connected by a fire-and-forget UDP channel, keeping the PHP hot path as cheap as possible.

graph LR
  subgraph PHP["PHP-FPM / CLI"]
    A["akari extension"]
  end
  subgraph FWD["Go forwarder"]
    B["akari-forwarder"]
  end
  subgraph COL["Collector"]
    C["Jaeger · Tempo · …"]
  end
  A -- "UDP / msgpack<br>sendto() — fire & forget, ~1μs" --> B
  B -- "OTLP / HTTP — POST JSON" --> C

Spans are serialized as compact msgpack and sent via UDP to the local Go forwarder, which batches and forwards them to your OTLP collector. Because the export is fire-and-forget UDP, the extension never blocks on the network — a slow or absent collector cannot slow down PHP request handling.

Log records emitted with Akari\log() travel the same UDP path and are forwarded to the collector's /v1/logs endpoint (spans go to /v1/traces).

Why a separate forwarder?

  • Non-blocking PHP — the extension hands off spans in ~1μs and returns to serving the request. Batching, retries, and HTTP are the forwarder's job.
  • Process-local aggregation — many PHP workers send to a single forwarder, which batches across them before talking to the collector.
  • Decoupled protocol — the wire format between extension and forwarder (msgpack/UDP) is independent of OTLP, so the collector side can evolve without touching the C extension.

Production readiness

Area Status
Memory safety ASAN-verified, no leaks, bounded allocations
Overhead when off Zero — observer_init returns {NULL, NULL}
Exception tracking Engine-level hook catches all exceptions, even caught ones
Extensibility conflicts Uses official zend_observer API — compatible with Xdebug, OPcache
Cross-platform macOS (kqueue) + Linux (timer_create / SIGEV_THREAD_ID)
Thread safety (ZTS) Full ZTS support via ZEND_MODULE_GLOBALS
Span limit 256K spans max per request (overflow → warning + drop)
Frame limit 64K deduplicated frames max
Stack depth Clamped to 256, defaults to 64
SQL normalization Query fingerprinting for grouping in dashboards

Project structure

src/
├── profiler.h / profiler.c       # Core state + lifecycle
├── profiler_internal.h           # Shared internal declarations
├── profiler_span.c               # Frame dedup, span management
├── observer.c                    # zend_observer begin/end callbacks
├── hook_registry.c/h             # Modular hook registration system
├── hook_pdo.c                    # PDO instrumentation
├── hook_sqlite3.c                # SQLite3 (ext-sqlite3) instrumentation
├── hook_curl.c                   # curl instrumentation
├── hook_redis.c                  # Redis / RedisCluster / Relay
├── hook_mysqli.c                 # MySQLi (OO + procedural)
├── hook_oci8.c                   # Oracle OCI8
├── hook_memcached.c              # Memcached + Memcache
├── hook_elasticsearch.c          # Elasticsearch v7/v8 + OpenSearch
├── hook_predis.c                 # Predis (pure PHP Redis)
├── hook_grpc.c                   # gRPC
├── hook_rdkafka.c                # rdkafka
├── hook_soap.c                   # SOAP
├── hook_pheanstalk.c             # Pheanstalk / Beanstalkd
├── hook_graphql.c                # webonyx/graphql-php
├── hook_amqp.c                   # AMQP + php-amqplib
├── hook_php_streams.c            # HTTP stream wrappers
├── hook_pcre.c                   # PCRE regex functions
├── hook_io.c                     # Filesystem, password, APCu, DNS, shell, mail, sleep
├── hook_framework.c              # Symfony/Laravel route detection
├── hook_symfony.c                # Symfony components
├── hook_laravel.c                # Laravel components
├── hook_shopware.c               # Shopware 6 DAL + kernel
├── hook_doctrine.c               # Doctrine ORM
├── hook_twig.c                   # Twig templates
├── hook_error.c                  # Framework error handlers
├── hook_engine.c                 # Engine-level hooks (compile, GC, exceptions)
├── hook_root_span.c              # HTTP root span + W3C traceparent
├── php_akari.c                   # Module entry, INI, userland API
├── otlp_export.c                 # OTLP JSON serialization
├── udp_export.c                  # UDP/msgpack export
├── sql_normalize.c               # SQL normalization + truncation
└── msgpack_write.h               # Header-only msgpack encoder
forwarder/
├── cmd/akari-forwarder/          # Go entry point
└── internal/                     # Receiver, buffer, transform, forwarder
tests/
└── *.phpt                        # PHPT integration tests