How to Implement a Custom Telegraf Output Plugin: Complete Go Guide
To implement a custom Telegraf output plugin, create a Go struct that satisfies the telegraf.Output interface defined in output.go by implementing Connect(), Write(), and Close() methods, provide configuration via SampleConfig(), and register the plugin using outputs.Add() in an init() function.
Telegraf is the open-source, plugin-driven metrics collection agent maintained by InfluxData. When you need to send metrics to a proprietary API or unsupported datastore, you can extend the agent by implementing a custom Telegraf output plugin that conforms to the core interfaces in the influxdata/telegraf repository.
Understanding the telegraf.Output Interface
Every output plugin must satisfy the Output interface defined in output.go. This contract ensures Telegraf can manage the plugin lifecycle uniformly while handling buffering and retries automatically.
type Output interface {
PluginDescriber // required for SampleConfig()
Connect() error // called once when the plugin starts
Close() error // called once on shutdown (after all writes)
Write(metrics []Metric) error // called repeatedly with metric batches
}
The interface embeds PluginDescriber (from plugin.go) which requires a SampleConfig() method that returns a TOML configuration string.
Core Lifecycle Methods
The runtime in models/running_output.go invokes three primary methods that you must implement:
-
Connect()– Opens connections, allocates resources, and prepares the destination. Called once when the plugin starts. -
Write(metrics []Metric)– Receives batches of metrics for export. Called repeatedly during operation. Return an error to signal write failure and trigger retry logic. -
Close()– Gracefully shuts down connections and cleans up resources. Called once when Telegraf stops.
Step-by-Step Implementation Guide
Follow these steps to create a production-ready custom output plugin in the influxdata/telegraf codebase.
1. Create the Plugin Directory
Add a new directory under plugins/outputs/<myplugin>/ and declare package <myplugin> in your Go files. This mirrors the structure used by the reference implementation in plugins/outputs/file/file.go.
2. Define the Plugin Struct
Create a struct that holds configuration fields (tagged with TOML) and runtime objects like loggers or clients.
type MyOutput struct {
Endpoint string `toml:"endpoint"`
APIKey string `toml:"api_key"`
Log telegraf.Logger `toml:"-"` // injected by the runtime
}
3. Implement PluginDescriber
Add a SampleConfig() method that returns a string containing the default TOML configuration. Telegraf uses this to generate configuration examples.
func (m *MyOutput) SampleConfig() string {
return `
## Endpoint URL
endpoint = "https://api.example.com/metrics"
## API authentication key
api_key = "secret"
`
}
4. Implement Connect
Add resource initialization logic. Return an error if the destination is unreachable.
func (m *MyOutput) Connect() error {
// Initialize HTTP client, open database connection, etc.
return nil
}
5. Implement Write
Process the metric batch. Iterate over []telegraf.Metric, serialize if necessary, and write to your destination.
func (m *MyOutput) Write(metrics []telegraf.Metric) error {
for _, metric := range metrics {
// Export logic here
}
return nil
}
6. Implement Close
Clean up connections and resources to prevent goroutine leaks.
func (m *MyOutput) Close() error {
// Close connections, flush buffers
return nil
}
7. (Optional) Implement Initializer
If you need validation before Connect(), implement the Initializer interface from plugin.go:
func (m *MyOutput) Init() error {
// Validate configuration
return nil
}
8. Register the Plugin
In an init() function, call outputs.Add() to make your plugin discoverable. This registration pattern appears in every built-in output, including plugins/outputs/file/file.go.
func init() {
outputs.Add("myplugin", func() telegraf.Output {
return &MyOutput{}
})
}
Complete Working Example: Stdout Output
Below is a compile-ready custom Telegraf output plugin that writes metrics to standard output in InfluxDB line protocol format. Save this as plugins/outputs/stdout/stdout.go.
package stdout
import (
"fmt"
"os"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/outputs"
)
//go:embed sample.conf
var sampleConfig string
type Stdout struct {
Log telegraf.Logger `toml:"-"` // injected logger
}
func (*Stdout) SampleConfig() string { return sampleConfig }
func (s *Stdout) Connect() error { return nil }
func (s *Stdout) Close() error { return nil }
func (s *Stdout) Write(metrics []telegraf.Metric) error {
for _, m := range metrics {
line, err := m.MarshalLineProtocol()
if err != nil {
s.Log.Errorf("failed to serialize metric: %v", err)
continue
}
if _, err = fmt.Fprintln(os.Stdout, string(line)); err != nil {
s.Log.Errorf("failed to write metric: %v", err)
return err
}
}
return nil
}
func init() {
outputs.Add("stdout", func() telegraf.Output {
return &Stdout{}
})
}
Include a sample.conf file in the same directory:
## Example configuration for the stdout output
# no configuration required
After building Telegraf, reference the plugin in your configuration:
[[outputs.stdout]]
Optional Interfaces and Advanced Features
Beyond the core Output interface, the influxdata/telegraf repository defines several optional interfaces in plugin.go that enhance functionality:
-
Initializer– Provides anInit()error method for pre-connection validation. -
SetSerializer– Allows the plugin to receive atelegraf.Serializerfor encoding metrics (used when the output supports multiple formats). -
PluginWithID– Enables unique identification for the plugin instance.
Telegraf's runtime automatically detects these interfaces via type assertions and invokes their methods when present.
Plugin Lifecycle and Runtime Behavior
Understanding how the core executes your plugin helps you write efficient, fault-tolerant code. The models/running_output.go file contains the RunningOutput struct, which wraps your plugin and manages:
-
Buffering – Accumulating metrics before calling
Write()to reduce I/O overhead. -
Retry Logic – Re-invoking
Write()if the method returns an error, respecting the agent's retry limits. -
Metrics Tracking – Recording write statistics without plugin intervention.
-
Logger Injection – Setting the
Logfield via reflection if the struct contains atelegraf.Loggerfield tagged withtoml:"-".
When Telegraf starts, it instantiates your plugin via the factory function registered with outputs.Add(), calls Init() if implemented, then Connect(), and enters the write loop. During shutdown, it calls Close() after flushing any buffered data.
Summary
-
Implement the
telegraf.Outputinterface defined inoutput.gowithConnect(),Write(), andClose()methods. -
Embed configuration by implementing
SampleConfig()from thePluginDescriberinterface inplugin.go. -
Register in
init()usingoutputs.Add()to make the plugin available to the Telegraf agent. -
Place code in
plugins/outputs/<name>/following the structure ofplugins/outputs/file/file.go. -
Let the runtime handle buffering, retries, and statistics via
models/running_output.go.
Frequently Asked Questions
How do I test my custom output plugin without building all of Telegraf?
You can run unit tests within your plugin directory using go test ./plugins/outputs/<myplugin>/.... For integration testing, build Telegraf with your plugin included by ensuring your package is imported in plugins/outputs/all/all.go (which is typically auto-generated), then run the binary with a configuration file referencing your output.
Can my custom output plugin use Telegraf's built-in serializers?
Yes. Implement the SetSerializer interface (defined in plugin.go) by adding a SetSerializer(serializer telegraf.Serializer) method to your struct. Telegraf's plugin loader will inject the appropriate serializer based on the data_format configuration option, allowing you to reuse JSON, InfluxDB line protocol, or other formats without custom encoding logic.
What happens if my Write method returns an error?
When Write() returns an error, the RunningOutput in models/running_output.go retains the unwritten metrics in its buffer and retries the write according to the agent's metric_buffer_limit and retry settings. If retries are exhausted or the buffer fills, metrics may be dropped depending on your configuration, and an error is logged via the injected logger.
Where should I place my custom output plugin code in the repository?
Place your plugin source in plugins/outputs/<pluginname>/ within the Telegraf repository. Include a README.md documenting configuration options and a sample.conf file. The registration in your init() function automatically integrates the plugin into the build system, which discovers packages via plugins/outputs/all/all.go.
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 →