Skip to content

Commit fd74f8c

Browse files
committed
Add prometheus metric metadata
1 parent 18413ff commit fd74f8c

7 files changed

Lines changed: 165 additions & 5 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,18 @@ To query "version" label values for the "postgres" job:
134134
"version" | prometheus label values --url https://prometheus.example:9090/ 'job="postgres"'
135135
```
136136

137+
### Metric metadata
138+
139+
Retrieve metric metadata with:
140+
141+
```nushell
142+
prometheus metric metadata --url https://prometheus.example:9090/
143+
```
144+
145+
This may take some time, so supply a metric name as input or supply `--limit`
146+
to reduce the number of records retrieved. Use `--limit-per-metric` to reduce
147+
the number of metadata items retrieved per metric.
148+
137149
## Series
138150

139151
Retrieve series matching the given label set with:

src/client.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod label_names;
22
mod label_names_builder;
33
mod label_values;
44
mod label_values_builder;
5+
mod metric_metadata;
56
mod query_builder;
67
mod query_instant;
78
mod query_range;
@@ -13,6 +14,7 @@ pub use label_names::LabelNames;
1314
pub use label_names_builder::LabelNamesBuilder;
1415
pub use label_values::LabelValues;
1516
pub use label_values_builder::LabelValuesBuilder;
17+
pub use metric_metadata::MetricMetadata;
1618
use nu_protocol::{LabeledError, Span};
1719
pub use query_builder::QueryBuilder;
1820
pub use query_instant::QueryInstant;

src/client/metric_metadata.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use nu_protocol::{record, LabeledError, Span, Value};
2+
use prometheus_http_query::MetricMetadataQueryBuilder;
3+
4+
use super::Client;
5+
6+
pub struct MetricMetadata {
7+
builder: MetricMetadataQueryBuilder,
8+
span: Span,
9+
}
10+
11+
impl MetricMetadata {
12+
pub fn new(builder: MetricMetadataQueryBuilder, span: Span) -> Self {
13+
Self { builder, span }
14+
}
15+
16+
pub fn run(self) -> Result<Value, LabeledError> {
17+
let Self { ref builder, span } = self;
18+
19+
self.runtime()?.block_on(async {
20+
let metric_metadata = builder
21+
.clone()
22+
.get()
23+
.await
24+
.map_err(|error| self.labeled_error(error, span))?;
25+
26+
let record = metric_metadata
27+
.iter()
28+
.fold(record!(), |mut record, (metric, metadata)| {
29+
let metadata: Vec<_> =
30+
metadata.iter().map(|item| {
31+
let item = record! {
32+
"type" => Value::string(item.metric_type().to_string(), Span::unknown()),
33+
"help" => Value::string(item.help().to_string(), Span::unknown()),
34+
"unit" => Value::string(item.unit().to_string(), Span::unknown()),
35+
};
36+
37+
Value::record(item, Span::unknown())
38+
}).collect();
39+
40+
record.insert(metric, Value::list(metadata, Span::unknown()));
41+
42+
record
43+
});
44+
45+
Ok(Value::record(record, Span::unknown()))
46+
})
47+
}
48+
}
49+
50+
impl Client for MetricMetadata {}

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use client::Client;
77
use nu_plugin::{serve_plugin, JsonSerializer};
88
use prometheus::Prometheus;
99
use query::Query;
10+
use source::Source;
1011

1112
fn main() {
1213
serve_plugin(&Prometheus, JsonSerializer)

src/prometheus.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod label_names_command;
22
mod label_values_command;
3+
mod metric_metadata_command;
34
mod prometheus_command;
45
mod query_command;
56
mod query_range_command;
@@ -9,9 +10,10 @@ mod targets_command;
910

1011
use crate::prometheus::{
1112
label_names_command::LabelNamesCommand, label_values_command::LabelValuesCommand,
12-
prometheus_command::PrometheusCommand, query_command::QueryCommand,
13-
query_range_command::QueryRangeCommand, series_command::SeriesCommand,
14-
sources_command::SourcesCommand, targets_command::TargetsCommand,
13+
metric_metadata_command::MetricMetadataCommand, prometheus_command::PrometheusCommand,
14+
query_command::QueryCommand, query_range_command::QueryRangeCommand,
15+
series_command::SeriesCommand, sources_command::SourcesCommand,
16+
targets_command::TargetsCommand,
1517
};
1618
use nu_plugin::Plugin;
1719

@@ -23,6 +25,7 @@ impl Plugin for Prometheus {
2325
vec![
2426
Box::new(LabelNamesCommand),
2527
Box::new(LabelValuesCommand),
28+
Box::new(MetricMetadataCommand),
2629
Box::new(PrometheusCommand),
2730
Box::new(QueryCommand),
2831
Box::new(QueryRangeCommand),
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use crate::{client::MetricMetadata, Prometheus, Source};
2+
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
3+
use nu_protocol::{LabeledError, Signature, SyntaxShape, Type, Value};
4+
use prometheus_http_query::Client;
5+
6+
#[derive(Clone, Default)]
7+
pub struct MetricMetadataCommand;
8+
9+
impl SimplePluginCommand for MetricMetadataCommand {
10+
type Plugin = Prometheus;
11+
12+
fn name(&self) -> &str {
13+
"prometheus metric metadata"
14+
}
15+
16+
fn signature(&self) -> nu_protocol::Signature {
17+
Signature::build(self.name())
18+
.usage(self.usage())
19+
.named(
20+
"source",
21+
SyntaxShape::String,
22+
"Prometheus source to query",
23+
Some('s'),
24+
)
25+
.named(
26+
"url",
27+
SyntaxShape::String,
28+
"Prometheus source url to query",
29+
Some('u'),
30+
)
31+
.named(
32+
"limit",
33+
SyntaxShape::Int,
34+
"Maximum number of metrics to retrieve",
35+
None,
36+
)
37+
.named(
38+
"limit-per-metric",
39+
SyntaxShape::Int,
40+
"Maximum number of metadata to retrieve per metric",
41+
None,
42+
)
43+
.input_output_types(vec![
44+
(Type::Nothing, Type::record()),
45+
(Type::String, Type::record()),
46+
])
47+
}
48+
49+
fn usage(&self) -> &str {
50+
"Retrieve metric metadata"
51+
}
52+
53+
fn run(
54+
&self,
55+
_plugin: &Self::Plugin,
56+
engine: &EngineInterface,
57+
call: &EvaluatedCall,
58+
metric: &Value,
59+
) -> Result<Value, LabeledError> {
60+
let span = metric.span();
61+
62+
let client: Client = Source::from(call, engine)?.try_into()?;
63+
64+
let mut builder = client.metric_metadata();
65+
66+
if let Value::String { val: metric, .. } = metric {
67+
builder = builder.metric(metric);
68+
};
69+
70+
if let Some(limit) = call.get_flag::<i64>("limit")? {
71+
let limit = limit.try_into().map_err(|_| {
72+
let span = call.get_flag_value("limit").unwrap().span();
73+
74+
LabeledError::new("Limit too large").with_label("does not fit in i32", span)
75+
})?;
76+
77+
builder = builder.limit(limit);
78+
}
79+
80+
if let Some(limit_per_metric) = call.get_flag::<i64>("limit-per-metric")? {
81+
let limit_per_metric = limit_per_metric.try_into().map_err(|_| {
82+
let span = call.get_flag_value("limit-per-metric").unwrap().span();
83+
84+
LabeledError::new("Limit per metric too large")
85+
.with_label("does not fit in i32", span)
86+
})?;
87+
88+
builder = builder.limit_per_metric(limit_per_metric);
89+
}
90+
91+
MetricMetadata::new(builder, span).run()
92+
}
93+
}

src/prometheus/series_command.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::{
22
client::{SelectorParser, Series},
3-
source::Source,
4-
Prometheus,
3+
Prometheus, Source,
54
};
65
use chrono::{DateTime, FixedOffset};
76
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};

0 commit comments

Comments
 (0)