vrl/datadog/search/
field.rs

1use super::grammar;
2
3/// Default fields that represent the search path when a Datadog tag/facet is not provided.
4static DEFAULT_FIELDS: &[&str] = &[
5    "message",
6    "custom.error.message",
7    "custom.error.stack",
8    "custom.title",
9    "_default_",
10];
11
12/// Attributes that represent special fields in Datadog.
13static RESERVED_ATTRIBUTES: &[&str] = &[
14    "host",
15    "source",
16    "status",
17    "service",
18    "trace_id",
19    "message",
20    "timestamp",
21    "tags",
22];
23
24/// Describes a field to search on.
25#[derive(Clone, Hash, PartialEq, Eq, Debug)]
26pub enum Field {
27    /// Default field (when tag/facet isn't provided)
28    Default(String),
29
30    // TODO investigate making this be an enum which may make more sense
31    //      when dealing with a fixed set of field names
32    /// Reserved field that receives special treatment in Datadog.
33    Reserved(String),
34
35    /// An Attribute-- i.e. started with `@`.
36    // In Datadog Log Search the `@` prefix is used to define a Facet for
37    // attribute searching, and the event structure is assumed to have a
38    // root level field "custom". In VRL we do not guarantee this event
39    // structure so we are diverging a little from the DD Log Search
40    // definition and implementation a bit here, by calling this "Attribute".
41    //
42    // Internally when we handle this enum variant, we attempt to parse the
43    // string as a log path to obtain the value.
44    Attribute(String),
45
46    /// Tag type - i.e. search in the `tags` field.
47    Tag(String),
48}
49
50impl Field {
51    pub fn as_str(&self) -> &str {
52        match self {
53            Self::Default(s) => s,
54            Self::Reserved(s) => s,
55            Self::Attribute(s) => s,
56            Self::Tag(s) => s,
57        }
58    }
59}
60
61/// Converts a field/facet name to the VRL equivalent. Datadog payloads have a `message` field
62/// (which is used whenever the default field is encountered.
63pub fn normalize_fields<T: AsRef<str>>(value: T) -> Vec<Field> {
64    let value = value.as_ref();
65    if value.eq(grammar::DEFAULT_FIELD) {
66        return DEFAULT_FIELDS
67            .iter()
68            .map(|s| Field::Default((*s).to_owned()))
69            .collect();
70    }
71
72    let field = match value.replace('@', ".") {
73        v if value.starts_with('@') => Field::Attribute(v),
74        v if DEFAULT_FIELDS.contains(&v.as_ref()) => Field::Default(v),
75        v if RESERVED_ATTRIBUTES.contains(&v.as_ref()) => Field::Reserved(v),
76        v => Field::Tag(v),
77    };
78
79    vec![field]
80}