How Metric Tagging and Global Tags Work in Telegraf: Complete Implementation Guide
Telegraf merges global tags with plugin-specific tags at runtime via the makeMetric function, giving precedence to plugin-level tags when keys collide.
Telegraf, the open-source server agent from InfluxData, implements a sophisticated two-stage tagging system that automatically decorates every metric with configuration-wide metadata. Understanding how metric tagging and global tags work requires examining the internal pipeline where configuration settings transform into runtime tag maps. This guide analyzes the source code to explain exactly how tags propagate from your telegraf.conf file to the final output.
Parsing the Global Tags Configuration
Global tags originate in the [global_tags] section of your Telegraf configuration file. When Telegraf starts, the configuration parser extracts these key-value pairs and stores them in Config.Tags, a map[string]string defined in config/config.go at line 289.
# telegraf.conf
[global_tags]
env = "production"
owner = "platform-team"
region = "us-east-1"
[agent]
interval = "10s"
According to the influxdata/telegraf source code, this map remains in memory as part of the global configuration object until the agent initializes individual input plugins.
Propagating Tags to Input Instances
During agent startup, Telegraf distributes global tags to each input through the RunningInput.SetDefaultTags method. This handshake occurs in models/running_input.go, where the method receives a copy of the global tag map and stores it in the defaultTags field:
// models/running_input.go
func (r *RunningInput) SetDefaultTags(tags map[string]string) {
r.defaultTags = tags
}
Each RunningInput instance maintains its own defaultTags map, ensuring that global tags are available during the metric gathering phase while isolating inputs from each other.
Merging Tags During Metric Creation
The actual tagging logic executes when an input produces metrics. Every input's Gather method creates raw telegraf.Metric objects, but before these reach the output sinks, RunningInput.MakeMetric processes them through the makeMetric function in models/makemetric.go (lines 7-34).
This function receives both the plugin-specific tags (r.Config.Tags) and the global tags (r.defaultTags), applying them in two distinct phases:
// models/makemetric.go – makeMetric (lines 7-34)
func makeMetric(metric telegraf.Metric,
nameOverride, namePrefix, nameSuffix string,
tags, globalTags map[string]string) telegraf.Metric {
// Apply name overrides, prefixes, and suffixes...
// Phase 1: Apply plugin-wide tags
for k, v := range tags {
if _, ok := metric.GetTag(k); !ok {
metric.AddTag(k, v)
}
}
// Phase 2: Apply global tags (only if key doesn't exist)
for k, v := range globalTags {
if _, ok := metric.GetTag(k); !ok {
metric.AddTag(k, v)
}
}
return metric
}
Plugin tags take precedence: The function checks metric.GetTag(k) before adding each tag, meaning plugin-specific tags override global tags when keys collide. This priority system allows inputs to enforce critical metadata while still inheriting default labels.
Handling Empty Metrics with always_include_global_tags
By default, Telegraf only adds global tags to metrics that contain at least one field. However, the [agent] configuration section exposes the always_include_global_tags boolean flag to modify this behavior.
When set to true, the agent forces global tags onto every metric—even empty ones. This flag is read from InputConfig.AlwaysIncludeGlobalTags in config/config.go and honored inside RunningInput.MakeMetric through a conditional block that re-invokes the tagging logic for metrics that would otherwise skip processing.
[agent]
interval = "10s"
always_include_global_tags = true
Parser Integration for Line-Based Inputs
Many Telegraf inputs rely on parsers to convert raw bytes into structured metrics. These parsers implement a SetDefaultTags method that mirrors the input plugin behavior, storing a copy of the global tag map for use during the parsing phase.
As implemented in plugins/parsers/*/*.go, parsers merge global tags into each newly created metric during the parsing routine, ensuring that line-based protocols (such as JSON, CSV, or InfluxDB line protocol) receive the same metadata treatment as native plugin metrics.
Complete Tagging Lifecycle Example
The following Go code demonstrates the tagging pipeline programmatically, simulating how RunningInput.MakeMetric processes a CPU metric:
package main
import (
"fmt"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/models"
)
func main() {
// Simulate an input creating a base metric
m, _ := telegraf.NewMetric(
"cpu",
map[string]string{"cpu": "cpu-total"},
map[string]interface{}{"usage_idle": 99.5},
time.Now(),
)
// Global tags from [global_tags] configuration
globalTags := map[string]string{
"env": "production",
"owner": "platform-team",
}
// Plugin-specific tags from [[inputs.cpu]] configuration
pluginTags := map[string]string{
"datacenter": "ny1",
}
// Apply tags as RunningInput.MakeMetric does
finalMetric := models.MakeMetric(
m, "", "", "", pluginTags, globalTags,
)
fmt.Printf("Final tags: %v\n", finalMetric.Tags())
// Output includes: cpu=cpu-total, datacenter=ny1,
// env=production, owner=platform-team
}
Summary
- Global tags are defined in the
[global_tags]configuration section and stored inConfig.Tagswithinconfig/config.go. - Tag propagation occurs via
RunningInput.SetDefaultTags, which stores global tags in each input'sdefaultTagsfield. - Merge priority follows a "plugin wins" rule:
makeMetricinmodels/makemetric.goapplies plugin tags first, then global tags only if the key does not already exist. - Empty metrics skip global tagging by default, unless
always_include_global_tags = trueis set in the[agent]section. - Parsers implement
SetDefaultTagsto ensure line-based inputs receive global metadata during the parsing phase.
Frequently Asked Questions
What happens when a plugin tag conflicts with a global tag?
Plugin tags take precedence over global tags. When makeMetric executes in models/makemetric.go, it checks metric.GetTag(k) before adding any tag. If the metric already contains the key from the plugin's local configuration, the global tag value is skipped, ensuring input-specific metadata overrides configuration defaults.
How do I force Telegraf to add global tags to metrics without fields?
Set always_include_global_tags = true in the [agent] section of your telegraf.conf. By default, Telegraf only decorates metrics that contain fields. Enabling this flag forces the agent to process global tags for every metric, including empty ones, by modifying the conditional logic in RunningInput.MakeMetric.
Where does Telegraf store global tags in memory?
Global tags reside in the Tags field of the Config struct, defined as map[string]string in config/config.go at line 289. During runtime, each RunningInput receives a copy of this map stored in its defaultTags field, as implemented in models/running_input.go.
Do output plugins receive metrics with global tags already applied?
Yes. The tagging occurs in the input stage before metrics reach the agent's internal metric channel. When RunningInput.MakeMetric returns the processed metric, it already contains both plugin-specific and global tags merged into the internal tag map. Output plugins serialize these fully decorated metrics without additional tag manipulation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →