-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(kafka sink): Fix Kafka partition key on metric events #20246
base: master
Are you sure you want to change the base?
fix(kafka sink): Fix Kafka partition key on metric events #20246
Conversation
Aside on the implementation:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed and approved by documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this @jerryjvl! With some help from @bruceg I was able to track down the closest equivalent to this logic that exists, in the implementation of VrlTarget
:
vector/lib/vector-core/src/event/vrl_target.rs
Lines 460 to 502 in b025ba7
/// Retrieves a value from a the provided metric using the path. | |
/// Currently the root path and the following paths are supported: | |
/// - name | |
/// - namespace | |
/// - timestamp | |
/// - kind | |
/// - tags | |
/// - tags.<tagname> | |
/// - type | |
/// | |
/// Any other paths result in a `MetricPathError::InvalidPath` being returned. | |
fn target_get_metric<'a>( | |
path: &OwnedValuePath, | |
value: &'a Value, | |
) -> Result<Option<&'a Value>, String> { | |
if path.is_root() { | |
return Ok(Some(value)); | |
} | |
let value = value.get(path); | |
for paths in path.to_alternative_components(MAX_METRIC_PATH_DEPTH) { | |
match paths.as_slice() { | |
["name"] | ["kind"] | ["type"] | ["tags", _] => return Ok(value), | |
["namespace"] | ["timestamp"] | ["tags"] => { | |
if let Some(value) = value { | |
return Ok(Some(value)); | |
} | |
} | |
_ => { | |
return Err(MetricPathError::InvalidPath { | |
path: &path.to_string(), | |
expected: VALID_METRIC_PATHS_GET, | |
} | |
.to_string()) | |
} | |
} | |
} | |
// We only reach this point if we have requested a tag that doesn't exist or an empty | |
// field. | |
Ok(None) | |
} |
I agree that something like this should exist as a method on Metric
, where it'd be easy to reuse here. The implementation above is not quite suitable, since it operates against the precomputed Value
representation of the metric instead of the Metric
type itself.
It seems like the best path would be to merge these two implementation into one method on Metric
, using the logic from the existing VrlTarget
method but applying it to the Metric
instead of the Value
.
Does that make sense to you as a next iteration? I'm happy to push some example code if that would be helpful.
I think that's enough context for me to give it a try; thanks! I'd still like to maintain the fall-through logic in the |
Yeah that makes sense to me! Definitely don't want to introduce a breaking change unnecessarily. Ideally we could separate out that logic so that we can cleanly deprecate and remove it in future releases. |
@lukesteensen I have some work-in-progress locally, but I'm running into some complexities that I want to query to make sure I'm not going about this completely the wrong way. (I'm neither a Rust veteran nor a Vector source-code veteran yet). I have effectively created But when I'm trying to consolidate the logic required for It means that for an I don't know how deep this distinction goes, but it seems for logging, data gets transformed into I don't know the Vector code-base well enough (yet) to know if there is an easy upstream location where these calls could more easily be consolidated without the trade-off downsides I'm struggling with at the level I'm trying to (minimally) intervene at. |
@jerryjvl I see what you mean, thanks for clarifying. Sorry for the delayed response, but here's roughly how I would think about it: It seems to me that the precomputed Given the different contexts, it may be simpler to just have two different but related methods. For the non-VRL use case, we should not need to worry about things like returning the root path or other composite |
Currently, the Kafka sink implementation of
key_field
uses a simple lookup intotags
for metrics. Unfortunately, this does not work because the round-trip through parsing the configuration results in a forced.
prefix, which only works if the tag name also contains an explicit dot at the start.Fixes: #20217
A workaround that I used in production:
This fix tries to improve the situation by:
.name
explicitly, so the hack above can be removed entirely.tags.<tag-name>
explicitly, so that config can also be implemented in a way that will remain compatible when the Metric type structure eventually implements Value compatibility for VRL-style de-referencing.key_field
so there is at least one version that can be used as a graceful cross-over for deprecation of the previous behaviour.