Expose application metrics from PHP into Caddy's metrics registry (frankenphp_metric_*)
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 38/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Ambito
- backend, observability
Direzione di ricerca
Inizia da frankenphp.stub.php e dal percorso esistente frankenphp_log()/go_log_attrs, quindi esamina il Metrics interface, PrometheusMetrics, nullMetrics e modules/caddyhttp/metrics.go. Prima definisci con i maintainer la dichiarazione delle metriche e la Cardinality API; il lavoro è completato quando il percorso PHP/CGO/Go concordato alimenta il registro esistente e il bridge OTLP, con il comportamento durante la vita del worker documentato.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe your feature request
Is your feature request related to a problem? Please describe.
FrankenPHP exposes server-side metrics (frankenphp_busy_workers, frankenphp_queue_depth, …) through Caddy's Prometheus registry. That registry feeds both outputs Caddy supports: the /metrics endpoint for Prometheus scraping, and the OpenTelemetry exporter enabled by metrics { otlp }. The pipeline is already configured, scraped and exported.
Application metrics cannot use it. There is no way for a PHP script to emit a counter or a gauge into that registry. frankenphp_log() is the only bridge to Caddy's observability, and a log is not a metric: no type, no aggregation, no labels.
So publishing something like "messages enqueued per second" requires a second, parallel pipeline: an OpenTelemetry SDK or a vendor client, with its own credentials, transport and flush timing, while a working pipeline runs in the same process. We currently maintain two exporters and two auth mechanisms for what is a single stream of metrics out of a single container, and the application metrics land in a different place from the server metrics that explain them.
Describe the solution you'd like
A userland API mirroring frankenphp_log(), writing into the registry Caddy already exposes:
frankenphp_metric_counter('app_messages_enqueued', 5, ['queue' => 'events']);
frankenphp_metric_gauge('app_pending_jobs', 42);
frankenphp_metric_histogram('app_job_duration_seconds', 0.235, ['type' => 'export']);
They would appear alongside the built-in metrics on /metrics:
# TYPE app_messages_enqueued counter
app_messages_enqueued{queue="events"} 5
and, for users who enabled metrics { otlp }, be exported over OpenTelemetry as part of the same batch, with no extra configuration. That second output costs nothing to implement: modules/caddyhttp/metrics.go wires the OTel exporter to the same registry through otelprom.NewMetricProducer(otelprom.WithGatherer(reg)), so anything registered there is picked up by the Prometheus-to-OpenTelemetry bridge. Counters map to monotonic sums, gauges to gauges, histograms to histograms; the export path itself does not need to change.
This matters beyond convenience: application and server metrics reach the backend through one exporter, sharing the same resource attributes and the same collection interval, which is what makes them comparable on the same dashboard.
Implementation-wise this follows the frankenphp_log() path almost exactly:
- PHP side: three functions in
frankenphp.stub.php, takingstring $name, float $value, array $labels = []. - CGO bridge:
//export go_metric_counter/go_metric_gauge/go_metric_histogram, receiving(threadIndex C.uintptr_t, name *C.zend_string, value C.double, cLabels *C.zval).GoString()for the name andGoMap[string]()for the labels, exactly asgo_log_attrsdoes today. Returning a non-nil*C.charraises a PHP exception, which covers unknown metric names and label mismatches. - Go side: three methods on the existing
Metricsinterface, implemented byPrometheusMetrics, which already holds theprometheus.Registerer. Userland series would live in*prometheus.CounterVec/GaugeVec/HistogramVeckeyed by name, guarded by the same RWMutex introduced in #2450.nullMetricsgets no-op implementations, so metrics stay free when disabled. - Worker lifetime:
CounterVecvalues persist across worker restarts since the registry belongs to the Caddy app, not the thread. That is what you want for counters, and worth stating explicitly in the docs.
The open question, and probably the reason this doesn't exist yet, is cardinality: userland code can blow up a Prometheus registry with unbounded label values. Options, strictest first:
- Metrics declared in the Caddyfile before use, unknown names rejected at the CGO boundary:
This also letsfrankenphp { metric app_messages_enqueued counter { labels queue } }CounterVecbe created once at provision time with a fixed label set, which is the cheapest and safest option. - Runtime registration with a configurable
max_seriescap; further series rejected and logged. - Reserved-prefix ban plus a cap.
We'd lean towards 1 but have no strong opinion beyond "there must be a bound", and would rather follow what maintainers consider idiomatic. Happy to implement if there's agreement on the API shape; this issue is meant to settle that first.
Describe alternatives you've considered
- The OpenTelemetry PHP SDK, or a vendor SDK, inside the application (current approach): works, but means a second exporter and auth path running next to Caddy's, its own resource attributes and export interval, and application metrics that cannot be correlated with the FrankenPHP metrics explaining them. Two OTLP exporters in one container is also awkward to configure, since both read the same
OTEL_*environment variables. - Scraping
/metricsfrom another process: extra moving part, and only relays FrankenPHP's own metrics, not application counters. - A Caddy module: requires a custom build, and metric names and types must be known at compile time.
frankenphp_log()plus backend-side extraction: acceptable for counters, poor for gauges and histograms, and moves aggregation to the backend.
- Lingua principale
- Go
- Stelle
- 11.4k
- Fork
- 490
- Merge medio
- 3g 11h
- PR unite (30g)
- 29
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di php/frankenphp
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
php/frankenphp#2671 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Include frankenphp version in logsForse già presa @ousamabenyounes l’ha presa 82 giorni fa. Apertaenhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
php/frankenphp#2483 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
suggestion about compile.mdForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Apertaenhancement
Difficoltà 1/5 1-3 ore Idoneità per principianti 68/100
php/frankenphp#601 · 5 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
v1.13.0: config reload returns HTTP 500 "server is not registered, you must first call frankenphp.Init() with the WithServer() option" while workers boot (regression from v1.12.7)Forse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 54/100
php/frankenphp#2698 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
php/frankenphp#2695 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di php/frankenphp
Issue simili
-
raised-by:reviewer
Difficoltà 1/5 1-3 ore Idoneità per principianti 84/100
medici-finance/assay#2681 ·
I maintainer di solito rispondono entro 1 giorno
-
feat(icons): add [solidping]Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 82/100
homarr-labs/dashboard-icons#2939 ·
I maintainer di solito rispondono entro 1 giorno
-
refine bluetooth scan listAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
AvengeMedia/DankMaterialShell#3761 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 77/100
I maintainer di solito rispondono entro 1 giorno
-
docs: clarify replacement for outdated Terms of Service URL in readme.mdForse già presa @mohitjoshi-hey l’ha presa oggi. Aperta
Difficoltà 1/5 1-3 ore Idoneità per principianti 66/100