Skip to content

Commit 468d525

Browse files
authored
enhancement(api): Support metric events in vector tap (#11201)
* Add skeleton for sending metrics from TapSink * Add Metric to graphQL output events subscription * Update help message * Allow querying tags * Support querying additional metric information Adds name, namespace, kind, value type, value * Update cli cue wording * Fix existing test to account for metrics * Add integration test for tapping metric event * Make metric kind a graphql enum * Model tag as GraphQL object * Update generated schema * Use API specific MetricKind This avoids taking on an otherwise unnecessary graphql dependency in vector-core::event
1 parent 7f3775b commit 468d525

File tree

10 files changed

+461
-33
lines changed

10 files changed

+461
-33
lines changed

lib/vector-api-client/graphql/schema.json

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2691,6 +2691,238 @@
26912691
"enumValues": null,
26922692
"possibleTypes": null
26932693
},
2694+
{
2695+
"kind": "OBJECT",
2696+
"name": "Metric",
2697+
"description": "Metric event with fields for querying metric data",
2698+
"fields": [
2699+
{
2700+
"name": "componentId",
2701+
"description": "Id of the component associated with the metric event",
2702+
"args": [],
2703+
"type": {
2704+
"kind": "NON_NULL",
2705+
"name": null,
2706+
"ofType": {
2707+
"kind": "SCALAR",
2708+
"name": "String",
2709+
"ofType": null
2710+
}
2711+
},
2712+
"isDeprecated": false,
2713+
"deprecationReason": null
2714+
},
2715+
{
2716+
"name": "timestamp",
2717+
"description": "Metric timestamp",
2718+
"args": [],
2719+
"type": {
2720+
"kind": "SCALAR",
2721+
"name": "DateTime",
2722+
"ofType": null
2723+
},
2724+
"isDeprecated": false,
2725+
"deprecationReason": null
2726+
},
2727+
{
2728+
"name": "name",
2729+
"description": "Metric name",
2730+
"args": [],
2731+
"type": {
2732+
"kind": "NON_NULL",
2733+
"name": null,
2734+
"ofType": {
2735+
"kind": "SCALAR",
2736+
"name": "String",
2737+
"ofType": null
2738+
}
2739+
},
2740+
"isDeprecated": false,
2741+
"deprecationReason": null
2742+
},
2743+
{
2744+
"name": "namespace",
2745+
"description": "Metric namespace",
2746+
"args": [],
2747+
"type": {
2748+
"kind": "SCALAR",
2749+
"name": "String",
2750+
"ofType": null
2751+
},
2752+
"isDeprecated": false,
2753+
"deprecationReason": null
2754+
},
2755+
{
2756+
"name": "kind",
2757+
"description": "Metric kind",
2758+
"args": [],
2759+
"type": {
2760+
"kind": "NON_NULL",
2761+
"name": null,
2762+
"ofType": {
2763+
"kind": "ENUM",
2764+
"name": "MetricKind",
2765+
"ofType": null
2766+
}
2767+
},
2768+
"isDeprecated": false,
2769+
"deprecationReason": null
2770+
},
2771+
{
2772+
"name": "valueType",
2773+
"description": "Metric type",
2774+
"args": [],
2775+
"type": {
2776+
"kind": "NON_NULL",
2777+
"name": null,
2778+
"ofType": {
2779+
"kind": "SCALAR",
2780+
"name": "String",
2781+
"ofType": null
2782+
}
2783+
},
2784+
"isDeprecated": false,
2785+
"deprecationReason": null
2786+
},
2787+
{
2788+
"name": "value",
2789+
"description": "Metric value in human readable form",
2790+
"args": [],
2791+
"type": {
2792+
"kind": "NON_NULL",
2793+
"name": null,
2794+
"ofType": {
2795+
"kind": "SCALAR",
2796+
"name": "String",
2797+
"ofType": null
2798+
}
2799+
},
2800+
"isDeprecated": false,
2801+
"deprecationReason": null
2802+
},
2803+
{
2804+
"name": "tags",
2805+
"description": "Metric tags",
2806+
"args": [],
2807+
"type": {
2808+
"kind": "LIST",
2809+
"name": null,
2810+
"ofType": {
2811+
"kind": "NON_NULL",
2812+
"name": null,
2813+
"ofType": {
2814+
"kind": "OBJECT",
2815+
"name": "MetricTag",
2816+
"ofType": null
2817+
}
2818+
}
2819+
},
2820+
"isDeprecated": false,
2821+
"deprecationReason": null
2822+
},
2823+
{
2824+
"name": "string",
2825+
"description": "Metric event as an encoded string format",
2826+
"args": [
2827+
{
2828+
"name": "encoding",
2829+
"description": null,
2830+
"type": {
2831+
"kind": "NON_NULL",
2832+
"name": null,
2833+
"ofType": {
2834+
"kind": "ENUM",
2835+
"name": "EventEncodingType",
2836+
"ofType": null
2837+
}
2838+
},
2839+
"defaultValue": null
2840+
}
2841+
],
2842+
"type": {
2843+
"kind": "NON_NULL",
2844+
"name": null,
2845+
"ofType": {
2846+
"kind": "SCALAR",
2847+
"name": "String",
2848+
"ofType": null
2849+
}
2850+
},
2851+
"isDeprecated": false,
2852+
"deprecationReason": null
2853+
}
2854+
],
2855+
"inputFields": null,
2856+
"interfaces": [],
2857+
"enumValues": null,
2858+
"possibleTypes": null
2859+
},
2860+
{
2861+
"kind": "ENUM",
2862+
"name": "MetricKind",
2863+
"description": null,
2864+
"fields": null,
2865+
"inputFields": null,
2866+
"interfaces": null,
2867+
"enumValues": [
2868+
{
2869+
"name": "INCREMENTAL",
2870+
"description": "Incremental metrics update previous values",
2871+
"isDeprecated": false,
2872+
"deprecationReason": null
2873+
},
2874+
{
2875+
"name": "ABSOLUTE",
2876+
"description": "Absolute metrics set the reference value for future updates",
2877+
"isDeprecated": false,
2878+
"deprecationReason": null
2879+
}
2880+
],
2881+
"possibleTypes": null
2882+
},
2883+
{
2884+
"kind": "OBJECT",
2885+
"name": "MetricTag",
2886+
"description": null,
2887+
"fields": [
2888+
{
2889+
"name": "key",
2890+
"description": "Metric tag key",
2891+
"args": [],
2892+
"type": {
2893+
"kind": "NON_NULL",
2894+
"name": null,
2895+
"ofType": {
2896+
"kind": "SCALAR",
2897+
"name": "String",
2898+
"ofType": null
2899+
}
2900+
},
2901+
"isDeprecated": false,
2902+
"deprecationReason": null
2903+
},
2904+
{
2905+
"name": "value",
2906+
"description": "Metric tag value",
2907+
"args": [],
2908+
"type": {
2909+
"kind": "NON_NULL",
2910+
"name": null,
2911+
"ofType": {
2912+
"kind": "SCALAR",
2913+
"name": "String",
2914+
"ofType": null
2915+
}
2916+
},
2917+
"isDeprecated": false,
2918+
"deprecationReason": null
2919+
}
2920+
],
2921+
"inputFields": null,
2922+
"interfaces": [],
2923+
"enumValues": null,
2924+
"possibleTypes": null
2925+
},
26942926
{
26952927
"kind": "INTERFACE",
26962928
"name": "MetricType",
@@ -2898,6 +3130,11 @@
28983130
"name": "Log",
28993131
"ofType": null
29003132
},
3133+
{
3134+
"kind": "OBJECT",
3135+
"name": "Metric",
3136+
"ofType": null
3137+
},
29013138
{
29023139
"kind": "OBJECT",
29033140
"name": "EventNotification",

lib/vector-api-client/graphql/subscriptions/output_events_by_component_id_patterns.graphql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ subscription OutputEventsByComponentIdPatternsSubscription(
88
timestamp
99
string(encoding: $encoding)
1010
}
11+
... on Metric {
12+
componentId
13+
timestamp
14+
string(encoding: $encoding)
15+
}
1116
... on EventNotification {
1217
pattern
1318
notification

lib/vector-api-client/src/gql/tap.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ impl From<TapEncodingFormat>
5151
}
5252

5353
impl output_events_by_component_id_patterns_subscription::OutputEventsByComponentIdPatternsSubscriptionOutputEventsByComponentIdPatterns {
54-
pub fn as_log(
54+
pub fn as_string(
5555
&self,
56-
) -> Option<&output_events_by_component_id_patterns_subscription::OutputEventsByComponentIdPatternsSubscriptionOutputEventsByComponentIdPatternsOnLog>{
56+
) -> Option<&str>{
5757
match self {
58-
output_events_by_component_id_patterns_subscription::OutputEventsByComponentIdPatternsSubscriptionOutputEventsByComponentIdPatterns::Log(ev) => Some(ev),
58+
output_events_by_component_id_patterns_subscription::OutputEventsByComponentIdPatternsSubscriptionOutputEventsByComponentIdPatterns::Log(ev) => Some(ev.string.as_ref()),
59+
output_events_by_component_id_patterns_subscription::OutputEventsByComponentIdPatternsSubscriptionOutputEventsByComponentIdPatterns::Metric(ev) => Some(ev.string.as_ref()),
5960
_ => None,
6061
}
6162
}

src/api/schema/events/metric.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use async_graphql::{Enum, Object};
2+
use chrono::{DateTime, Utc};
3+
4+
use super::EventEncodingType;
5+
use crate::{
6+
config::OutputId,
7+
event::{self},
8+
};
9+
10+
#[derive(Debug, Clone)]
11+
pub struct Metric {
12+
output_id: OutputId,
13+
event: event::Metric,
14+
}
15+
16+
impl Metric {
17+
pub const fn new(output_id: OutputId, event: event::Metric) -> Self {
18+
Self { output_id, event }
19+
}
20+
}
21+
22+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
23+
enum MetricKind {
24+
/// Incremental metrics update previous values
25+
Incremental,
26+
/// Absolute metrics set the reference value for future updates
27+
Absolute,
28+
}
29+
30+
impl From<event::MetricKind> for MetricKind {
31+
fn from(kind: event::MetricKind) -> Self {
32+
match kind {
33+
event::MetricKind::Incremental => Self::Incremental,
34+
event::MetricKind::Absolute => Self::Absolute,
35+
}
36+
}
37+
}
38+
39+
struct MetricTag {
40+
key: String,
41+
value: String,
42+
}
43+
44+
#[Object]
45+
impl MetricTag {
46+
/// Metric tag key
47+
async fn key(&self) -> &str {
48+
self.key.as_ref()
49+
}
50+
51+
/// Metric tag value
52+
async fn value(&self) -> &str {
53+
self.value.as_ref()
54+
}
55+
}
56+
57+
#[Object]
58+
/// Metric event with fields for querying metric data
59+
impl Metric {
60+
/// Id of the component associated with the metric event
61+
async fn component_id(&self) -> &str {
62+
self.output_id.component.id()
63+
}
64+
65+
/// Metric timestamp
66+
async fn timestamp(&self) -> Option<&DateTime<Utc>> {
67+
self.event.data().timestamp().as_ref()
68+
}
69+
70+
/// Metric name
71+
async fn name(&self) -> &str {
72+
self.event.name()
73+
}
74+
75+
/// Metric namespace
76+
async fn namespace(&self) -> Option<&str> {
77+
self.event.namespace()
78+
}
79+
80+
/// Metric kind
81+
async fn kind(&self) -> MetricKind {
82+
self.event.kind().into()
83+
}
84+
85+
/// Metric type
86+
async fn value_type(&self) -> &str {
87+
self.event.value().as_name()
88+
}
89+
90+
/// Metric value in human readable form
91+
async fn value(&self) -> String {
92+
self.event.value().to_string()
93+
}
94+
95+
/// Metric tags
96+
async fn tags(&self) -> Option<Vec<MetricTag>> {
97+
self.event.tags().map(|tags| {
98+
tags.iter()
99+
.map(|(key, value)| MetricTag {
100+
key: key.to_owned(),
101+
value: value.to_owned(),
102+
})
103+
.collect()
104+
})
105+
}
106+
107+
/// Metric event as an encoded string format
108+
async fn string(&self, encoding: EventEncodingType) -> String {
109+
match encoding {
110+
EventEncodingType::Json => serde_json::to_string(&self.event)
111+
.expect("JSON serialization of metric event failed. Please report."),
112+
EventEncodingType::Yaml => serde_yaml::to_string(&self.event)
113+
.expect("YAML serialization of metric event failed. Please report."),
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)