Telegraf Template Processor 完全指南:用 Go Template 动态生成 Tag 实现指标路由
【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf
本文面向 Telegraf 用户,系统讲解processors.template处理器(自 Telegraf v1.14.0 起提供)的配置语法、模板语言上下文与底层实现原理。通过本文,你将掌握如何基于测量名(measurement name)、标签、字段与时间戳,用 Go Template 语法动态生成新标签,为多输出动态路由、字段转标签、时间维度拆分等场景构建可落地的配置方案。
一、插件定位与典型应用场景
Template Processor 的核心能力是对指标应用模板,生成一个新的 Tag(既有 Tag 名、又有 Tag 值都可以由模板动态计算)。该插件的首要设计用途是:创建可用于多输出插件动态路由的标签,或者配合输出插件特定的路由选项使用——例如按hostname、level等标签拼接出topic,让不同来源的指标落到不同的 Kafka Topic、MQTT 主题或 InfluxDB 存储桶。
模板中可以访问每一条指标的全部要素:
| 可访问数据 | 说明 |
|---|---|
| 测量名(measurement name) | 例如cpu、mem |
| 标签(tags) | 指标的全部键值对标签 |
| 字段(fields) | 指标的全部字段及其值 |
| 时间戳(timestamp) | 指标采集时间,可参与格式化 |
模板遵循 Go Template 语法(标准库text/template),并内置了 Sprig 函数库(sprig.TxtFuncMap()),因此可以使用default、lower、upper、trim、join等大量实用函数对模板结果做二次加工。
二、快速开始:完整配置解析
插件只需两个配置项,完整样例见 sample.conf:
# Uses a Go template to create a new tag [[processors.template]] ## Go template used to create the tag name of the output. In order to ## ease TOML escaping requirements, you should use single quotes around ## the template string. tag = "topic" ## Go template used to create the tag value of the output. In order to ## ease TOML escaping requirements, you should use single quotes around ## the template string. template = '{{ .Tag "hostname" }}.{{ .Tag "level" }}'两个参数的核心要点:
tag:输出标签的名称。它本身也是一个 Go 模板——静态字符串(如"topic")或动态表达式(如'{{ .Field "type" }}')均可,详见下文“字段值作为标签名”示例。template:输出标签的值模板。- 两个参数都支持模板语法,因此“标签名”和“标签值”都可以完全动态生成。
- 文档与样例都特别建议:使用单引号包裹模板字符串,以规避 TOML 对反斜杠、花括号等字符的转义负担(单引号字符串在 TOML 中为字面量,不做转义处理)。
从源码 template.go 可以看出,插件结构体只保存Tag、Template两个toml字段,与上述配置一一对应。
三、模板上下文:TemplateMetric 接口全解
模板的执行对象不是原始指标对象本身,而是 Telegraf 专门为模板场景暴露的TemplateMetric接口。该接口定义在仓库根目录 metric.go:
// TemplateMetric is an interface to use in templates (e.g text/template) // to generate complex strings from metric properties // e.g. '{{.Name}}-{{.Tag "foo"}}-{{.Field "bar"}}' type TemplateMetric interface { Name() string Field(key string) interface{} Fields() map[string]interface{} Tag(key string) string Tags() map[string]string Time() time.Time String() string }模板中可直接调用的方法:
| 方法 | 返回类型 | 作用 | 模板写法示例 |
|---|---|---|---|
Name() | string | 测量名 | {{ .Name }} |
Tag(key) | string | 取指定标签值 | {{ .Tag "hostname" }} |
Tags() | map[string]string | 全部标签的 map | {{ .Tags }} |
Field(key) | interface{} | 取指定字段值 | {{ .Field "temperature" }} |
Fields() | map[string]interface{} | 全部字段的 map | {{ .Fields }} |
Time() | time.Time | 指标时间戳(Go 的time.Time,可继续链式调用UTC()、Year()、Format等) | {{ .Time.UTC.Year }} |
String() | string | 指标的整体字符串表示 | {{ . }} |
接口的默认实现位于 metric/metric.go。例如String()的实现为fmt.Sprintf("%s %v %v %d", name, tags, fields, timeUnixNano),这也是下文{{.}}示例输出格式的来源;Time()直接返回内部time.Time,因此{{.Time.UTC.Year}}这样的链式调用在 Go 模板中是合法的。
从源码看,处理器在Apply中会先尝试对指标调用Unwrap()(处理跟踪指标包装),再断言为TemplateMetric后执行模板——这保证了该插件对普通指标和跟踪型指标都能正常工作。
四、实战示例全解
以下示例完整继承自插件官方文档 README.md,并辅以源码与测试佐证。
4.1 合并多个标签生成单一标签(动态路由首选)
[[processors.template]] tag = "topic" template = '{{ .Tag "hostname" }}.{{ .Tag "level" }}'处理前后对比:
- cpu,level=debug,hostname=localhost time_idle=42 + cpu,level=debug,hostname=localhost,topic=localhost.debug time_idle=42这正是 README 中“为多输出动态路由创建标签”这一首要用例的标准形态。对应单元测试见 template_test.go 的TestTagTemplateConcatenate:输入带hostname=localhost、level=debug标签的指标,断言输出新增topic=localhost.debug。
4.2 使用字段值作为标签名(动态标签名)
[[processors.template]] tag = '{{ .Field "type" }}' template = '{{ .Name }}'处理前后对比:
- cpu,level=debug,hostname=localhost time_idle=42,type=sensor + cpu,level=debug,hostname=localhost,sensor=cpu time_idle=42,type=sensortag参数本身是模板的体现:标签名来自字段type的值(sensor),标签值来自测量名(cpu)。对应测试为 template_test.go 的TestNameTemplate。
4.3 将测量名添加为标签
[[processors.template]] tag = "measurement" template = '{{ .Name }}'处理前后对比:
- cpu,hostname=localhost time_idle=42 + cpu,hostname=localhost,measurement=cpu time_idle=42把测量名显式沉淀为标签,便于在 InfluxDB、Prometheus 等时序存储中按测量名做统一的标签维度查询。对应测试TestName见 template_test.go。
4.4 添加年份标签(类似 date 处理器的用法)
[[processors.template]] tag = "year" template = '{{.Time.UTC.Year}}'利用Time()返回的time.Time进行链式调用:UTC()转为 UTC 时区、Year()取年份,即可为每条指标打上采集年份标签,可用于数据按时间分桶或归档。
4.5 将全部字段打包为单个标签(消息型输出场景)
当需要把全部字段连同值拼进一条消息,转发给 Syslog、GroundWork 等监控系统时,可直接使用.Fields或.Tags输出 map 的字符串表示:
[[processors.template]] tag = "message" template = 'Message about {{.Name}} fields: {{.Fields}}'处理前后对比:
- cpu,hostname=localhost time_idle=42 + cpu,hostname=localhost,message=Message\ about\ cpu\ fields:\ map[time_idle:42] time_idle=42更高级的写法——用range逐字段迭代并换行格式化,得到多行消息:
[[processors.template]] tag = "message" template = '''Message about {{.Name}} fields: {{ range $field, $value := .Fields -}} {{$field}}:{{$value}} {{ end }}'''处理前后对比:
- cpu,hostname=localhost time_idle=42 + cpu,hostname=localhost,message=Message\ about\ cpu\ fields:\ntime_idle:42\n time_idle=42注意这里使用了 TOML 的三引号'''包裹多行模板,range循环后的-用于去除相邻空白,这是 Go 模板控制流的常用写法。
4.6 将完整指标作为标签(调试与原始数据透传)
[[processors.template]] tag = "metric" template = '{{.}}'处理前后对比:
- cpu,hostname=localhost time_idle=42 + cpu,hostname=localhost,metric=cpu\ map[hostname:localhost]\ map[time_idle:42]\ 1257894000000000000 time_idle=42{{.}}输出TemplateMetric.String()的结果,格式为“测量名 + 标签 map + 字段 map + UnixNano 时间戳”。测试TestString、TestDot验证了该行为(见 template_test.go),输出形如test1 map[tag1:value1] map[value:1.23] 1257894000000000000。
五、源码级实现原理
插件核心实现在 template.go,处理流程可分为两个阶段:
5.1 Init 阶段:模板预编译
func (r *Template) Init() error { r.tmplTag, err = template.New("tag template").Funcs(sprig.TxtFuncMap()).Parse(r.Tag) ... r.tmplValue, err = template.New("value template").Funcs(sprig.TxtFuncMap()).Parse(r.Template) ... }两个模板(标签名模板、标签值模板)在Init()中一次性解析并注册 Sprig 函数库,解析失败会直接返回错误(例如模板语法错误),插件无法启动。这意味着模板的合法性检查发生在启动阶段而非运行阶段,符合 Telegraf 插件Init() -> Apply()的生命周期约定。
5.2 Apply 阶段:逐指标执行
func (r *Template) Apply(in ...telegraf.Metric) []telegraf.Metric { for _, raw := range in { m := raw if wm, ok := raw.(telegraf.UnwrappableMetric); ok { m = wm.Unwrap() } tm, ok := m.(telegraf.TemplateMetric) if !ok { r.Log.Errorf("metric of type %T is not a template metric", raw) continue } ... raw.AddTag(tag, value) } return in }值得注意的工程细节:
- 指标不丢失:即使某条指标模板执行失败(如引用了不存在的标签导致空值拼接),插件也只是记录错误日志并跳过该条,绝不会丢弃或吞掉指标。测试
TestMetricMissingTagsIsNotLost(见 template_test.go)专门断言了“输入条数 = 输出条数”这一不变量。 Unwrap()兼容:对实现了UnwrappableMetric的跟踪型指标先解包再断言为TemplateMetric,确保与 Telegraf 的指标跟踪机制(metric/tracking.go)协同工作;测试TestTracking验证了带投递通知的指标经模板处理后仍能正确触发Accept()回调。- 模板结果直接落标签:标签名、标签值两个模板执行结果拼接后,通过
raw.AddTag(tag, value)写入原指标,因此处理器输出的是在原始指标上新增标签的新指标,测量名、字段、时间戳均保持不变。
5.3 Sprig 函数加持
由于Init()中注入了sprig.TxtFuncMap(),模板可以自由调用 Sprig 提供的字符串、列表、数学等函数。测试TestSprig(见 template_test.go)展示了典型组合:
[[processors.template]] tag = '{{ .Tag "foo" | lower }}' template = '{{ .Name | upper }}'即标签名先取foo标签值再转小写,标签值取测量名转大写——|管道是 Go 模板的标准用法。
六、处理器顺序与全局配置
Template Processor 同样支持 Telegraf 的全局/插件级配置能力,例如通过namepass、namedrop、tagexclude等过滤器限定其作用范围,或通过order指定与其它处理器的执行先后。完整说明参见 CONFIGURATION.md。
在处理器编排上需注意:模板处理器读取的是到达它时指标已有的标签与字段。若需要先由其它处理器(如 rename、regex)整理标签,再基于整理结果做模板拼接,应合理设置order(隐式顺序按配置文件中[[processors.template]]出现的位置,显式顺序可用order指定)。从源码结构看,处理器经由 processors 注册机制 注册为"template"名称。
七、延伸:同一模板引擎在其它组件中的应用
TemplateMetric与 Go 模板 + Sprig 的组合并非 template 处理器独有。仓库中的 template 序列化器 使用了完全相同的机制:在Init()中解析模板并注入sprig.TxtFuncMap(),在序列化时对指标执行同样的Unwrap()与TemplateMetric断言,支持单指标模板与批量模板(batch_template)。两者的差异在于:处理器把模板结果写入标签,序列化器把模板结果作为输出数据的正文。
这意味着你在此处学到的全部模板写法,都可以直接复用到输出序列化场景;而当需要把模板结果用于动态路由时,优先选择 processor 方案,因为路由标签必须附着在指标上随指标流转。
八、排查与使用建议
- 模板执行失败不会丢指标:只会输出错误日志(
failed to execute tag name template/failed to execute value template),可通过 Telegraf 日志定位问题(参考 LOGGING.md)。 - 单引号优先:模板字符串在 TOML 中尽量使用单引号或三引号,避免转义错误。
- 注意拼接空值:
{{ .Tag "xxx" }}在标签不存在时返回空字符串,拼接出的标签值可能形如.debug(前导点),可用 Sprig 的default函数兜底,例如'{{ .Tag "hostname" | default "unknown" }}.{{ .Tag "level" }}'。 - 标签值长度与基数:生成的标签会成为时序数据的维度,注意控制其基数(cardinality),避免因拼接时间戳等高频变化值造成存储膨胀。
- 版本前提:本插件自 Telegraf v1.14.0 引入,
TemplateMetric接口、UnwrappableMetric解包逻辑均以当前仓库源码为准。
参考文件索引
- 官方插件文档:plugins/processors/template/README.md
- 插件源码:plugins/processors/template/template.go
- 样例配置:plugins/processors/template/sample.conf
- 单元测试:plugins/processors/template/template_test.go
TemplateMetric接口定义:metric.go- 默认实现:metric/metric.go
- 同引擎序列化器:plugins/serializers/template/template.go
【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考