NAME
Langertha::Role::Runtime::MetricsPoll - Async Prometheus /metrics scraper for self-hosted engines
VERSION
version 0.503
SYNOPSIS
use Langertha::Engine::vLLM;
my $vllm = Langertha::Engine::vLLM->new(
url => 'http://localhost:8000/v1',
);
# Async — preferred for live systems
my $records = await $vllm->poll_metrics_f;
# Returns: [ { name => 'vllm:num_requests_running', type => 'gauge',
# value => 3, labels => { model_name => 'Qwen/...' } }, ... ]
# Sync wrapper
my $records = $vllm->poll_metrics;
DESCRIPTION
Composes onto self-hosted engines that expose a Prometheus GET /metrics endpoint at the server's url attribute. The role scrapes the body, parses it via Langertha::Runtime::Metrics, and returns the parsed ArrayRef. No filtering is applied by default — pass a prefix to "poll_metrics_f($prefix)" or filter with "filter_prefix" in Langertha::Runtime::Metrics downstream.
The parsed records can be exported to any OTLP/HTTP metrics receiver (OpenTelemetry Collector, Prometheus, Grafana) via "export_otlp_f" / "export_otlp", which serialize them with Langertha::Runtime::Metrics::OTLP. Langfuse does not ingest OTLP metrics — see "export_otlp_f" for the details and sources.
The endpoint path is derived by stripping the trailing /v1 (or any trailing slash) from url, then appending /metrics. Engines whose url is e.g. http://localhost:8000/v1 therefore hit http://localhost:8000/metrics — matching the convention used by vLLM, SGLang, and llama.cpp's built-in server.
Authentication: None. These are local servers; no api_key header is sent. If a deployment sits behind auth, layer it on externally (proxy or "generate_http_request" in Langertha::Role::HTTP extension).
Ollama is intentionally not composed with this role: Ollama's runtime stats live at /api/ps in JSON, not /metrics in Prometheus text. See Langertha::Runtime::Metrics::EngineContract for the wire contract and the follow-up karr ticket tracked alongside that document for the JSON-to-Prometheus adapter work.
metrics_url
my $url = $engine->metrics_url;
Derives the /metrics URL from the engine's url attribute by stripping trailing slashes and a trailing /v1 path segment, then appending /metrics. Any other path prefix is kept, so http://host/vllm/v1 and http://host/vllm both give http://host/vllm/metrics. Returns the full URL as a string.
poll_metrics_f
my $records = await $engine->poll_metrics_f;
my $vllm = await $engine->poll_metrics_f('vllm:');
Async scrape. Returns a Future that resolves to the ArrayRef of parsed Langertha::Runtime::Metrics records. Optional prefix arguments OR-filter the parser output (see "parse_and_filter" in Langertha::Runtime::Metrics).
Croaks on a non-success HTTP response.
poll_metrics
my $records = $engine->poll_metrics;
Synchronous scrape. Returns the ArrayRef of records or croaks on HTTP failure. It blocks on "poll_metrics_f" with ->get, which drives the loop the pending future belongs to: the engine's IO::Async::Loop on the Net::Async::HTTP backend, or the loop of an injected _async_http client. On the synchronous Langertha::Request::SyncHTTP fallback the future is already complete, so no event loop is created (Langertha::Role::AsyncHTTP).
Use this only when no event loop is already running. Inside an async context prefer "poll_metrics_f".
export_otlp_f
my $response = await $engine->export_otlp_f($records,
endpoint => 'http://localhost:4318/v1/metrics',
headers => { Authorization => 'Basic ...' },
service_name => 'vllm',
resource_attributes => { trace_id => 'trace-123' },
);
Async export. Serializes the parsed records (the ArrayRef from "poll_metrics_f") into an OTLP/HTTP JSON metrics payload via Langertha::Runtime::Metrics::OTLP and POSTs it to endpoint. Returns the HTTP::Response. Croaks on a non-success HTTP response.
%opts are passed through to "build_payload" in Langertha::Runtime::Metrics::OTLP (service_name, resource_attributes, scope_name, timestamp) plus:
endpoint— required. The OTLP/HTTP metrics receiver URL, e.g.http://localhost:4318/v1/metrics(OpenTelemetry Collector), a Prometheus OTLP receiver, or Grafana.headers— optional HashRef of extra request headers (e.g.Authorizationfor a protected receiver).
Langfuse note: Langfuse does not ingest OTLP metrics. Its /api/public/otel endpoint accepts traces only; a POST to /api/public/otel/v1/metrics is accepted and silently discarded (dummy route since langfuse/langfuse#6408), and /api/public/metrics is a read-only query API over Langfuse's own trace data. Point this exporter at a real OTLP metrics backend (Collector, Prometheus, Grafana). Sources: https://github.com/langfuse/langfuse/issues/6395 and https://github.com/orgs/langfuse/discussions/10686.
export_otlp
my $response = $engine->export_otlp($records, endpoint => '...');
Synchronous export. Returns the HTTP::Response or croaks on HTTP failure. Like "poll_metrics" it blocks on "export_otlp_f" with ->get, driving the pending future's own loop; on the synchronous Langertha::Request::SyncHTTP fallback the future is already complete, so no event loop is created (Langertha::Role::AsyncHTTP).
Use this only when no event loop is already running. Inside an async context prefer "export_otlp_f".
SEE ALSO
Langertha::Runtime::Metrics - The parser this role drives
Langertha::Runtime::Metrics::OTLP - OTLP/HTTP JSON serializer used by "export_otlp_f"
Langertha::Runtime::Metrics::EngineContract - Per-engine wire contract
Langertha::Engine::vLLM - vLLM self-hosted engine (composes this role)
Langertha::Engine::SGLang - SGLang self-hosted engine (composes this role)
Langertha::Engine::LlamaCpp - llama.cpp server engine (composes this role)
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.