Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
93
plugins/inputs/example/README.md
Normal file
93
plugins/inputs/example/README.md
Normal file
|
@ -0,0 +1,93 @@
|
|||
# Example Input Plugin
|
||||
|
||||
The `example` plugin gathers metrics about example things. This description
|
||||
explains at a high level what the plugin does and provides links to where
|
||||
additional information can be found.
|
||||
|
||||
Telegraf minimum version: Telegraf x.x Plugin minimum tested version: x.x
|
||||
|
||||
⭐ Telegraf v1.0.0 <!-- introduction version -->
|
||||
🚩 Telegraf v1.10.0 <!-- deprecation version if any -->
|
||||
🔥 Telegraf v1.20.0 <!-- removal version if any -->
|
||||
🏷️ system <!-- plugin tags -->
|
||||
💻 all <!-- OS support -->
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# This is an example plugin
|
||||
[[inputs.example]]
|
||||
example_option = "example_value"
|
||||
```
|
||||
|
||||
Running `telegraf --usage <plugin-name>` also gives the sample TOML
|
||||
configuration.
|
||||
|
||||
### example_option
|
||||
|
||||
A more in depth description of an option can be provided here, but only do so if
|
||||
the option cannot be fully described in the sample config.
|
||||
|
||||
## Metrics
|
||||
|
||||
Here you should add an optional description and links to where the user can get
|
||||
more information about the measurements.
|
||||
|
||||
If the output is determined dynamically based on the input source, or there are
|
||||
more metrics than can reasonably be listed, describe how the input is mapped to
|
||||
the output.
|
||||
|
||||
- measurement1
|
||||
- tags:
|
||||
- tag1 (optional description)
|
||||
- tag2
|
||||
- fields:
|
||||
- field1 (type, unit)
|
||||
- field2 (float, percent)
|
||||
|
||||
- measurement2
|
||||
- tags:
|
||||
- tag3
|
||||
- fields:
|
||||
- field3 (integer, bytes)
|
||||
- field4 (integer, green=1 yellow=2 red=3)
|
||||
- field5 (string)
|
||||
- field6 (float)
|
||||
- field7 (boolean)
|
||||
|
||||
## Sample Queries
|
||||
|
||||
This section can contain some useful InfluxDB queries that can be used to get
|
||||
started with the plugin or to generate dashboards. For each query listed,
|
||||
describe at a high level what data is returned.
|
||||
|
||||
Get the max, mean, and min for the measurement in the last hour:
|
||||
|
||||
```sql
|
||||
SELECT max(field1), mean(field1), min(field1) FROM measurement1 WHERE tag1=bar AND time > now() - 1h GROUP BY tag
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
This optional section can provide basic troubleshooting steps that a user can
|
||||
perform.
|
||||
|
||||
## Example Output
|
||||
|
||||
This section shows example output in Line Protocol format. You can often use
|
||||
`telegraf --input-filter <plugin-name> --test` or use the `file` output to get
|
||||
this information.
|
||||
|
||||
```text
|
||||
measurement1,tag1=foo,tag2=bar field1=1i,field2=2.1 1453831884664956455
|
||||
measurement2,tag1=foo,tag2=bar,tag3=baz field3=1i 1453831884664956455
|
||||
```
|
139
plugins/inputs/example/example.go
Normal file
139
plugins/inputs/example/example.go
Normal file
|
@ -0,0 +1,139 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package example
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
// Example struct should be named the same as the Plugin
|
||||
type Example struct {
|
||||
// Example for a mandatory option to set a tag
|
||||
DeviceName string `toml:"device_name"`
|
||||
|
||||
// Config options are converted to the correct type automatically
|
||||
NumberFields int64 `toml:"number_fields"`
|
||||
|
||||
// We can also use booleans and have diverging names between user-configuration options and struct members
|
||||
EnableRandomVariable bool `toml:"enable_random"`
|
||||
|
||||
// Example of passing a duration option allowing the format of e.g. "100ms", "5m" or "1h"
|
||||
Timeout config.Duration `toml:"timeout"`
|
||||
|
||||
// Example of passing a password/token/username or other sensitive data with the secret-store
|
||||
UserName config.Secret `toml:"username"`
|
||||
Password config.Secret `toml:"password"`
|
||||
|
||||
// Telegraf logging facility
|
||||
// The exact name is important to allow automatic initialization by telegraf.
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
// This is a non-exported internal state.
|
||||
count int64
|
||||
}
|
||||
|
||||
func (*Example) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
// Init can be implemented to do one-time processing stuff like initializing variables
|
||||
func (m *Example) Init() error {
|
||||
// Check your options according to your requirements
|
||||
if m.DeviceName == "" {
|
||||
return errors.New("device name cannot be empty")
|
||||
}
|
||||
|
||||
// Set your defaults.
|
||||
// Please note: In golang all fields are initialized to their nil value, so you should not
|
||||
// set these fields if the nil value is what you want (e.g. for booleans).
|
||||
if m.NumberFields < 1 {
|
||||
m.Log.Debugf("Setting number of fields to default from invalid value %d", m.NumberFields)
|
||||
m.NumberFields = 2
|
||||
}
|
||||
|
||||
// Check using the secret-store
|
||||
if m.UserName.Empty() {
|
||||
// For example, use a default value
|
||||
m.Log.Debug("using default username")
|
||||
}
|
||||
|
||||
// Retrieve credentials using the secret-store
|
||||
password, err := m.Password.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting password failed: %w", err)
|
||||
}
|
||||
defer password.Destroy()
|
||||
|
||||
// Initialize your internal states
|
||||
m.count = 1
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gather defines what data the plugin will gather.
|
||||
func (m *Example) Gather(acc telegraf.Accumulator) error {
|
||||
// Imagine some completely arbitrary error occurring here
|
||||
if m.NumberFields > 10 {
|
||||
return errors.New("too many fields")
|
||||
}
|
||||
|
||||
// For illustration, we gather three metrics in one go
|
||||
for run := 0; run < 3; run++ {
|
||||
// Imagine an error occurs here, but you want to keep the other
|
||||
// metrics, then you cannot simply return, as this would drop
|
||||
// all later metrics. Simply accumulate errors in this case
|
||||
// and ignore the metric.
|
||||
if m.EnableRandomVariable && m.DeviceName == "flappy" && run > 1 {
|
||||
acc.AddError(errors.New("too many runs for random values"))
|
||||
continue
|
||||
}
|
||||
|
||||
// Construct the fields
|
||||
fields := map[string]interface{}{"count": m.count}
|
||||
for i := int64(1); i < m.NumberFields; i++ {
|
||||
name := fmt.Sprintf("field%d", i)
|
||||
var err error
|
||||
value := big.NewInt(0)
|
||||
if m.EnableRandomVariable {
|
||||
value, err = rand.Int(rand.Reader, big.NewInt(math.MaxUint32))
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
fields[name] = float64(value.Int64())
|
||||
}
|
||||
|
||||
// Construct the tags
|
||||
tags := map[string]string{"device": m.DeviceName}
|
||||
|
||||
// Add the metric with the current timestamp
|
||||
acc.AddFields("example", fields, tags)
|
||||
|
||||
m.count++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Register the plugin
|
||||
func init() {
|
||||
inputs.Add("example", func() telegraf.Input {
|
||||
return &Example{
|
||||
// Set the default timeout here to distinguish it from the user setting it to zero
|
||||
Timeout: config.Duration(100 * time.Millisecond),
|
||||
}
|
||||
})
|
||||
}
|
439
plugins/inputs/example/example_test.go
Normal file
439
plugins/inputs/example/example_test.go
Normal file
|
@ -0,0 +1,439 @@
|
|||
package example
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
// This file should contain a set of unit-tests to cover your plugin. This will ease
|
||||
// spotting bugs and mistakes when later modifying or extending the functionality.
|
||||
// To do so, please write one 'TestXYZ' function per 'case' e.g. default init,
|
||||
// things that should fail or expected values from a mockup.
|
||||
|
||||
func TestInitDefault(t *testing.T) {
|
||||
// This test should succeed with the default initialization.
|
||||
|
||||
// Use whatever you use in the init() function plus the mandatory options.
|
||||
// ATTENTION: Always initialize the "Log" as you will get SIGSEGV otherwise.
|
||||
plugin := &Example{
|
||||
DeviceName: "test",
|
||||
Timeout: config.Duration(100 * time.Millisecond),
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
|
||||
// Test the initialization succeeds
|
||||
require.NoError(t, plugin.Init())
|
||||
|
||||
// Also test that default values are set correctly
|
||||
require.Equal(t, config.Duration(100*time.Millisecond), plugin.Timeout)
|
||||
require.Equal(t, "test", plugin.DeviceName)
|
||||
require.Equal(t, int64(2), plugin.NumberFields)
|
||||
}
|
||||
|
||||
func TestInitFail(t *testing.T) {
|
||||
// You should also test for your safety nets to work i.e. you get errors for
|
||||
// invalid configuration-option values. So check your error paths in Init()
|
||||
// and check if you reach them
|
||||
|
||||
// We setup a table-test here to specify "setting" - "expected error" values.
|
||||
// Even though it seems overkill here for the example plugin, we reuse this structure
|
||||
// later for checking the metrics
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *Example
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "all empty",
|
||||
plugin: &Example{},
|
||||
expected: "device name cannot be empty",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Always initialize the logger to avoid SIGSEGV. This is done automatically by
|
||||
// telegraf during normal operation.
|
||||
tt.plugin.Log = testutil.Logger{}
|
||||
err := tt.plugin.Init()
|
||||
require.Error(t, err)
|
||||
require.EqualError(t, err, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFixedValue(t *testing.T) {
|
||||
// You can organize the test e.g. by operation mode (like we do here random vs. fixed), by features or
|
||||
// by different metrics gathered. Please choose the partitioning most suited for your plugin
|
||||
|
||||
// We again setup a table-test here to specify "setting" - "expected output metric" pairs.
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *Example
|
||||
expected []telegraf.Metric
|
||||
}{
|
||||
{
|
||||
name: "count only",
|
||||
plugin: &Example{
|
||||
DeviceName: "test",
|
||||
NumberFields: 1,
|
||||
},
|
||||
expected: []telegraf.Metric{
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 1,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 2,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 3,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default settings",
|
||||
plugin: &Example{
|
||||
DeviceName: "test",
|
||||
},
|
||||
expected: []telegraf.Metric{
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 1,
|
||||
"field1": float64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 2,
|
||||
"field1": float64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 3,
|
||||
"field1": float64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "more fields",
|
||||
plugin: &Example{
|
||||
DeviceName: "test",
|
||||
NumberFields: 4,
|
||||
},
|
||||
expected: []telegraf.Metric{
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 1,
|
||||
"field1": float64(0),
|
||||
"field2": float64(0),
|
||||
"field3": float64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 2,
|
||||
"field1": float64(0),
|
||||
"field2": float64(0),
|
||||
"field3": float64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 3,
|
||||
"field1": float64(0),
|
||||
"field2": float64(0),
|
||||
"field3": float64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
|
||||
tt.plugin.Log = testutil.Logger{}
|
||||
require.NoError(t, tt.plugin.Init())
|
||||
|
||||
// Call gather and check no error occurs. In case you use acc.AddError() somewhere
|
||||
// in your code, it is not sufficient to only check the return value of Gather().
|
||||
require.NoError(t, tt.plugin.Gather(&acc))
|
||||
require.Empty(t, acc.Errors, "found errors accumulated by acc.AddError()")
|
||||
|
||||
// Wait for the expected number of metrics to avoid flaky tests due to
|
||||
// race conditions.
|
||||
acc.Wait(len(tt.expected))
|
||||
|
||||
// Compare the metrics in a convenient way. Here we ignore
|
||||
// the metric time during comparison as we cannot inject the time
|
||||
// during test. For more comparison options check testutil package.
|
||||
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomValue(t *testing.T) {
|
||||
// Sometimes, you cannot know the exact outcome of the gather cycle e.g. if the gathering involves random data.
|
||||
// However, you should check the result nevertheless, applying as many conditions as you can.
|
||||
|
||||
// We again setup a table-test here to specify "setting" - "expected output metric" pairs.
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *Example
|
||||
template telegraf.Metric
|
||||
}{
|
||||
{
|
||||
name: "count only",
|
||||
plugin: &Example{
|
||||
DeviceName: "test",
|
||||
NumberFields: 1,
|
||||
EnableRandomVariable: true,
|
||||
},
|
||||
template: testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 1,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "default settings",
|
||||
plugin: &Example{
|
||||
DeviceName: "test",
|
||||
EnableRandomVariable: true,
|
||||
},
|
||||
template: testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 1,
|
||||
"field1": float64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "more fields",
|
||||
plugin: &Example{
|
||||
DeviceName: "test",
|
||||
NumberFields: 4,
|
||||
EnableRandomVariable: true,
|
||||
},
|
||||
template: testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "test",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 1,
|
||||
"field1": float64(0),
|
||||
"field2": float64(0),
|
||||
"field3": float64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
|
||||
tt.plugin.Log = testutil.Logger{}
|
||||
require.NoError(t, tt.plugin.Init())
|
||||
|
||||
// Call gather and check no error occurs. In case you use acc.AddError() somewhere
|
||||
// in your code, it is not sufficient to only check the return value of Gather().
|
||||
require.NoError(t, tt.plugin.Gather(&acc))
|
||||
require.Empty(t, acc.Errors, "found errors accumulated by acc.AddError()")
|
||||
|
||||
// Wait for the expected number of metrics to avoid flaky tests due to
|
||||
// race conditions.
|
||||
acc.Wait(3)
|
||||
|
||||
// Compare all aspects of the metric that are known to you
|
||||
for i, m := range acc.GetTelegrafMetrics() {
|
||||
require.Equal(t, m.Name(), tt.template.Name())
|
||||
require.Equal(t, m.Tags(), tt.template.Tags())
|
||||
|
||||
// Check if all expected fields are there
|
||||
fields := m.Fields()
|
||||
for k := range tt.template.Fields() {
|
||||
if k == "count" {
|
||||
require.Equal(t, fields["count"], int64(i+1))
|
||||
continue
|
||||
}
|
||||
_, found := fields[k]
|
||||
require.Truef(t, found, "field %q not found", k)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatherFail(t *testing.T) {
|
||||
// You should also test for error conditions in your Gather() method. Try to cover all error paths.
|
||||
|
||||
// We again setup a table-test here to specify "setting" - "expected error" pair.
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *Example
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "too many fields",
|
||||
plugin: &Example{
|
||||
DeviceName: "test",
|
||||
NumberFields: 11,
|
||||
},
|
||||
expected: "too many fields",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
|
||||
tt.plugin.Log = testutil.Logger{}
|
||||
require.NoError(t, tt.plugin.Init())
|
||||
|
||||
err := tt.plugin.Gather(&acc)
|
||||
require.Error(t, err)
|
||||
require.EqualError(t, err, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomValueFailPartial(t *testing.T) {
|
||||
// You should also test for error conditions in your Gather() with partial output. This is required when
|
||||
// using acc.AddError() as Gather() might succeed (return nil) but there are some metrics missing.
|
||||
|
||||
// We again setup a table-test here to specify "setting" - "expected output metric" and "errors".
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *Example
|
||||
expected []telegraf.Metric
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "flappy gather",
|
||||
plugin: &Example{
|
||||
DeviceName: "flappy",
|
||||
NumberFields: 1,
|
||||
EnableRandomVariable: true,
|
||||
},
|
||||
expected: []telegraf.Metric{
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "flappy",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 1,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
testutil.MustMetric(
|
||||
"example",
|
||||
map[string]string{
|
||||
"device": "flappy",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"count": 2,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
expectedErr: "too many runs for random values",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
|
||||
tt.plugin.Log = testutil.Logger{}
|
||||
require.NoError(t, tt.plugin.Init())
|
||||
|
||||
// Call gather and check no error occurs. However, we expect an error accumulated by acc.AddError()
|
||||
require.NoError(t, tt.plugin.Gather(&acc))
|
||||
|
||||
// Wait for the expected number of metrics to avoid flaky tests due to
|
||||
// race conditions.
|
||||
acc.Wait(len(tt.expected))
|
||||
|
||||
// Check the accumulated errors
|
||||
require.Len(t, acc.Errors, 1)
|
||||
require.EqualError(t, acc.Errors[0], tt.expectedErr)
|
||||
|
||||
// Compare the expected partial metrics.
|
||||
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||||
})
|
||||
}
|
||||
}
|
3
plugins/inputs/example/sample.conf
Normal file
3
plugins/inputs/example/sample.conf
Normal file
|
@ -0,0 +1,3 @@
|
|||
# This is an example plugin
|
||||
[[inputs.example]]
|
||||
example_option = "example_value"
|
Loading…
Add table
Add a link
Reference in a new issue