如何使用 Telegraf 将 JSON 解析到 InfluxDB Cloud
作者 Samantha Wang / 用例,产品,开发者
2021 年 11 月 02 日
导航至
在 Telegraf 1.19 版本中,我们发布了一个新的 JSON 解析器(json_v2
)。原始解析器存在配置不灵活的问题,并且在一些非常常见的情况下数据无法解析。虽然可以使用 Starlark 处理器 解决很多边缘解析案例,但这仍然是一个更高级的方法,需要编写脚本。我们对新的 JSON 解析器进行了大量改进,可以帮助您轻松将 JSON 数据读取到 InfluxDB 中。
新 JSON v2 解析器包含哪些内容?
json_v2 解析器提供了一种改进的方法来从 JSON 中收集指标,但仍然支持原始 JSON 解析器(为了避免混淆,将被称为 json_v1)的所有功能。
以下是一个 json_v2 的示例配置。
[[inputs.file]]
files = []
data_format = "json_v2"
[[inputs.file.json_v2]]
measurement_name_path = ""
timestamp_path = ""
timestamp_format = ""
[[inputs.file.json_v2.object]]
path = ""
timestamp_key = ""
timestamp_format = ""
disable_prepend_keys = false
[inputs.file.json_v2.object.renames]
key = "new name"
[inputs.file.json_v2.object.fields]
key = "int"
独立的 GJSON 查询
json_v1 解析器的一个限制是您只能提供一个 GJSON 查询,并且您只能使用该查询返回的数据来构建行协议。json_v2 解析器允许您提供多个独立的 GJSON 查询,这样您就可以从 JSON 的任何位置收集时间戳(timestamp_path
)、测量名称(measurement_name_path
)和字段/标签。
更详细的 Telegraf 配置但更清晰
配置不再需要为配置键添加前缀 json_
,而是使用 TOML 子表([[inputs.file.json_v2.object]]
)。这次更改的目的是提供一个更容易阅读的配置文件,您可以清楚地看到插件配置和解析器配置之间的区别。虽然这会导致配置文件更冗长,但改进的读写能力应该可以弥补这一点。
对字段类型的更多控制
在使用json_v1时,您只能使用json_string_fields
来指定字段为字符串。在json_v2中,您现在可以指定InfluxDB行协议支持的字段类型,甚至在可能的情况下进行类型转换(将type = "int"
设置为将所有值转换为整数)。有关更多详细信息,请阅读解析器README中的类型部分。
对生成的标签/字段名称的控制
json_v2解析器为您提供了重命名所有标签/字段的能力,而无需像json_v1解析器那样的后处理步骤。您还可以访问disable_prepend_keys
设置
,您可以通过启用它来自动提供更干净的字段/标签名称,这些名称不包括从嵌套数组/对象收集时预置的父键。
配置Telegraf以摄取JSON
天气示例
对于配置示例,我们将使用来自openweathermap.org的当前天气JSON数据。您需要注册并接收API密钥以访问数据,但当前天气如下所示。
{
"coord":{
"lon":-0.1257,
"lat":51.5085
},
"weather":[
{
"id":802,
"main":"Clouds",
"description":"scattered clouds",
"icon":"03n"
}
],
"base":"stations",
"main":{
"temp":16.15,
"feels_like":16.22,
"temp_min":15.31,
"temp_max":16.98,
"pressure":998,
"humidity":92
},
"visibility":10000,
"wind":{
"speed":4.12,
"deg":210
},
"clouds":{
"all":40
},
"dt":1628202887,
"sys":{
"type":2,
"id":2019646,
"country":"GB",
"sunrise":1628137828,
"sunset":1628192550
},
"timezone":3600,
"id":2643743,
"name":"London",
"cod":200
}
JSON路径语法
Telegraf JSON v2解析器使用的查询语法是GJSON路径语法。JSON v2解析器中的大多数配置选项都需要您列出GJSON查询路径,因此当您配置时,它对使用GJSON沙盒进行开发和调试查询非常有帮助。
在您立即开始配置解析器之前,请确保您了解您的JSON格式以及您想要的时戳、度量、标签和字段。
1. 在插件中设置输入数据格式
JSON是Telegraf支持的许多输入数据格式之一。这意味着包含data_format
选项的任何输入插件都可以设置为json_v2
,然后您可以使用json_v2解析器开始解析JSON数据。
data_format = "json_v2"
2. 设置度量名称(可选)
您正在使用的输入插件可能已经有一个默认的度量名称,通常这个名字是插件的名字。如果您想覆盖那个默认名称,您可以使用解析器中的measurement_name
选项来硬编码它。如果您想使您的度量通过JSON文档中的一个值来设置,您可以在measurement_name_path
选项中设置一个GJSON查询。查询必须返回单个数据值,否则解析器将使用默认度量名称。如果同时设置了measurement_name_path
和measurement_name
,则measurement_name_path
选项优先。
确定在您的JSON文档中您是否将使用字段和标签子表或对象(或者两者都使用!)
4. 设置您想要的时戳值及其格式(可选)
如果您在JSON文档中有您想解析的时间戳,您可以使用timestamp_path
和timestamp_format
选项一起。您将使用GJSON查询路径配置timestamp_path
到您的时间值。然后必须将timestamp_format
设置为unix
、unix_ms
、unix_us
、unix_ns
或Go“参考时间”,它被定义为特定时间:Mon Jan 2 15:04:05 MST 2006
。
在解析不包含时区指定符的时间时,假定时间为UTC。要默认使用另一个时区或本地时间,请指定json_timezone选项
。此选项应设置为Unix TZ值,例如America/New_York
或设置为Local
以使用系统时区。如果您使用timestamp_timezone
,则必须配置timestamp_format
和timestamp_path
。
如果没有配置这些timestamp
选项,默认情况下,Telegraf将使用当前时间用于所有创建的度量。
5. 从JSON数据中设置您想要的标签
要将您的JSON中的值指定为标签,您将配置一个.tag
子表,例如[inputs.http.json_v2.tag]
。在子表中,您将在path
选项中添加您的GJSON查询路径。如果您定义的GJSON路径返回单个值,则将得到单个结果标签度量。然而,如果您定义的GJSON路径返回一个值数组,则每个标签将放入一个单独的度量中(这也适用于下一步的字段)。提醒一下,无论JSON文档中原始数据类型是什么,标签始终存储为字符串。
对于我们的天气数据,我将添加城市和ID作为标签。在天气JSON中,位置字段名为name
。我需要一个更具描述性的标签名,所以在我的解析器中,我可以在子表中设置rename
将其重命名为city
。您只能在每个子表中设置一个标签,但如果您想设置多个标签,可以在解析器中包含多个子表。
[[inputs.http.json_v2.tag]]
path = "name"
rename = "city"
[[inputs.http.json_v2.tag]]
path = "id"
6. 添加您想要从JSON数据中获取的字段
您需要添加您将从中读取的JSON数据字段。这非常类似于您使用带有GJSON查询的.field
子表来配置标签的方式,将您的JSON值设置为path
。您可以使用rename
选项轻松重命名任何字段名称,并使用type
更改字段的类型。数据类型可以设置为float、int、uint、string或bool。与json_v1解析器不同,在新的JSON解析器中读取字符串字段不需要特殊配置。
在我们的天气示例中,我们实际上可以读取整个JSON文档,但我只想获取一些值,例如温度、风速和经纬度(以便在地图中可视化我的数据)。默认情况下,解析器会删除前缀键,所以我的风速将以speed
读取,所以我要将其重命名为wind_speed
。我将在稍后的博客中向您展示如何在我们读取整个对象时保留前缀键。
我将通过在其子表中设置type = "int"
将温度数据类型从float转换为整数。
[[inputs.http.json_v2.field]]
path = "weather.#.description"
[[inputs.http.json_v2.field]]
path = "main.temp"
type = "int"
[[inputs.http.json_v2.field]]
path = "wind.speed"
rename = "wind_speed"
[[inputs.http.json_v2.field]]
path = "coord.lon"
[[inputs.http.json_v2.field]]
path = "coord.lat"
7. 设置您想要解析的对象
您的JSON文档通常会包含完整的JSON对象,其中包含您想要解析的键和值。在之前步骤中讨论的field
或tag
子表中指定每个单独的键,而不是使用object
子表来读取您JSON对象的一部分。让我们继续使用天气JSON进行此配置。目前,我们将读取整个JSON文档并展示如何轻松选择要包含在您的JSON中的键。
a. 设置您的JSON对象的路径
使用GJSON游乐场来确定JSON对象的路径对于确保您的查询拉取您想要读取的整个JSON对象非常有帮助。由于我们想要读取整个天气文档,因此我将设置path = "@this"
。如果我们只想解析对象main
下的通用天气统计信息,我们将设置path = "main"
,并读取以下数据。
{
"temp": 16.15,
"feels_like": 16.22,
"temp_min": 15.31,
"temp_max": 16.98,
"pressure": 998,
"humidity": 92
}
但是,对于其他示例的概述,我们将继续使用@this
读取整个JSON文档。
b. 根据上述步骤4设置您的时戳及其格式和时区
如果您的JSON对象中有一个您希望设置为时戳的值,您需要在object
子表下设置时戳参数。它们遵循与上面讨论的全局时戳设置相同的规则。
c. 启用或禁用嵌套数据中的前缀键
此设置是JSON v2解析器的一个非常令人兴奋的新功能,可以在正确的情况下帮助清理您的数据。您现在可以轻松地通过将disable_prepend_keys
设置为true来移除附加到嵌套键名的父键。在天气数据中,如果此设置为false
,我们的字段名称将是coord_lat
、coord_long
、main_temp
。但是,通过将其设置为true,我们拥有更干净的名字lat
、long
和temp
,同时仍然给出了对值的理解。
请注意,如果启用此功能,则重复名称将相互覆盖。例如,您通常会看到不同的嵌套键名为name
。如果此设置启用,并且您正在读取没有父键的多个name
键,则它们不会全部被包含。因此,在启用此设置之前,您需要查看您的JSON键非常重要。由于这个原因,此设置默认为false。
d. 定义您想要包含或排除的键列表
当您处理一个大型JSON对象进行解析,并且只想包含或排除少数几个字段时,您可以使用included_keys = []
或excluded_keys = []
设置轻松地做到这一点。为了使我的配置简单,我将只从对象中获取纬度、经度、温度和风速,如下所示。
included_keys = ["coord_lat", "coord_lon", "main_temp", "wind_speed"]
e. 定义来自您的JSON对象的标签
您还希望定义您希望在JSON中成为标签的键。标签始终是字符串,因此无论它们在原始JSON中的值是什么,它们都将存储为字符串。标签通常是元数据/信息类型的数据,因此对于我的天气数据,我将设置城市名称和ID作为标签。
tags = ["name", "id"]
天气示例配置
使用字段 + 标签子表
[[inputs.http]]
urls = [
"https://api.openweathermap.org/data/2.5/weather?q=london&appid=$API_KEY&units=metric"
]
tagexclude = ["url", "host"]
data_format = "json_v2"
[[inputs.http.json_v2]]
measurement_name = "openweather"
[[inputs.http.json_v2.tag]]
path = "name"
rename = "city"
[[inputs.http.json_v2.tag]]
path = "id"
[[inputs.http.json_v2.field]]
path = "weather.#.main"
rename = "summary"
[[inputs.http.json_v2.field]]
path = "main.temp"
type = "int"
[[inputs.http.json_v2.field]]
path = "wind.speed"
rename = "wind_speed"
[[inputs.http.json_v2.field]]
path = "coord.lon"
[[inputs.http.json_v2.field]]
path = "coord.lat"
输出
openweather,city=London,id=2.643743e+06 description="moderate rain",lat=51.5085,lon=-0.1257,temp=13i,wind_speed=4.63 1632859197000000000
使用字段 + 对象子表
[[inputs.http]]
urls = [
"https://api.openweathermap.org/data/2.5/weather?q=london&appid=$API_KEY&units=metric"
]
tagexclude = ["url", "host"]
data_format = "json_v2"
[[inputs.http.json_v2]]
measurement_name = "openweather"
[[inputs.http.json_v2.field]]
path = "weather.#.description"
[[inputs.http.json_v2.field]]
path = "weather.#.description"
[[inputs.http.json_v2.object]]
path = "@this"
disable_prepend_keys = true
included_keys = ["coord_lat", "coord_lon", "main_temp", "wind_speed"]
tags = ["name", "id"]
输出
openweather,id=2.643743e+06,name=London description="moderate rain",lat=51.5085,lon=-0.1257,speed=4.63,temp=13.68 1632859241000000000
将配置从JSON v1迁移到v2
我们计划在任何Telegraf 1.x版本中都不会废弃json_v1解析器。然而,对Telegraf中JSON解析所做的任何增强都只会应用于json_v2解析器。
json_v2解析器提供了一种改进的方法来从JSON中收集指标,但仍然支持原始JSON解析器(以下简称json_v1以避免混淆)的所有功能。因此,迁移应该适用于所有配置,并在过程中有望改进旧配置。
以下是一个示例,说明如何使用Baywheels自行车共享站点状态数据的老配置,并附带JSON片段。
{
"data": {
"stations": [
{
"last_reported": 1626817316,
"num_ebikes_available": 3,
"station_id": "549",
"is_returning": 1,
"eightd_has_available_keys": false,
"is_renting": 1,
"num_bikes_disabled": 0,
"legacy_id": "549",
"num_docks_disabled": 0,
"station_status": "active",
"num_docks_available": 9,
"num_bikes_available": 26,
"is_installed": 1
}
]
},
"last_updated": 1626818120,
"ttl": 5
}
JSON v1
[[inputs.http]]
urls = ["https://gbfs.baywheels.com/gbfs/en/station_status.json"]
data_format = "json"
json_query = "data.stations"
tag_keys = ["station_id"]
json_string_fields = ["eightd_has_available_keys","legacy_id", "station_status"]
json_time_key = "last_reported"
json_time_format = "unix"
JSON v2
[[inputs.http]]
urls = ["https://gbfs.baywheels.com/gbfs/en/station_status.json"]
data_format = "json_v2"
[[inputs.http.json_v2]]
[[inputs.http.json_v2.object]]
path = "data.stations"
tags = ["station_id"]
timestamp_key = "last_reported"
timestamp_format = "unix"
这两种配置都将导致此输出
http,host=MBP15-SWANG.local,station_id=549,url=https://gbfs.baywheels.com/gbfs/en/station_status.json eightd_has_available_keys=false,is_installed=1,is_renting=1,is_returning=1,legacy_id="549",num_bikes_available=26,num_bikes_disabled=0,num_docks_available=9,num_docks_disabled=0,num_ebikes_available=3,station_status="active" 1626817316000000000
更多示例!
你可能会遇到无数种JSON格式的例子。以下是我们要用新JSON解析器专门解决的问题的一些例子。请务必查看此处完整的测试数据示例列表。
嵌套标签
旧解析器在处理JSON层次结构时会出现很多问题,因为它很难在保持JSON层次结构的同时保留标签。用户通常不得不将JSON数据扁平化,以便解析器能够工作。使用新的json_v2解析器,每个数组都可以正确读取。
JSON输入
{
"Group A": [
{
"Sub-group 1": [
{
"Count": 0,
"Errors": 0,
"Serial": "9JHNGTUT",
"Model": "WDC WUH721414ALE604",
"Firmware": "LDGSW07G"
}
]
}
],
"Group B": [
{
"Sub-group 1": [
{
"Count": 0,
"Errors": 0,
"Serial": "9JHLPW9T",
"Model": "WDC WUH721414ALE604",
"Firmware": "LDGSW07G"
}
]
}
]
}
配置
[[inputs.http]]
urls = ["https://gist.githubusercontent.com/sjwang90/6c3b066656aa3cece50b4642a4cfef88/raw/3b591c7d3c0bd85687ea90ed920c091178d47ee5/hierarchy-tags.json"]
tagexclude = ["url", "host"]
data_format = "json_v2"
[[inputs.http.json_v2]]
[[inputs.http.json_v2.object]]
path = "Group A.#.Sub-group 1"
tags = ["Firmware", "Model", "Serial"]
[[inputs.http.json_v2.object]]
path = "Group B.#.Sub-group 1"
tags = ["Firmware", "Model", "Serial"]
输出
http,Firmware=LDGSW07G,Model=WDC\ WUH721414ALE604,Serial=9JHNGTUT Count=0,Errors=0 1633389803000000000
http,Firmware=LDGSW07G,Model=WDC\ WUH721414ALE604,Serial=9JHLPW9T Count=0,Errors=0 1633389803000000000
嵌套数组之外的时间戳
未在数组之外的时间戳在JSON v1解析器中导致了问题。在新的JSON v2解析器中,时间戳可以作为独立查询运行。这种JSON格式在物联网环境中很常见,其中多个设备在特定时间报告事件。为了使这篇博客和这些例子保持新鲜感,我使用了Britney Spears在Spotify上的流媒体播放的相同结构。
JSON输入
{
"timestamp": 1626449400,
"spotify": [
{
"album": "Oops...I Did It Again",
"song": "Stronger",
"single": true,
"streams": 87467784
},
{
"album": "Oops...I Did It Again",
"song": "Dear Diary",
"single": false,
"streams": 2243088
},
{
"album": "The Singles Collection",
"song": 3,
"single": true,
"streams": 67916609
}
]
}
配置
[[inputs.http]]
urls = ["https://gist.githubusercontent.com/sjwang90/a7b370eb8cdbd59970e4d5eccdd3438f/raw/3bd22befd0b1c5d0eb398c8391ec29eff2621630/britney.json"]
tagexclude = ["url", "host"]
data_format = "json_v2"
[[inputs.http.json_v2]]
measurement_name = "britney"
timestamp_path = "timestamp"
timestamp_format = "unix"
[[inputs.http.json_v2.object]]
path = "spotify"
tags = ["album, song"]
输出
britney album="Oops...I Did It Again",single=true,song="Stronger",streams=87467784 1626449400000000000
britney album="Oops...I Did It Again",single=false,song="Dear Diary",streams=2243088 1626449400000000000
britney album="The Singles Collection",single=true,song=3,streams=67916609 1626449400000000000
试试看!
使用新的JSON解析器开始将数据发送到InfluxDB Cloud。如有任何问题,请在我们的InfluxData社区Slack的#telegraf频道中提出,或在我们的社区网站上发帖。