Skip to content

Exemplars/3445 shared contracts - #97

Merged
Gladorme merged 3 commits into
perses:mainfrom
julianocosta89:exemplars/3445-shared-contracts
Sep 8, 2026
Merged

Gladorme merged 3 commits into
perses:mainfrom
julianocosta89:exemplars/3445-shared-contracts

Conversation

@julianocosta89

@julianocosta89 julianocosta89 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

feat: add exemplars to TimeSeriesData contract

Part of #3445 (cross-repo effort — this PR closes nothing on its own; see "Cross-repo plan" below).

Summary

Adds exemplar support to the TimeSeriesData contract so time-series query plugins can attach exemplar data to their results. This is the first, contracts-only step toward
displaying Prometheus exemplars in Perses (diamond markers on time-series charts with metadata on click), as designed by @AntoineThebaud in #3445.

The new types mirror the shape of the Prometheus query_exemplars response:

export interface Exemplar {                                                                                                                                                           
  labels: Labels;                                                                                                                                                                     
  value: number;                                                                                                                                                                      
  timestamp: UnixTimeMs;                                                                                                                                                              
}                                                                                                                                                                                     
                                                                                                                                                                                      
export interface TimeSeriesExemplars {                                                                                                                                                
  seriesLabels: Labels;                                                                                                                                                               
  exemplars: Exemplar[];                                                                                                                                                              
}                                                                                                                                                                                     

And a new optional field on the existing contract:

 export interface TimeSeriesData {                                                                                                                                                    
   timeRange?: AbsoluteTimeRange;                                                                                                                                                     
   stepMs?: number;                                                                                                                                                                   
   series: TimeSeries[];                                                                                                                                                              
+  exemplars?: TimeSeriesExemplars[];                                                                                                                                                 
   metadata?: TimeSeriesMetadata;                                                                                                                                                     
 }                                                                                                                                                                                    

Design decisions

  • Purely additive / backward compatibleexemplars? is optional and no existing types changed, so query plugins (ClickHouse, GreptimeDB, …), panels, and consumers of
    @perses-dev/spec are unaffected and require no changes.
  • seriesLabels keying — exemplars are grouped per series (keyed by the series labels), matching how Prometheus scopes each exemplar set to the series it was scraped from.
  • UnixTimeMs timestamps — follows the existing spec convention (milliseconds). Datasource plugins are responsible for converting from the source format (e.g. Prometheus
    returns seconds).
  • Plain number values — no unit/formatting assumptions at the contract level.

Changes

  • ts/src/dashboard/query-type/time-series-data.ts — add Exemplar, TimeSeriesExemplars, and optional TimeSeriesData.exemplars field (+13/−1 lines; single file)

Testing

  • npm run type-check, oxlint, oxfmt, and the full vitest suite (14 tests) pass in ts/
  • No existing types were modified — purely additive contract, no migration needed

Cross-repo plan

This is PR 1 of 3 for the exemplars feature, released in dependency order to keep everything backward compatible:

# Repo Change
- perses/spec (this PR) TimeSeriesData exemplars contract
perses/shared#278 perses/shared ExemplarMetadataDialog component in @perses-dev/components
perses/plugins#799 and perses/plugins#800 perses/plugins Prometheus datasource exemplars spec section + queryExemplars() client method, exemplars fetch in PrometheusTimeSeriesQuery, diamond markers in TimeSeriesChart

Links to the follow-up PRs: (to be filled once opened)

Notes for reviewers

The exemplar labels typically include identifying metadata such as a trace ID (e.g. from trace_id exemplar config in Prometheus), which downstream UI will use to display/link
exemplar metadata — trace-to-logs linking is explicitly out of scope for the MVP (see #3445).

@Nexucis

Nexucis commented Sep 7, 2026

Copy link
Copy Markdown
Member

Hi @julianocosta89
Thanks for helping us on that topic !

Can you signoff your commit in every PRs you have opened.

@Gladorme @AntoineThebaud as you are much more knowledgable than me on that topic, can you help @julianocosta89 to move forward ? Thank you 🙏

Extend the time-series query data model with an optional `exemplars`
field mirroring the Prometheus query_exemplars response shape
(TimeSeriesExemplars: { seriesLabels, exemplars: [{ labels, value,
timestamp }] }).

The field is optional so existing query plugins remain backward
compatible. Timestamps are UnixTimeMs (ms) and values are numbers;
datasource plugins are responsible for converting raw Prometheus
responses (string values, second-based timestamps).

Related to perses/perses#3445

Signed-off-by: Juliano Costa <juliano.costa@datadoghq.com>
Signed-off-by: Juliano Costa <juliano.costa@datadoghq.com>
@julianocosta89
julianocosta89 force-pushed the exemplars/3445-shared-contracts branch from b55cfba to 5f1a72c Compare September 8, 2026 07:30
@julianocosta89

Copy link
Copy Markdown
Contributor Author

Thank you @Nexucis!
I signed-off all commits

export interface Exemplar {
labels: Labels;
value: number;
timestamp: UnixTimeMs;

@Gladorme Gladorme Sep 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I see in Prometheus HTTP API doc (https://prometheus.io/docs/prometheus/latest/querying/api/#querying-exemplars), the timestamp is in seconds, not milliseconds

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's fine to keep milliseconds, we just need to be careful when implementing prometheus datasource

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I fix this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we can keep ms, but in Prometheus datasource implem, we need to do a * 1000 😄

@Gladorme
Gladorme added this pull request to the merge queue Sep 8, 2026
Merged via the queue into perses:main with commit 90a9876 Sep 8, 2026
14 checks passed
@julianocosta89
julianocosta89 deleted the exemplars/3445-shared-contracts branch September 8, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants