0.22 Upgrade Guide
An upgrade guide that addresses breaking changes in 0.22.0
Vector’s 0.22.0 release includes breaking changes:
gcp_stackdriver_metrics
configuration change- VRL now supports template strings
encode_key_value
andencode_logfmt
quote wrapping behavior change
and deprecations:
We cover them below to help you upgrade quickly:
Upgrade guide
Breaking changes
gcp_stackdriver_metrics
configuration change
The gcp_stackdriver_metrics
sink now matches the gcp_stackdriver_logs
configuration, and doesn’t require an additional labels
section to add
labels to submitted metrics.
TOML transform example
Old configuration
[sinks.my_sink_id]
type = "gcp_stackdriver_metrics"
inputs = [ "my-source-or-transform-id" ]
credentials_path = "/path/to/credentials.json"
project_id = "vector-123456"
[sinks.my_sink_id.resource]
type = "global"
[sinks.my_sink_id.resource.labels]
projectId = "vector-123456"
instanceId = "Twilight"
zone = "us-central1-a"
New configuration
[sinks.my_sink_id]
type = "gcp_stackdriver_metrics"
inputs = [ "my-source-or-transform-id" ]
credentials_path = "/path/to/credentials.json"
project_id = "vector-123456"
[sinks.my_sink_id.resource]
type = "global"
projectId = "vector-123456"
instanceId = "Twilight"
zone = "us-central1-a"
For more information on the new syntax, you can review the documentation here
VRL now supports template strings
VRL strings can now be templated. It is now possible to insert the values of
variables by inserting a placeholder with an embedded variable name into the
string using {{..}}
. For example in the following code:
beverage = "coffee"
preference = "I love to drink {{ beverage }}!"
assert!(preference == "I love to drink coffee!")
It should be noted that currently the placeholder must contain a simple variable name and that variable must resolve to a string.
This will not work:
stars = 42
sky = "There are {{ stars }} in the sky."
Instead, the variable must be converted to a string first:
stars = to_string(42)
sky = "There are {{ stars }} in the sky."
Also paths are currently not supported, so this will not work:
message = "The message is {{ .message }}."
Assign the field to a variable first:
message = .message
message = "The message is {{ message }}."
If you wish to insert {{
into the string, you can escape using \{{
and \}}
.
You also still have the option to use raw strings (s'...'
):
assert!("\{{ right here \}}" == s'{{ right here }}')
encode_key_value
and encode_logfmt
quote wrapping behavior change
Values and keys containing whitespace and/or double quotes are now wrapped in
double quotes with the original quotes escaped. This change brings
encode_logfmt
inline with the defined spec
used by other libraries.
Previously, only keys and values containing whitespace were wrapped in quotes.
If using encode_logfmt
, the previous behavior would result in messages similar to:
lvl=info msg={"some":"val"}
With this change, the message would be encoded as:
lvl=info msg="{\"some\":\"val\"}"
Deprecations
Imminent removal of deprecated transforms replaced by remap
and reduce
When the remap
transform was introduced, several transforms were deprecated
and removed from the documentation, despite still being available in Vector.
Additionally the merge
transform was replaced by reduce
. In 0.23.0 we will
be finally removing these transforms as part of some cleanup to our reference
documentation.
Transforms to be removed:
add_fields
add_tags
ansi_stripper
aws_cloudwatch_logs_subscription_parser
coercer
concat
grok_parser
json_parser
key_value_parser
logfmt_parser
merge
regex_parser
remove_fields
remove_tags
rename_fields
split
tokenizer
See below for examples of how to replicate functionality using the remap
transform.
add_fields
Before:
[transforms.add_fields]
type = "add_fields"
inputs = ["some_input"]
fields.parent.child2 = "value2"
After:
[transforms.add_fields]
type = "remap"
inputs = ["some_input"]
source = '''
.parent.child2 = "value2"
'''
add_tags
Before:
[transforms.add_tags]
type = "add_tags"
inputs = ["some_input"]
tags.some_tag = "some_value"
After:
[transforms.add_tags]
type = "remap"
inputs = ["some_input"]
source = '''
.tags.some_tag = "some_value"
'''
ansi_stripper
Before:
[transforms.ansi_stripper]
type = "ansi_stripper"
inputs = ["some_input"]
After:
[transforms.ansi_stripper]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
.message = strip_ansi_escape_codes(string!(.message))
'''
aws_cloudwatch_logs_subscription_parser
Before:
[transforms.aws_cloudwatch_logs_subscription_parser]
type = "aws_cloudwatch_logs_subscription_parser"
inputs = ["some_input"]
After:
[transforms.aws_cloudwatch_logs_subscription_parser]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
. |= parse_aws_cloudwatch_log_subscription_message!(.message)
'''
coercer
Before:
[transforms.coercer]
type = "coercer"
inputs = ["some_input"]
types.some_bool = "bool"
types.some_float = "float"
types.some_int = "int"
types.some_string = "string"
types.some_timestamp = "timestamp"
After:
[transforms.coercer]
type = "remap"
inputs = ["some_input"]
source = '''
.some_bool = to_bool!(.some_bool)
.some_float = to_float!(.some_float)
.some_int = to_int!(.some_int)
.some_string = to_string!(.some_string)
.some_timestamp = to_timestamp!(.some_timestamp)
'''
concat
Before:
[transforms.concat]
type = "concat"
inputs = ["some_input"]
items = ["month", "day", "year"]
target = "date"
joiner = "/"
After:
[transforms.concat]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
.date = join!([.month, .day, .year], "/")
'''
grok_parser
Before:
[transforms.grok_parser]
type = "grok_parser"
inputs = ["some_input"]
pattern = "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}"
types.timestamp = "timestamp|%+"
types.level = "string"
types.message = "string"
After:
[transforms.grok_parser]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
. |= parse_grok!(.message, "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}")
.timestamp = parse_timestamp!(.timestamp , format: "%+")
'''
json_parser
Before:
[transforms.json_parser]
type = "json_parser"
inputs = ["some_input"]
After:
[transforms.json_parser]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
. |= object!(parse_json(.message))
'''
key_value_parser
Before:
[transforms.key_value_parser]
type = "key_value_parser"
inputs = ["some_input"]
After:
[transforms.key_value_parser]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
. |= parse_key_value!(.message)
'''
logfmt_parser
Before:
[transforms.logfmt_parser]
type = "logfmt_parser"
inputs = ["some_input"]
After:
[transforms.logfmt_parser]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
. |= parse_logfmt!(.message)
'''
merge
Before:
[transforms.merge]
type = "merge"
inputs = ["some_input"]
After:
[transforms.merge]
type = "reduce"
inputs = ["some_input"]
starts_when = "._partial == true"
merge_strategies.message = "concat"
regex_parser
Before:
[transforms.regex_parser]
type = "regex_parser"
inputs = ["some_input"]
patterns = ['^(?P<host>[\w\.]+) - (?P<user>[\w]+) (?P<bytes_in>[\d]+) \[(?P<timestamp>.*)\] "(?P<method>[\w]+) (?P<path>.*)" (?P<status>[\d]+) (?P<bytes_out>[\d]+)$']
types.bytes_in = "int"
types.timestamp = "timestamp|%d/%m/%Y:%H:%M:%S %z"
types.status = "int"
types.bytes_out = "int"
After:
[transforms.regex_parser]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
. |= parse_regex!(.message, [#"^(?P<host>[\w\.]+) - (?P<user>[\w]+) (?P<bytes_in>[\d]+) \[(?P<timestamp>.*)\] "(?P<method>[\w]+) (?P<path>.*)" (?P<status>[\d]+) (?P<bytes_out>[\d]+)$"#]
.bytes_in = to_int!(.bytes_in)
.some_timestamp = parse_timestamp!(.some_timestamp, "%d/%m/%Y:%H:%M:%S %z")
.status = to_int!(.status)
.bytes_out = to_int!(.bytes_out)
'''
remove_fields
Before:
[transforms.remove_fields]
type = "remove_fields"
inputs = ["some_input"]
fields = ["parent.child"]
After:
[transforms.remove_fields]
type = "remap"
inputs = ["some_input"]
source = '''
del(.parent.child)
'''
remove_tags
Before:
[transforms.remove_tags]
type = "remove_tags"
inputs = ["some_input"]
tags = ["some_tag"]
After:
[transforms.remove_tags]
type = "remap"
inputs = ["some_input"]
source = '''
del(.tags.some_tag)
'''
rename_fields
Before:
[transforms.rename_fields]
type = "rename_fields"
inputs = ["some_input"]
fields.new_name = ["old_name"]
After:
[transforms.rename_fields]
type = "remap"
inputs = ["some_input"]
source = '''
.new_name = del(.old_name)
'''
split
Before:
[transforms.split]
type = "split"
inputs = ["some_input"]
field_names = ["remote_addr", "user_id", "timestamp", "message", "status", "bytes"]
types.status = "int"
types.bytes = "int"
After:
[transforms.split]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
values = split(.message)
.remote_addr = values[0]
.user_id = values[1]
.timestamp = values[2]
.message = values[3]
.status = to_int!(values[4])
.bytes = to_int!(values[5])
'''
tokenizer
Before:
[transforms.tokenizer]
type = "tokenizer"
inputs = ["some_input"]
field_names = ["remote_addr", "ident", "user_id", "timestamp", "message", "status", "bytes"]
.types.status = "int"
.types.bytes = "int"
After:
[transforms.tokenizer]
type = "remap"
inputs = ["some_input"]
drop_on_error = false
source = '''
values = parse_tokens!(.message)
.remote_addr = values[0]
.user_id = values[1]
.timestamp = values[2]
.message = values[3]
.status = to_int!(values[4])
.bytes = to_int!(values[5])
'''