1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,114 @@
# Docker Log Input Plugin
This plugin uses the [Docker Engine API][api] to gather logs from running
docker containers.
> [!NOTE]
> This plugin works only for containers with the `local` or `json-file` or
> `journald` logging driver. Please make sure Telegraf has sufficient
> permissions to access the configured endpoint!
⭐ Telegraf v1.12.0
🏷️ containers, logging
💻 all
[api]: https://docs.docker.com/engine/api
## 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
# Read logging output from the Docker engine
[[inputs.docker_log]]
## Docker Endpoint
## To use TCP, set endpoint = "tcp://[ip]:[port]"
## To use environment variables (ie, docker-machine), set endpoint = "ENV"
# endpoint = "unix:///var/run/docker.sock"
## When true, container logs are read from the beginning; otherwise reading
## begins at the end of the log. If state-persistence is enabled for Telegraf,
## the reading continues at the last previously processed timestamp.
# from_beginning = false
## Timeout for Docker API calls.
# timeout = "5s"
## Containers to include and exclude. Globs accepted.
## Note that an empty array for both will include all containers
# container_name_include = []
# container_name_exclude = []
## Container states to include and exclude. Globs accepted.
## When empty only containers in the "running" state will be captured.
# container_state_include = []
# container_state_exclude = []
## docker labels to include and exclude as tags. Globs accepted.
## Note that an empty array for both will include all labels as tags
# docker_label_include = []
# docker_label_exclude = []
## Set the source tag for the metrics to the container ID hostname, eg first 12 chars
source_tag = false
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false
```
### Environment Configuration
When using the `"ENV"` endpoint, the connection is configured using the
[CLI Docker environment variables][env]
[env]: https://godoc.org/github.com/moby/moby/client#NewEnvClient
## source tag
Selecting the containers can be tricky if you have many containers with the same
name. To alleviate this issue you can set the below value to `true`
```toml
source_tag = true
```
This will cause all data points to have the `source` tag be set to the first 12
characters of the container id. The first 12 characters is the common hostname
for containers that have no explicit hostname set, as defined by docker.
## Metrics
- docker_log
- tags:
- container_image
- container_version
- container_name
- stream (stdout, stderr, or tty)
- source
- fields:
- container_id
- message
## Example Output
```text
docker_log,container_image=telegraf,container_name=sharp_bell,container_version=alpine,stream=stderr container_id="371ee5d3e58726112f499be62cddef800138ca72bbba635ed2015fbf475b1023",message="2019-06-19T03:11:11Z I! [agent] Config: Interval:10s, Quiet:false, Hostname:\"371ee5d3e587\", Flush Interval:10s" 1560913872000000000
docker_log,container_image=telegraf,container_name=sharp_bell,container_version=alpine,stream=stderr container_id="371ee5d3e58726112f499be62cddef800138ca72bbba635ed2015fbf475b1023",message="2019-06-19T03:11:11Z I! Tags enabled: host=371ee5d3e587" 1560913872000000000
docker_log,container_image=telegraf,container_name=sharp_bell,container_version=alpine,stream=stderr container_id="371ee5d3e58726112f499be62cddef800138ca72bbba635ed2015fbf475b1023",message="2019-06-19T03:11:11Z I! Loaded outputs: file" 1560913872000000000
docker_log,container_image=telegraf,container_name=sharp_bell,container_version=alpine,stream=stderr container_id="371ee5d3e58726112f499be62cddef800138ca72bbba635ed2015fbf475b1023",message="2019-06-19T03:11:11Z I! Loaded processors:" 1560913872000000000
docker_log,container_image=telegraf,container_name=sharp_bell,container_version=alpine,stream=stderr container_id="371ee5d3e58726112f499be62cddef800138ca72bbba635ed2015fbf475b1023",message="2019-06-19T03:11:11Z I! Loaded aggregators:" 1560913872000000000
docker_log,container_image=telegraf,container_name=sharp_bell,container_version=alpine,stream=stderr container_id="371ee5d3e58726112f499be62cddef800138ca72bbba635ed2015fbf475b1023",message="2019-06-19T03:11:11Z I! Loaded inputs: net" 1560913872000000000
docker_log,container_image=telegraf,container_name=sharp_bell,container_version=alpine,stream=stderr container_id="371ee5d3e58726112f499be62cddef800138ca72bbba635ed2015fbf475b1023",message="2019-06-19T03:11:11Z I! Using config file: /etc/telegraf/telegraf.conf" 1560913872000000000
docker_log,container_image=telegraf,container_name=sharp_bell,container_version=alpine,stream=stderr container_id="371ee5d3e58726112f499be62cddef800138ca72bbba635ed2015fbf475b1023",message="2019-06-19T03:11:11Z I! Starting Telegraf 1.10.4" 1560913872000000000
```

View file

@ -0,0 +1,70 @@
package docker_log
import (
"context"
"crypto/tls"
"io"
"net/http"
"github.com/docker/docker/api/types/container"
docker "github.com/docker/docker/client"
)
// This file is inherited from telegraf docker input plugin
var (
version = "1.24"
defaultHeaders = map[string]string{"User-Agent": "engine-api-cli-1.0"}
)
type dockerClient interface {
// ContainerList lists the containers in the Docker environment.
ContainerList(ctx context.Context, options container.ListOptions) ([]container.Summary, error)
// ContainerLogs retrieves the logs of a specific container.
ContainerLogs(ctx context.Context, containerID string, options container.LogsOptions) (io.ReadCloser, error)
// ContainerInspect inspects a specific container and retrieves its details.
ContainerInspect(ctx context.Context, containerID string) (container.InspectResponse, error)
}
func newEnvClient() (dockerClient, error) {
client, err := docker.NewClientWithOpts(docker.FromEnv)
if err != nil {
return nil, err
}
return &socketClient{client}, nil
}
func newClient(host string, tlsConfig *tls.Config) (dockerClient, error) {
transport := &http.Transport{
TLSClientConfig: tlsConfig,
}
httpClient := &http.Client{Transport: transport}
client, err := docker.NewClientWithOpts(
docker.WithHTTPHeaders(defaultHeaders),
docker.WithHTTPClient(httpClient),
docker.WithVersion(version),
docker.WithHost(host))
if err != nil {
return nil, err
}
return &socketClient{client}, nil
}
type socketClient struct {
client *docker.Client
}
// ContainerList lists the containers in the Docker environment.
func (c *socketClient) ContainerList(ctx context.Context, options container.ListOptions) ([]container.Summary, error) {
return c.client.ContainerList(ctx, options)
}
// ContainerLogs retrieves the logs of a specific container.
func (c *socketClient) ContainerLogs(ctx context.Context, containerID string, options container.LogsOptions) (io.ReadCloser, error) {
return c.client.ContainerLogs(ctx, containerID, options)
}
// ContainerInspect inspects a specific container and retrieves its details.
func (c *socketClient) ContainerInspect(ctx context.Context, containerID string) (container.InspectResponse, error) {
return c.client.ContainerInspect(ctx, containerID)
}

View file

@ -0,0 +1,498 @@
//go:generate ../../../tools/readme_config_includer/generator
package docker_log
import (
"bufio"
"bytes"
"context"
"crypto/tls"
_ "embed"
"errors"
"fmt"
"io"
"strings"
"sync"
"time"
"unicode"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/pkg/stdcopy"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/filter"
"github.com/influxdata/telegraf/internal/docker"
common_tls "github.com/influxdata/telegraf/plugins/common/tls"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
var (
// ensure *DockerLogs implements telegraf.ServiceInput
_ telegraf.ServiceInput = (*DockerLogs)(nil)
containerStates = []string{"created", "restarting", "running", "removing", "paused", "exited", "dead"}
)
const (
defaultEndpoint = "unix:///var/run/docker.sock"
)
type DockerLogs struct {
Endpoint string `toml:"endpoint"`
FromBeginning bool `toml:"from_beginning"`
Timeout config.Duration `toml:"timeout"`
LabelInclude []string `toml:"docker_label_include"`
LabelExclude []string `toml:"docker_label_exclude"`
ContainerInclude []string `toml:"container_name_include"`
ContainerExclude []string `toml:"container_name_exclude"`
ContainerStateInclude []string `toml:"container_state_include"`
ContainerStateExclude []string `toml:"container_state_exclude"`
IncludeSourceTag bool `toml:"source_tag"`
common_tls.ClientConfig
newEnvClient func() (dockerClient, error)
newClient func(string, *tls.Config) (dockerClient, error)
client dockerClient
labelFilter filter.Filter
containerFilter filter.Filter
stateFilter filter.Filter
opts container.ListOptions
wg sync.WaitGroup
mu sync.Mutex
containerList map[string]context.CancelFunc
// State of the plugin mapping container-ID to the timestamp of the
// last record processed
lastRecord map[string]time.Time
lastRecordMtx sync.Mutex
}
func (*DockerLogs) SampleConfig() string {
return sampleConfig
}
func (d *DockerLogs) Init() error {
var err error
if d.Endpoint == "ENV" {
d.client, err = d.newEnvClient()
if err != nil {
return err
}
} else {
tlsConfig, err := d.ClientConfig.TLSConfig()
if err != nil {
return err
}
d.client, err = d.newClient(d.Endpoint, tlsConfig)
if err != nil {
return err
}
}
// Create filters
err = d.createLabelFilters()
if err != nil {
return err
}
err = d.createContainerFilters()
if err != nil {
return err
}
err = d.createContainerStateFilters()
if err != nil {
return err
}
filterArgs := filters.NewArgs()
for _, state := range containerStates {
if d.stateFilter.Match(state) {
filterArgs.Add("status", state)
}
}
if filterArgs.Len() != 0 {
d.opts = container.ListOptions{
Filters: filterArgs,
}
}
d.lastRecord = make(map[string]time.Time)
return nil
}
// Start is a noop which is required for a *DockerLogs to implement the telegraf.ServiceInput interface
func (*DockerLogs) Start(telegraf.Accumulator) error {
return nil
}
func (d *DockerLogs) GetState() interface{} {
d.lastRecordMtx.Lock()
recordOffsets := make(map[string]time.Time, len(d.lastRecord))
for k, v := range d.lastRecord {
recordOffsets[k] = v
}
d.lastRecordMtx.Unlock()
return recordOffsets
}
func (d *DockerLogs) SetState(state interface{}) error {
recordOffsets, ok := state.(map[string]time.Time)
if !ok {
return fmt.Errorf("state has wrong type %T", state)
}
d.lastRecordMtx.Lock()
for k, v := range recordOffsets {
d.lastRecord[k] = v
}
d.lastRecordMtx.Unlock()
return nil
}
func (d *DockerLogs) Gather(acc telegraf.Accumulator) error {
ctx := context.Background()
acc.SetPrecision(time.Nanosecond)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.Timeout))
defer cancel()
containers, err := d.client.ContainerList(ctx, d.opts)
if err != nil {
return err
}
for _, cntnr := range containers {
if d.containerInContainerList(cntnr.ID) {
continue
}
containerName := d.matchedContainerName(cntnr.Names)
if containerName == "" {
continue
}
ctx, cancel := context.WithCancel(context.Background())
d.addToContainerList(cntnr.ID, cancel)
// Start a new goroutine for every new container that has logs to collect
d.wg.Add(1)
go func(container container.Summary) {
defer d.wg.Done()
defer d.removeFromContainerList(container.ID)
err = d.tailContainerLogs(ctx, acc, container, containerName)
if err != nil && !errors.Is(err, context.Canceled) {
acc.AddError(err)
}
}(cntnr)
}
return nil
}
func (d *DockerLogs) Stop() {
d.cancelTails()
d.wg.Wait()
}
func (d *DockerLogs) addToContainerList(containerID string, cancel context.CancelFunc) {
d.mu.Lock()
defer d.mu.Unlock()
d.containerList[containerID] = cancel
}
func (d *DockerLogs) removeFromContainerList(containerID string) {
d.mu.Lock()
defer d.mu.Unlock()
delete(d.containerList, containerID)
}
func (d *DockerLogs) containerInContainerList(containerID string) bool {
d.mu.Lock()
defer d.mu.Unlock()
_, ok := d.containerList[containerID]
return ok
}
func (d *DockerLogs) cancelTails() {
d.mu.Lock()
defer d.mu.Unlock()
for _, cancel := range d.containerList {
cancel()
}
}
func (d *DockerLogs) matchedContainerName(names []string) string {
// Check if all container names are filtered; in practice I believe
// this array is always of length 1.
for _, name := range names {
trimmedName := strings.TrimPrefix(name, "/")
if !strings.Contains(trimmedName, "/") {
match := d.containerFilter.Match(trimmedName)
if match {
return trimmedName
}
}
}
return ""
}
func (d *DockerLogs) hasTTY(ctx context.Context, cntnr container.Summary) (bool, error) {
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.Timeout))
defer cancel()
c, err := d.client.ContainerInspect(ctx, cntnr.ID)
if err != nil {
return false, err
}
return c.Config.Tty, nil
}
func (d *DockerLogs) tailContainerLogs(
ctx context.Context,
acc telegraf.Accumulator,
cntnr container.Summary,
containerName string,
) error {
imageName, imageVersion := docker.ParseImage(cntnr.Image)
tags := map[string]string{
"container_name": containerName,
"container_image": imageName,
"container_version": imageVersion,
}
if d.IncludeSourceTag {
tags["source"] = hostnameFromID(cntnr.ID)
}
// Add matching container labels as tags
for k, label := range cntnr.Labels {
if d.labelFilter.Match(k) {
tags[k] = label
}
}
hasTTY, err := d.hasTTY(ctx, cntnr)
if err != nil {
return err
}
since := time.Time{}.Format(time.RFC3339Nano)
if !d.FromBeginning {
d.lastRecordMtx.Lock()
if ts, ok := d.lastRecord[cntnr.ID]; ok {
since = ts.Format(time.RFC3339Nano)
}
d.lastRecordMtx.Unlock()
}
logOptions := container.LogsOptions{
ShowStdout: true,
ShowStderr: true,
Timestamps: true,
Details: false,
Follow: true,
Since: since,
}
logReader, err := d.client.ContainerLogs(ctx, cntnr.ID, logOptions)
if err != nil {
return err
}
// If the container is using a TTY, there is only a single stream
// (stdout), and data is copied directly from the container output stream,
// no extra multiplexing or headers.
//
// If the container is *not* using a TTY, streams for stdout and stderr are
// multiplexed.
var last time.Time
if hasTTY {
last, err = tailStream(acc, tags, cntnr.ID, logReader, "tty")
} else {
last, err = tailMultiplexed(acc, tags, cntnr.ID, logReader)
}
if err != nil {
return err
}
if ts, ok := d.lastRecord[cntnr.ID]; !ok || ts.Before(last) {
d.lastRecordMtx.Lock()
d.lastRecord[cntnr.ID] = last
d.lastRecordMtx.Unlock()
}
return nil
}
func parseLine(line []byte) (time.Time, string, error) {
parts := bytes.SplitN(line, []byte(" "), 2)
if len(parts) == 1 {
parts = append(parts, []byte(""))
}
tsString := string(parts[0])
// Keep any leading space, but remove whitespace from end of line.
// This preserves space in, for example, stacktraces, while removing
// annoying end of line characters and is similar to how other logging
// plugins such as syslog behave.
message := bytes.TrimRightFunc(parts[1], unicode.IsSpace)
ts, err := time.Parse(time.RFC3339Nano, tsString)
if err != nil {
return time.Time{}, "", fmt.Errorf("error parsing timestamp %q: %w", tsString, err)
}
return ts, string(message), nil
}
func tailStream(
acc telegraf.Accumulator,
baseTags map[string]string,
containerID string,
reader io.ReadCloser,
stream string,
) (time.Time, error) {
defer reader.Close()
tags := make(map[string]string, len(baseTags)+1)
for k, v := range baseTags {
tags[k] = v
}
tags["stream"] = stream
r := bufio.NewReaderSize(reader, 64*1024)
var lastTS time.Time
for {
line, err := r.ReadBytes('\n')
if len(line) != 0 {
ts, message, err := parseLine(line)
if err != nil {
acc.AddError(err)
} else {
acc.AddFields("docker_log", map[string]interface{}{
"container_id": containerID,
"message": message,
}, tags, ts)
}
// Store the last processed timestamp
if ts.After(lastTS) {
lastTS = ts
}
}
if err != nil {
if err == io.EOF {
return lastTS, nil
}
return time.Time{}, err
}
}
}
func tailMultiplexed(
acc telegraf.Accumulator,
tags map[string]string,
containerID string,
src io.ReadCloser,
) (time.Time, error) {
outReader, outWriter := io.Pipe()
errReader, errWriter := io.Pipe()
var tsStdout, tsStderr time.Time
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
var err error
tsStdout, err = tailStream(acc, tags, containerID, outReader, "stdout")
if err != nil {
acc.AddError(err)
}
}()
wg.Add(1)
go func() {
defer wg.Done()
var err error
tsStderr, err = tailStream(acc, tags, containerID, errReader, "stderr")
if err != nil {
acc.AddError(err)
}
}()
_, err := stdcopy.StdCopy(outWriter, errWriter, src)
// Ignore the returned errors as we cannot do anything if the closing fails
_ = outWriter.Close()
_ = errWriter.Close()
_ = src.Close()
wg.Wait()
if err != nil {
return time.Time{}, err
}
if tsStdout.After(tsStderr) {
return tsStdout, nil
}
return tsStderr, nil
}
// Following few functions have been inherited from telegraf docker input plugin
func (d *DockerLogs) createContainerFilters() error {
containerFilter, err := filter.NewIncludeExcludeFilter(d.ContainerInclude, d.ContainerExclude)
if err != nil {
return err
}
d.containerFilter = containerFilter
return nil
}
func (d *DockerLogs) createLabelFilters() error {
labelFilter, err := filter.NewIncludeExcludeFilter(d.LabelInclude, d.LabelExclude)
if err != nil {
return err
}
d.labelFilter = labelFilter
return nil
}
func (d *DockerLogs) createContainerStateFilters() error {
if len(d.ContainerStateInclude) == 0 && len(d.ContainerStateExclude) == 0 {
d.ContainerStateInclude = []string{"running"}
}
stateFilter, err := filter.NewIncludeExcludeFilter(d.ContainerStateInclude, d.ContainerStateExclude)
if err != nil {
return err
}
d.stateFilter = stateFilter
return nil
}
func hostnameFromID(id string) string {
if len(id) > 12 {
return id[0:12]
}
return id
}
func init() {
inputs.Add("docker_log", func() telegraf.Input {
return &DockerLogs{
Timeout: config.Duration(time.Second * 5),
Endpoint: defaultEndpoint,
newEnvClient: newEnvClient,
newClient: newClient,
containerList: make(map[string]context.CancelFunc),
}
})
}

View file

@ -0,0 +1,178 @@
package docker_log
import (
"bytes"
"context"
"crypto/tls"
"io"
"testing"
"time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/stdcopy"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/testutil"
)
type mockClient struct {
ContainerListF func() ([]container.Summary, error)
ContainerInspectF func() (container.InspectResponse, error)
ContainerLogsF func() (io.ReadCloser, error)
}
func (c *mockClient) ContainerList(context.Context, container.ListOptions) ([]container.Summary, error) {
return c.ContainerListF()
}
func (c *mockClient) ContainerInspect(context.Context, string) (container.InspectResponse, error) {
return c.ContainerInspectF()
}
func (c *mockClient) ContainerLogs(context.Context, string, container.LogsOptions) (io.ReadCloser, error) {
return c.ContainerLogsF()
}
type response struct {
io.Reader
}
func (*response) Close() error {
return nil
}
func mustParse(layout, value string) time.Time {
tm, err := time.Parse(layout, value)
if err != nil {
panic(err)
}
return tm
}
func Test(t *testing.T) {
tests := []struct {
name string
client *mockClient
expected []telegraf.Metric
}{
{
name: "no containers",
client: &mockClient{
ContainerListF: func() ([]container.Summary, error) {
return nil, nil
},
},
},
{
name: "one container tty",
client: &mockClient{
ContainerListF: func() ([]container.Summary, error) {
return []container.Summary{
{
ID: "deadbeef",
Names: []string{"/telegraf"},
Image: "influxdata/telegraf:1.11.0",
},
}, nil
},
ContainerInspectF: func() (container.InspectResponse, error) {
return container.InspectResponse{
Config: &container.Config{
Tty: true,
},
}, nil
},
ContainerLogsF: func() (io.ReadCloser, error) {
return &response{Reader: bytes.NewBufferString("2020-04-28T18:43:16.432691200Z hello\n")}, nil
},
},
expected: []telegraf.Metric{
testutil.MustMetric(
"docker_log",
map[string]string{
"container_name": "telegraf",
"container_image": "influxdata/telegraf",
"container_version": "1.11.0",
"stream": "tty",
"source": "deadbeef",
},
map[string]interface{}{
"container_id": "deadbeef",
"message": "hello",
},
mustParse(time.RFC3339Nano, "2020-04-28T18:43:16.432691200Z"),
),
},
},
{
name: "one container multiplex",
client: &mockClient{
ContainerListF: func() ([]container.Summary, error) {
return []container.Summary{
{
ID: "deadbeef",
Names: []string{"/telegraf"},
Image: "influxdata/telegraf:1.11.0",
},
}, nil
},
ContainerInspectF: func() (container.InspectResponse, error) {
return container.InspectResponse{
Config: &container.Config{
Tty: false,
},
}, nil
},
ContainerLogsF: func() (io.ReadCloser, error) {
var buf bytes.Buffer
w := stdcopy.NewStdWriter(&buf, stdcopy.Stdout)
_, err := w.Write([]byte("2020-04-28T18:42:16.432691200Z hello from stdout"))
return &response{Reader: &buf}, err
},
},
expected: []telegraf.Metric{
testutil.MustMetric(
"docker_log",
map[string]string{
"container_name": "telegraf",
"container_image": "influxdata/telegraf",
"container_version": "1.11.0",
"stream": "stdout",
"source": "deadbeef",
},
map[string]interface{}{
"container_id": "deadbeef",
"message": "hello from stdout",
},
mustParse(time.RFC3339Nano, "2020-04-28T18:42:16.432691200Z"),
),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
plugin := &DockerLogs{
Timeout: config.Duration(time.Second * 5),
newClient: func(string, *tls.Config) (dockerClient, error) { return tt.client, nil },
containerList: make(map[string]context.CancelFunc),
IncludeSourceTag: true,
}
err := plugin.Init()
require.NoError(t, err)
err = plugin.Gather(&acc)
require.NoError(t, err)
acc.Wait(len(tt.expected))
plugin.Stop()
require.Nil(t, acc.Errors) // no errors during gathering
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
})
}
}

View file

@ -0,0 +1,39 @@
# Read logging output from the Docker engine
[[inputs.docker_log]]
## Docker Endpoint
## To use TCP, set endpoint = "tcp://[ip]:[port]"
## To use environment variables (ie, docker-machine), set endpoint = "ENV"
# endpoint = "unix:///var/run/docker.sock"
## When true, container logs are read from the beginning; otherwise reading
## begins at the end of the log. If state-persistence is enabled for Telegraf,
## the reading continues at the last previously processed timestamp.
# from_beginning = false
## Timeout for Docker API calls.
# timeout = "5s"
## Containers to include and exclude. Globs accepted.
## Note that an empty array for both will include all containers
# container_name_include = []
# container_name_exclude = []
## Container states to include and exclude. Globs accepted.
## When empty only containers in the "running" state will be captured.
# container_state_include = []
# container_state_exclude = []
## docker labels to include and exclude as tags. Globs accepted.
## Note that an empty array for both will include all labels as tags
# docker_label_include = []
# docker_label_exclude = []
## Set the source tag for the metrics to the container ID hostname, eg first 12 chars
source_tag = false
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false