Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2e9f378
chore(buffers): replace string metric prefixes with typed BufferChann…
thomasqueirozb May 15, 2026
8ed979c
fmt
thomasqueirozb May 15, 2026
169c4ff
fix(buffers): update test usages of ChannelMetricMetadata to use Buff…
thomasqueirozb May 15, 2026
19e994f
fix(buffers): import BufferChannelKind in test module
thomasqueirozb May 15, 2026
61468e9
fmt
thomasqueirozb May 15, 2026
9446e03
chore(metrics): add rustdoc and Configurable derive to metric name enums
thomasqueirozb May 15, 2026
d9e5acb
chore(metrics): generate metric descriptions from docs.json via jq
thomasqueirozb May 15, 2026
dc445a5
chore(metrics): generate internal metric descriptions via vdev build …
thomasqueirozb May 15, 2026
770c312
chore(metrics): merge #25445, add deprecated variants, extend configu…
thomasqueirozb May 15, 2026
1f26375
fix(vdev): use write! instead of push_str(&format!()) to satisfy clippy
thomasqueirozb May 18, 2026
b99838f
fix(docs): restore internal_metrics source description removed by mis…
thomasqueirozb May 18, 2026
3c241d0
chore(metrics): add type to generated metric descriptions, restore st…
thomasqueirozb May 18, 2026
238eced
chore(metrics): add default_namespace to generated metric descriptions
thomasqueirozb May 18, 2026
76f6b26
chore(metrics): add tags to generated metric descriptions
thomasqueirozb May 18, 2026
35bea48
fix(docs): remove empty metric blocks from internal_metrics.cue
thomasqueirozb May 18, 2026
7291028
chore(metrics): use Rust constants for tag sets, encode all complex t…
thomasqueirozb May 18, 2026
b45fc6d
chore(metrics): move remaining non-enum metrics to Rust enums, intern…
thomasqueirozb May 18, 2026
38fec43
fix(vdev): escape newlines in descriptions before writing CUE string …
thomasqueirozb May 18, 2026
40abd88
Regenerate website/cue/reference/generated/internal_metric_descriptio…
thomasqueirozb May 18, 2026
d47eeb6
chore(metrics): replace raw CUE tag strings with typed TagSet struct …
thomasqueirozb May 18, 2026
35c5871
chore(metrics): replace TagSet abstraction with serde_json::json!() f…
thomasqueirozb May 18, 2026
a8cd2c3
chore(metrics): define all tag groups in Rust using std::sync::LazyLo…
thomasqueirozb May 18, 2026
20eed32
fix(docs): restore collect features and how_it_works section accident…
thomasqueirozb May 18, 2026
ddf1b1c
chore(metrics): inline all single-use tag field helpers
thomasqueirozb May 18, 2026
6fe26c1
chore(metrics): accept &* exprs in macro, use &*LAZY for annotation s…
thomasqueirozb May 18, 2026
539fbf6
chore(metrics): remove TagSet — all tag sets are now pub static LazyL…
thomasqueirozb May 18, 2026
8e22c50
chore(metrics): add merge() helper, inline SQS_S3_IGNORED_TOTAL_TAGS …
thomasqueirozb May 18, 2026
49c6ae7
chore(metrics): inline 7 single-use tag statics with merge()
thomasqueirozb May 18, 2026
1aa3bb0
chore(metrics): introduce merge_lazy! macro, rewrite all statics with it
thomasqueirozb May 18, 2026
4aec549
chore(metrics): replace merge_lazy! macro with merge_lazy() function
thomasqueirozb May 18, 2026
205481e
Remove whitespace
thomasqueirozb May 18, 2026
acdd77e
Fix whitespace
thomasqueirozb May 18, 2026
e5e870f
fix(vdev): warn and skip instead of falling back to stale _component_…
thomasqueirozb May 18, 2026
5269019
fix(metrics): add docs::tags annotations to all 90 remaining unannota…
thomasqueirozb May 18, 2026
ac0a975
fix(metrics): remove ghost metric CUE references, fix websocket_serve…
thomasqueirozb May 18, 2026
0de8220
fmt cue
thomasqueirozb May 18, 2026
164b216
Remove json_tags_to_cue, add examples, and use cue import
thomasqueirozb May 18, 2026
135d91e
Fix description generation and wrongly updated descriptions
thomasqueirozb May 18, 2026
47cd9cb
Add deprecation notice to buffer-max-byte-size/buffer-max-event-size
thomasqueirozb May 18, 2026
71bc432
fmt
thomasqueirozb May 18, 2026
f64f3e8
fix(metrics): correct descriptions and add missing tags/metrics to si…
thomasqueirozb May 18, 2026
d143e7a
fix(metrics): correct inaccurate metric descriptions
thomasqueirozb May 18, 2026
e140e7f
Refactor match into kind-only/kind + limit matches
thomasqueirozb May 18, 2026
87c287c
Add transform test
thomasqueirozb May 18, 2026
bbaeeef
Merge remote-tracking branch 'origin/chore/type-safe-buffer-metric-na…
thomasqueirozb May 18, 2026
5b19561
Merge branch 'master' into thomasqueirozb/25374-metric-name-docs
thomasqueirozb May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
589 changes: 584 additions & 5 deletions lib/vector-common/src/internal_event/metric_name.rs

Large diffs are not rendered by default.

230 changes: 230 additions & 0 deletions lib/vector-common/src/internal_event/metric_tags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
use serde_json::{Value, json};
use std::sync::LazyLock;

/// Clones `base` (a `LazyLock<Value>`) and inserts all fields from `extra`.
///
/// Intended for:
/// - static initializers: `LazyLock::new(|| merge_lazy(&BASE, json!({...})))`
/// - inline annotations: `merge_lazy(&COMPONENT_TAGS, json!({...}))`
#[must_use]
pub fn merge_lazy(base: &LazyLock<Value>, extra: Value) -> Value {
let mut result = (**base).clone();
if let (Some(obj), Value::Object(extra_obj)) = (result.as_object_mut(), extra) {
obj.extend(extra_obj);
}
result
}

// ─── Base tag groups ───────────────────────────────────────────────────────────

pub static INTERNAL_METRICS_TAGS: LazyLock<Value> = LazyLock::new(|| {
json!({
"pid": {"description": "The process ID of the Vector instance.", "required": false, "examples": ["4232"]},
"host": {"description": "The hostname of the system Vector is running on.", "required": false, "examples": ["my-host.local"]}
})
});

pub static COMPONENT_TAGS: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&INTERNAL_METRICS_TAGS,
json!({
"component_kind": {
"description": "The Vector component kind.",
"required": true,
"enum": {
"sink": "Vector sink components",
"source": "Vector source components",
"transform": "Vector transform components"
}
},
"component_id": {"description": "The Vector component ID.", "required": true, "examples": ["my_source", "my_sink"]},
"component_type": {"description": "The Vector component type.", "required": true, "examples": ["file", "http", "honeycomb", "splunk_hec"]}
}),
)
});

// ─── Extensions of COMPONENT_TAGS ─────────────────────────────────────────────

pub static COMPONENT_TAGS_OUTPUT: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS,
json!({
"output": {"description": "The specific output of the component.", "required": false}
}),
)
});

pub static COMPONENT_TAGS_GRPC_METHOD_SERVICE: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS,
json!({
"grpc_method": {"description": "The name of the method called on the gRPC service.", "required": true},
"grpc_service": {"description": "The gRPC service name.", "required": true}
}),
)
});

pub static COMPONENT_TAGS_GRPC_ALL: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS_GRPC_METHOD_SERVICE,
json!({
"grpc_status": {"description": "The human-readable [gRPC status code](https://grpc.github.io/grpc/core/md_doc_statuscodes.html).", "required": true}
}),
)
});

pub static COMPONENT_TAGS_HTTP_METHOD: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS,
json!({
"method": {"description": "The HTTP method of the request.", "required": false}
}),
)
});

pub static COMPONENT_TAGS_HTTP_STATUS: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS,
json!({
"status": {"description": "The HTTP status code of the request.", "required": false}
}),
)
});

pub static COMPONENT_TAGS_HTTP_METHOD_PATH: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS_HTTP_METHOD,
json!({
"path": {"description": "The path that produced the error.", "required": true}
}),
)
});

pub static COMPONENT_TAGS_HTTP_ALL: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS_HTTP_METHOD_PATH,
json!({
"status": {"description": "The HTTP status code of the request.", "required": false}
}),
)
});

pub static COMPONENT_TAGS_ERROR_TYPE_STAGE: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS,
json!({
"error_type": {
"description": "The type of the error",
"required": true,
"enum": {
"acknowledgements_failed": "The acknowledgement operation failed.",
"delete_failed": "The file deletion failed.",
"encode_failed": "The encode operation failed.",
"field_missing": "The event field was missing.",
"glob_failed": "The glob pattern match operation failed.",
"http_error": "The HTTP request resulted in an error code.",
"invalid_metric": "The metric was invalid.",
"kafka_offset_update": "The consumer offset update failed.",
"kafka_read": "The message from Kafka was invalid.",
"mapping_failed": "The mapping failed.",
"match_failed": "The match operation failed.",
"out_of_order": "The event was out of order.",
"parse_failed": "The parsing operation failed.",
"read_failed": "The file read operation failed.",
"render_error": "The rendering operation failed.",
"stream_closed": "The downstream was closed, forwarding the event(s) failed.",
"type_conversion_failed": "The type conversion operating failed.",
"type_field_does_not_exist": "The type field does not exist.",
"type_ip_address_parse_error": "The IP address did not parse.",
"unlabeled_event": "The event was not labeled.",
"value_invalid": "The value was invalid.",
"watch_failed": "The file watch operation failed.",
"write_failed": "The file write operation failed."
}
},
"stage": {
"description": "The stage within the component at which the error occurred.",
"required": true,
"enum": {
"receiving": "While receiving data.",
"processing": "While processing data within the component.",
"sending": "While sending data."
}
}
}),
)
});

// ─── Extensions of INTERNAL_METRICS_TAGS ──────────────────────────────────────

pub static INTERNAL_METRICS_TAGS_FILE: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&INTERNAL_METRICS_TAGS,
json!({
"file": {"description": "The file that produced the error.", "required": false}
}),
)
});

pub static INTERNAL_METRICS_TAGS_REASON: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&INTERNAL_METRICS_TAGS,
json!({
"reason": {
"description": "The type of the error",
"required": true,
"enum": {
"out_of_order": "The event was out of order.",
"oversized": "The event was too large."
}
}
}),
)
});

// ─── Metric-specific tag sets ─────────────────────────────────────────────────

pub static COMPONENT_RECEIVED_EVENTS_TOTAL_TAGS: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS,
json!({
"file": {"description": "The file from which the data originated.", "required": false},
"uri": {"description": "The sanitized URI from which the data originated.", "required": false},
"container_name": {"description": "The name of the container from which the data originated.", "required": false},
"pod_name": {"description": "The name of the pod from which the data originated.", "required": false},
"peer_addr": {"description": "The IP from which the data originated.", "required": false},
"peer_path": {"description": "The pathname from which the data originated.", "required": false},
"mode": {
"description": "The connection mode used by the component.",
"required": false,
"enum": {
"udp": "User Datagram Protocol",
"tcp": "Transmission Control Protocol",
"unix": "Unix domain socket"
}
}
}),
)
});

/// Same tag set as `component_received_events_total` (inherited by byte-count metrics).
pub static COMPONENT_RECEIVED_EVENTS_TAGS: LazyLock<Value> =
LazyLock::new(|| COMPONENT_RECEIVED_EVENTS_TOTAL_TAGS.clone());

pub static COMPONENT_TAGS_HTTP_ERROR_KIND: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS,
json!({
"error_kind": {"description": "The kind of HTTP error encountered (e.g. connection refused, connection reset).", "required": true}
}),
)
});

pub static S3_OBJECT_PROCESSING_TAGS: LazyLock<Value> = LazyLock::new(|| {
merge_lazy(
&COMPONENT_TAGS,
json!({
"bucket": {"description": "The name of the S3 bucket.", "required": true}
}),
)
});
1 change: 1 addition & 0 deletions lib/vector-common/src/internal_event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod component_events_timed_out;
mod events_received;
mod events_sent;
pub mod metric_name;
pub mod metric_tags;
mod optional_tag;
mod prelude;
pub mod service;
Expand Down
26 changes: 26 additions & 0 deletions lib/vector-config-macros/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,32 @@ impl FromMeta for Metadata {
}),
}
}
// Accept path expressions so callers can reference constants:
// `#[configurable(metadata(docs::tags = INTERNAL_METRICS_TAGS))]`
Expr::Path(path) => Some(LazyCustomAttribute::KeyValue {
key: path_to_string(&nv.path),
value: path.to_token_stream(),
}),
// Accept macro invocations such as `serde_json::json!({...})`.
Expr::Macro(mac) => Some(LazyCustomAttribute::KeyValue {
key: path_to_string(&nv.path),
value: mac.to_token_stream(),
}),
// Accept reference/deref expressions such as `&*SOME_LAZY_LOCK`
// so callers can coerce `LazyLock<T>` to `&T: Serialize`.
Expr::Reference(r) => Some(LazyCustomAttribute::KeyValue {
key: path_to_string(&nv.path),
value: r.to_token_stream(),
}),
Expr::Unary(u) => Some(LazyCustomAttribute::KeyValue {
key: path_to_string(&nv.path),
value: u.to_token_stream(),
}),
// Accept function calls such as `merge(&*BASE, json!({...}))`.
Expr::Call(c) => Some(LazyCustomAttribute::KeyValue {
key: path_to_string(&nv.path),
value: c.to_token_stream(),
}),
expr => {
errors
.push(darling::Error::unexpected_expr_type(expr).with_span(nmeta));
Expand Down
19 changes: 16 additions & 3 deletions lib/vector-config-macros/src/ast/variant.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use darling::{FromAttributes, error::Accumulator, util::Flag};
use darling::{FromAttributes, error::Accumulator, util::Override};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use serde_derive_internals::ast as serde_ast;
Expand Down Expand Up @@ -149,7 +149,20 @@ impl<'a> Variant<'a> {
/// standard `#[deprecated]` attribute, neither automatically applying it nor deriving the
/// deprecation status of a variant when it is present.
pub fn deprecated(&self) -> bool {
self.attrs.deprecated.is_present()
self.attrs.deprecated.is_some()
}

/// The deprecation message, if one has been set.
///
/// Set via `#[configurable(deprecated = "message")]`.
pub fn deprecated_message(&self) -> Option<&String> {
self.attrs
.deprecated
.as_ref()
.and_then(|message| match message {
Override::Inherit => None,
Override::Explicit(message) => Some(message),
})
}

/// Whether or not this variant is visible during either serialization or deserialization.
Expand Down Expand Up @@ -186,7 +199,7 @@ impl ToTokens for Variant<'_> {
struct Attributes {
title: Option<String>,
description: Option<String>,
deprecated: Flag,
deprecated: Option<Override<String>>,
#[darling(skip)]
visible: bool,
#[darling(multiple)]
Expand Down
3 changes: 3 additions & 0 deletions lib/vector-config-macros/src/configurable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ fn generate_variant_metadata(
let maybe_title = get_metadata_title(meta_ident, variant.title());
let maybe_description = get_metadata_description(meta_ident, variant.description());
let maybe_deprecated = get_metadata_deprecated(meta_ident, variant.deprecated());
let maybe_deprecated_message =
get_metadata_deprecated_message(meta_ident, variant.deprecated_message());

// We have to mark variants as transparent, so that if we're dealing with an untagged enum, we
// don't panic if their description is intentionally left out.
Expand Down Expand Up @@ -520,6 +522,7 @@ fn generate_variant_metadata(
#maybe_title
#maybe_description
#maybe_deprecated
#maybe_deprecated_message
#maybe_transparent
#maybe_custom_attributes
#variant_logical_name
Expand Down
22 changes: 21 additions & 1 deletion src/generate_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
use std::{fs, path::PathBuf};

use clap::Parser;
use serde_json::{Value, json};
use vector_common::internal_event::{CounterName, GaugeName, HistogramName};
use vector_lib::configurable::schema::generate_root_schema;

use crate::config::ConfigBuilder;
Expand All @@ -16,11 +18,29 @@ pub struct Opts {
pub(crate) output_path: Option<PathBuf>,
}

fn metric_enum_schema<T: vector_lib::configurable::Configurable + 'static>() -> Value {
generate_root_schema::<T>()
.map(|s| serde_json::to_value(s).unwrap_or(Value::Null))
.unwrap_or(Value::Null)
}

/// Execute the `generate-schema` command.
#[allow(clippy::print_stdout, clippy::print_stderr)]
pub fn cmd(opts: &Opts) -> exitcode::ExitCode {
match generate_root_schema::<ConfigBuilder>() {
Ok(schema) => {
Ok(config_schema) => {
// Convert to Value so we can inject the metric enum schemas.
let mut schema = serde_json::to_value(config_schema)
.expect("rendering root schema to JSON should not fail");

// Inject metric name enum schemas so vdev can generate
// internal_metrics.cue output descriptions from them.
schema["_metric_schemas"] = json!({
"counters": metric_enum_schema::<CounterName>(),
"histograms": metric_enum_schema::<HistogramName>(),
"gauges": metric_enum_schema::<GaugeName>(),
});

let json = serde_json::to_string_pretty(&schema)
.expect("rendering root schema to JSON should not fail");

Expand Down
Loading
Loading