如何使用 Telegraf 将 JSON 解析到 InfluxDB Cloud

导航至

在 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 playground 来开发和调试查询真的很有帮助。

在您立即开始配置解析器之前,请确保您了解 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 选项优先于 measurement_name

3. 确定在您的 JSON 文档中,您将使用字段和标签子表还是对象(或两者都使用!)

4. 设置您想要作为时间戳的值及其格式(可选)

如果您的 JSON 文档中包含您想要解析的时间戳,您可以一起使用 timestamp_pathtimestamp_format 选项。您将使用指向您的时间值的 GJSON 查询配置 timestamp_path。然后,timestamp_format 必须设置为 unixunix_msunix_usunix_nsGo “参考时间”,它被定义为特定时间:Mon Jan 2 15:04:05 MST 2006

当解析不包含时区说明符的时间时,时间被假定为 UTC。要默认使用另一个时区或本地时间,请指定 json_timezone 选项。此选项应设置为 Unix TZ 值,例如 America/New_YorkLocal 以使用系统时区。如果您使用 timestamp_timezone,则必须配置 timestamp_formattimestamp_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 数据中读取的字段。这与您配置标签的方式非常相似,即使用 .field 子表,并将指向您的 JSON 值的 GJSON 查询设置为 path。您可以使用 rename 选项轻松重命名任何字段名称,并使用 type 更改字段的数据类型。数据类型可以设置为 float、int、uint、string 或 bool。与 json_v1 解析器不同,新的 JSON 解析器不需要特殊配置即可读取字符串字段。

在我们的天气示例中,从技术上讲,我们可以读取整个 JSON 文档,但我只想要一些值,例如温度、风速和纬度/经度(以便 在地图中可视化我的数据!!)。解析器默认删除预先添加的键,因此我的风速将被读取为 speed,因此我将其重命名为 wind_speed。稍后我将在本博客中向您展示如何在读取整个对象时保留预先添加的键。

我将通过在其子表中设置 type = "int" 将温度数据类型从浮点数转换为整数。

[[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 对象,其中包含您要解析的键和值。您可以不使用上一步中讨论的 fieldtag 子表来指定每个单独的键,而是可以使用 object 子表来读取您的 JSON 对象的所有或一部分。让我们继续使用天气 JSON 进行此配置。现在,我们将读取整个 JSON 文档,并展示如何轻松选择要包含在 JSON 中的键。

a. 设置您的 JSON 对象的路径

使用 GJSON playground 确定您的 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_latcoord_longmain_temp。但是通过将其设置为 true,我们有了更简洁的名称 latlongtemp ,它们仍然让我们了解该值。

重要的是要注意,如果启用此设置,重复的名称将相互覆盖。例如,您通常会有不同的嵌套键,标题为 name。如果启用此设置,并且您正在读取多个没有父键的 name 键,则它们不会全部包含在内。因此,在启用此设置之前,您必须查看 JSON 的键。出于这个原因,此设置默认为 false。

d. 定义您想要从 JSON 对象中包含或排除的键的列表

当您处理要解析的大型 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 格式在 IoT 环境中可能很常见,在 IoT 环境中,事件从多个设备在特定时间报告。为了使此博客和这些示例令人兴奋,我对 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 频道中联系我们,或在我们的 社区网站 上发布任何问题。