Page MenuHomePhabricator

Set up new prometheus label structure for the API monitoring
Open, HighPublic8 Estimated Story PointsBUG REPORT

Description

As we're thinking and exploring ways to improve monitoring of the API modules and the ability to enable teams to create their alerts when things break, we need to make sure we're providing a sensible way to get specific monitoring so we can create specific alarms.

The problem

Prometheus labels can by utilized in grafana for dynamic graphs, data "cuts" and focused alerts; most of those are done through targeted label values and regular expressions for targeting pieces of the label properly. The main property for this at the moment is the "path" property, that reflect the endpoint paths, and as a result, they aren't always consistent in allowing us to "cut" the data consistently with information we need.

For example, we currently have prometheus the "path" label that present the REST module paths, and are therefore inconsistent with the way they display the name and version of the module:

  • campaignevents_v0_event_registration_id and site_v1_sitemap_indexId (version after module name)
  • flaggedrevs_internal_diffheader_oldId_newId (no version at all)
  • v1_file_title (version as the module name)

etc.

There are multiple tickets trying to resolve the problem of endpoint path consistency, and while we should resolve this issue, the question of endpoint paths structure (while related to monitoring) is not the same as the structure of monitoring labels that are meant to give us information about what is happening and where in the code.

Reframing the problem

I offer we separate this problem -- endpoints paths consistency vs prometheus representation of endpoints -- and first tackle the latter (prometheus labels and grafana graphs) so we can consistently have enough standardized information to allow for powerful graphs AND alerts.

For this, we need to understand and decide not quite what path/structure the labels should have, but rather what information/data we want to have in them. This should potentially be a little less complicated than thinking about the full endpoint pathways because unlike the endpoint paths, prometheus labels aren't meant for public consumption -- and we "just" need to use them for data management, monitoring, graphs and alerts.

The proposed solution

The solution offered here will solve the need to monitor API modules and provide focused alerts in multiple "cuts" of the data (module types, module names, status codes, etc) across the Wikimedia API systems. It is also designed to give API owners the power to monitor specific code-paths on top of the "basic" request/response monitoring that we currently have.

These types of metrics will be agnostic to the endpoint paths and are aimed to reflect module groupings and behaviors, which means they should not be affected by specific endpoint changes or even where the API module is (in REST or ACTION) api, and can therefore allow feature teams to produce targeted Grafana graphs and alerts.

Details

The patch (attached below) provides the following behavior:

Metrics representation

It creates new metrics:

  • rest_api_modules_hits_total (counter)
  • rest_api_modules_latency_seconds (observable)

A followup patch creates two more for Action API:

  • action_api_modules_hits_total (counter)
  • action_api_modules_latency_seconds (observable)

These new metrics share these labels:

  • api_type: The type of API endpoint that the metric reflects; currently REST_API or ACTION_API (and can be extended further if needed)
  • api_module: The module name (a logical collection of endpoints or behaviors, defined by the API module owner)
  • api_scope: Whether the module is public or private (internal)
  • path: (existing label) will provide a string that reflects data about the module and sub-behavior, if the API owner decided to monitor a code-path
  • method (existing label) the method of the request (GET/PUT etc)
  • status: The status code of the result (200/400/etc)

These details will be automatically collected on execution of the endpoint, and use the programmatic details we already have, though those details can also be overridden in the code.

Providing consistency in the data

This approach will ensure we get at least closer to providing consistent data representation so we can "cut" elements in grafana to produce consistent dashboards and alerts.

  • The new labels provide easier way to fetch module-related information (rather than having to use regex on inconsistent "path"-like values that exist today)
  • The label values are as consistent as possible between modules and endpoints
  • The label values are as conceptually consistent as possible between REST and ACTION apis.

Assumptions and decisions

We are aiming to provide a consistent experience for the monitoring of our API ecosystem. However, there are inherent inconsistencies -- especially conceptually -- between REST and ACTION that we had to make some assumptions and decisions on how to provide a helpful consolidation:

Conceptual meaning of "Module"

The concept of "module" is different between Action and Rest APIs. In REST, a module is a collection of endpoints (Handlers) and in ActionAPI a "module" is a (for the most part) a single endpoint. Action API does not have a concept of 'groups' of related endpoints for the moment, though there are some attempts to define module information that provides some helpful groupings for documentation and behavior.

Given that, and the fact that we would like to consolidate on the conceptual meaning that an API Module is a *collection* of endpoints, the labels would be represented as:

labelREST APIACTION API
api_moduleThe module nameThe NAMESPACE of the module class
api_endpointThe handler ID (from OpenAPISpec)The module name

The NAMESPACE isn't an ideal representation, but when we fetch the metrics, it can give us a general grouping of where the API module comes from within extensions, and so can still provide some basic grouping while we figure out a more systemic way to document and define ActionAPI module groupings later in the system.

Status code inconsistency

The label status already exists in other metrics, and will be reused here to represent the status code of the response. However, there is another inconsistency here between REST and ACTION APIs, both practically and conceptually.

REST API represents the response fairly straightforward. However, Action API usually converts failures into a success so it can send a user a successful JSON response with a failure message. We can't quite rely on the response status, then, but the action API code does collect any failure codes it encountered for the logs -- and that can be used for the status label.

The only caveat here, is that ActionAPI seems to enable (and expect?) multiple status codes with an underscore separator. We will store those in the status label. This will still allow us to extract all metrics that have a certain code (using RegEx) but will still result in a slight mismatch between Rest and Action in the raw values.

Event Timeline

Change #1180199 had a related patch set uploaded (by Mooeypoo; author: Mooeypoo):

[mediawiki/core@master] [PoC/DO NOT MERGE] Module metrics for Wikimedia REST API

https://gerrit.wikimedia.org/r/1180199

HCoplin-WMF renamed this task from Explore new prometheus label structure for the API monitoring to [PoC] Explore new prometheus label structure for the API monitoring.Aug 21 2025, 4:09 PM
HCoplin-WMF changed the task status from Open to In Progress.
HCoplin-WMF assigned this task to Mooeypoo.
HCoplin-WMF triaged this task as High priority.
HCoplin-WMF set the point value for this task to 8.
Mooeypoo updated the task description. (Show Details)

Notes after some chats (thanks @Catrope for the insights!):

  • The api_scope doesn't need to be manually given on registration, especially for the Action API, since it exists already in the definition of the module. I imagine it probably exists in the REST module definition too, and if I can access it in-code it can be set automatically without having it in the metadata registration, for consistency.
  • The api_module might actually be misleading here. We don't QUITE care about the official module name, this is more a way to group API modules together for conceptual or thematic/feature fetching. For example, ApiPatrol and ApiProtect are both Moderation conceptual groupings, with different "sub module" or "functionality". Setting it up as both belonging to that group would enable us to look at "all moderation modules that have errors" or something similar, which can enable the feature teams to control the specific/broad purpose of alerts. If we want to do it that way, then:
    • We should change the name of the label to api_group or something like that so it's not confusing.
    • We could store the actual "proper" name of the module (given by path or action) in another label, so we can query either.

Notes from chat with @daniel, @Mooeypoo and myself:

Consider if the labelling can be set declaratively and automatically, without requiring a handler to opt-in (by calling functions in class constructors). This would reduce friction and mistakes (i.e. you can't do it "wrong" in a way that's right elsewhere, or otherwise hard to notice), and avoids introducing another grouping mechanism that is almost-the-same-but-not-quite as other grouping mechanisms.

For the REST API, we could leverage the built-in grouping mechanism ("modules", with one or more route handlers). This is in the REST registry, and can be passed down as-needed.

For the Action API, actions have no mandated prefix or grouping. However, we do have the ExtensionRegistry, which knows which extension registers a given API (from the APIModules attribute in extension.json). We leverage this in other places already to automate instrumentation. For example: ResourceLoader's QUnitTestModule attribute (in extractResourceLoaderModules, each test suite is automatically labelled with the extension name), and HookHandler (in extractHooks, each hook handler is attributed to the extension name to ease stats/debugging). We could do something similar here by letting ApiModuleManager::addModules take a component name. Then, most of the registered APIModules entries (which come from global scope today. in ApiMain) can instead be consumed from ExtensionRegistry->getAttribute. See also ServiceWiring.php#ResourceLoader, WikimediaMaintenance/blameStartupRegistry.php, and other services that call ExtensionRegistry->getAttribute.

Naming: The word "module" may be confusing because "API module" is an established term to refer to one action in the Action API, rather than a grouping of one or more routes. The word "component" might work. Besides core components, it would correspond 1:1 with extensions.

It creates two new metrics:

  • api_modules_hits_total
  • api_modules_latency_seconds

In Prometheus (and Graphite/Statsd) timers are a superset of counters (i.e. every timer is a counter), so we don't need a separate hit counter.

It may be worth considering if we can one or two labels to the existing mediawiki_api_executeTiming_seconds and mediawiki_rest_api_latency_seconds metrics instead.

For example, if we want to measure the latency of the "getRevisionText" method within CompareHandler (REST), we could add a latency metrics call to the method: […]

We have numerous profiling tools in MediaWiki core.

  • MW-StatsLib (docs, Codesearch). These create Prometheus histograms to embed in Grafana dashboards, and are widely adopted in core and in extensions. I would recommend against wrapping this but instead offer a way to augment these without standing between the code and the metric. That way, dependency injection and component prefixes continue to work automatically, and developers benefit from greater familiarity and feature parity.
  • OpenTelemetry tracing spans (docs, Codesearch), which are aggregated in the Jaeger interface at trace.wikimedia.org.
  • Flame Graphs (automatic, no manual instrumentation)
  • More

For APIs specifically, we also have the Grafana: MediaWiki Entrypoint Profiling dashboard. This automatically breaks down any REST API, Action API, or SpecialPage into high-level components. For example, the api_opensearch handler spends 53% in the CirrusSeach extension (no surprise), but also 11% in the PageImages extension, and 3% in the core Permissions component. For function-level breakdown, one would browse the Flame Graph (prod aggregate) or Excimer UI profile (ad-hoc).

For code that involves caching (a lot in MediaWiki!) we also automatically receive StatsLib spans from WANObjectCache. For example, an important part of the CentralNotice API module (and its ResourceLoader module) is the fetching and computing of the banner list. This is similar to your getRevisionText example. The CentralNotice extension caches this via the CentralNoticeChoiceData keygroup (Codesearch). The span for this is automatically visualised, including latency heatmap and call rate, on the Grafana: WANObjectCache dashboard. There are similarly spans for parts of AbuseFilter, Campaigns, Echo, Gadgets, Wikibase, etc.

Daniel noted that we want to discourage REST API and Action API handlers having code that is "worthy" of breaking down beyond the handler itself, so we might not want to make this too easy. The number of "legacy" Action API modules with lots of business logic has declined over the past decade, and the majority of the codebase now lives outside these handlers. Even so, tools like Flame Graphs, Entrypoint Profiling, and WANObjectCache, automatically provide detailed telemetry on these as well, and maintainers can add more as-needed. StatsLib and OpenTelemetry are both high-level abstractions optimised to ease adoption, with each instrument requiring only one statement.

It creates two new metrics:

  • api_modules_hits_total (counter)
  • api_modules_latency_seconds (observable)

IIUC, these are metrics specifically about MediaWiki HTTP APIs, yes? If so, suggestion:

Consider prefixing these with something like mediawiki_http_, so as not to be confused with other APIs when discovering existent metrics.

It creates two new metrics:

  • api_modules_hits_total (counter)
  • api_modules_latency_seconds (observable)

IIUC, these are metrics specifically about MediaWiki HTTP APIs, yes? If so, suggestion:

Consider prefixing these with something like mediawiki_http_, so as not to be confused with other APIs when discovering existent metrics.

That's a good point; the existent metrics have a structure like mediawiki_rest_api_latency_seconds (the ActionAPI seems to have more obscure names, but those aren't yet coming from the application layer). I am consolidating things with the mediawiki_api_modules_hit_total and mediawiki_api_modules_latency (adding "modules" there to note the purpose here, but it might be wise to add http in there like mediawiki_http_api_modules_latency or something.

the ActionAPI seems to have more obscure names, but those aren't yet coming from the application layer

The action API does define these metrics in the application layer, e.g. api_executeTiming_seconds comes from https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/master/includes/api/ApiMain.php#963 and api_query_executeTiming_seconds from https://gerrit.wikimedia.org/g/mediawiki/core/+/e099aa914cce56fd863cee40a230a3edbaee0619/includes/api/ApiQuery.php

the ActionAPI seems to have more obscure names, but those aren't yet coming from the application layer

The action API does define these metrics in the application layer, e.g. api_executeTiming_seconds comes from https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/master/includes/api/ApiMain.php#963 and api_query_executeTiming_seconds from https://gerrit.wikimedia.org/g/mediawiki/core/+/e099aa914cce56fd863cee40a230a3edbaee0619/includes/api/ApiQuery.php

Yeah, I'm adding the unified metrics right below it.

Change #1186560 had a related patch set uploaded (by Mooeypoo; author: Mooeypoo):

[mediawiki/core@master] [WIP] Implement unified metrics for the Action API

https://gerrit.wikimedia.org/r/1186560

Change #1180199 merged by jenkins-bot:

[mediawiki/core@master] Unified module metrics for MediaWiki REST API

https://gerrit.wikimedia.org/r/1180199

Mooeypoo renamed this task from [PoC] Explore new prometheus label structure for the API monitoring to Set up new prometheus label structure for the API monitoring.Sep 15 2025, 4:33 PM
Mooeypoo updated the task description. (Show Details)

Change #1186560 merged by jenkins-bot:

[mediawiki/core@master] Implement unified metrics for the Action API

https://gerrit.wikimedia.org/r/1186560

HCoplin-WMF subscribed.

Just noting that I pulled this through to our next sprint since you weren't able to demo and the team would love to see more details :)

tappof subscribed.

Hello folks,

We had to disable scraping of these metrics (mediawiki_action_api_modules_latency_.*) due to high cardinality.
You can find more details in T410152: Disk space saturation (/srv) on Titan hosts.

I’m reopening this task to keep you informed.

Thank you.

Change #1233199 had a related patch set uploaded (by Mooeypoo; author: Mooeypoo):

[mediawiki/core@master] Remove latency logging from the new unified metrics for ActionAPI

https://gerrit.wikimedia.org/r/1233199

Change #1233199 merged by jenkins-bot:

[mediawiki/core@master] Remove latency logging from the new unified metrics for ActionAPI

https://gerrit.wikimedia.org/r/1233199