Skip to content

Commit

Permalink
Improve docker input plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
titilambert committed Mar 2, 2016
1 parent b2a4d4a commit 7da18f4
Show file tree
Hide file tree
Showing 3 changed files with 359 additions and 3 deletions.
46 changes: 44 additions & 2 deletions plugins/inputs/docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,25 +95,67 @@ on the availability of per-cpu stats on your system.
- io_serviced_recursive_sync
- io_serviced_recursive_total
- io_serviced_recursive_write
- docker_
- n_used_file_descriptors
- n_cpus
- n_containers
- n_images
- n_goroutines
- n_listener_events
- memory_total
- pool_blocksize
- docker_data
- available
- total
- used
- docker_metadata
- available
- total
- used


### Tags:

- All stats have the following tags:
- docker (memeory_total)
- unit=bytes
- docker (pool_blocksize)
- unit=bytes
- docker_data
- unit=bytes
- docker_metadata
- unit=bytes

- docker_cpu specific:
- cont_id (container ID)
- cont_image (container image)
- cont_name (container name)
- docker_cpu specific:
- cpu
- docker_net specific:
- cont_id (container ID)
- cont_image (container image)
- cont_name (container name)
- network
- docker_blkio specific:
- cont_id (container ID)
- cont_image (container image)
- cont_name (container name)
- device

### Example Output:

```
% ./telegraf -config ~/ws/telegraf.conf -input-filter docker -test
* Plugin: docker, Collection 1
> docker n_cpus=8i 1456926671065383978
> docker n_used_file_descriptors=15i 1456926671065383978
> docker n_containers=7i 1456926671065383978
> docker n_images=152i 1456926671065383978
> docker n_goroutines=36i 1456926671065383978
> docker n_listener_events=0i 1456926671065383978
> docker,unit=bytes memory_total=18935443456i 1456926671065383978
> docker,unit=bytes pool_blocksize=65540i 1456926671065383978
> docker_data,unit=bytes available=24340000000i,total=107400000000i,used=14820000000i 1456926671065383978
> docker_metadata,unit=bytes available=2126999999i,total=2146999999i,used=20420000i 145692667106538
> docker_mem,cont_id=5705ba8ed8fb47527410653d60a8bb2f3af5e62372297c419022a3cc6d45d848,\
cont_image=spotify/kafka,cont_name=kafka \
active_anon=52568064i,active_file=6926336i,cache=12038144i,fail_count=0i,\
Expand Down
136 changes: 135 additions & 1 deletion plugins/inputs/docker/docker.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package system

import (
"encoding/json"
"fmt"
"log"
"regexp"
"strconv"
"strings"
"sync"
"time"
Expand All @@ -17,9 +20,29 @@ type Docker struct {
Endpoint string
ContainerNames []string

client *docker.Client
client DockerClient
}

type DockerClient interface {
// Docker Client wrapper
// Useful for test
Info() (*docker.Env, error)
ListContainers(opts docker.ListContainersOptions) ([]docker.APIContainers, error)
Stats(opts docker.StatsOptions) error
}

const (
KB = 1000
MB = 1000 * KB
GB = 1000 * MB
TB = 1000 * GB
PB = 1000 * TB
)

var (
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`)
)

var sampleConfig = `
## Docker Endpoint
## To use TCP, set endpoint = "tcp://[ip]:[port]"
Expand Down Expand Up @@ -58,12 +81,20 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
d.client = c
}

// Get daemon info
err := d.gatherInfo(acc)
if err != nil {
fmt.Println(err.Error())
}

// List containers
opts := docker.ListContainersOptions{}
containers, err := d.client.ListContainers(opts)
if err != nil {
return err
}

// Get container data
var wg sync.WaitGroup
wg.Add(len(containers))
for _, container := range containers {
Expand All @@ -81,6 +112,88 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
return nil
}

func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
// Init vars
var driverStatus [][]string
dataFields := make(map[string]interface{})
metadataFields := make(map[string]interface{})
now := time.Now()
// Get info from docker daemon
info, err := d.client.Info()
if err != nil {
return err
}

// Add metrics
acc.AddFields("docker",
map[string]interface{}{"n_cpus": info.GetInt64("NCPU")},
nil,
now)
acc.AddFields("docker",
map[string]interface{}{"n_used_file_descriptors": info.GetInt64("NFd")},
nil,
now)
acc.AddFields("docker",
map[string]interface{}{"n_containers": info.GetInt64("Containers")},
nil,
now)
acc.AddFields("docker",
map[string]interface{}{"n_images": info.GetInt64("Images")},
nil,
now)
acc.AddFields("docker",
map[string]interface{}{"n_goroutines": info.GetInt64("NGoroutines")},
nil,
now)
acc.AddFields("docker",
map[string]interface{}{"n_listener_events": info.GetInt64("NEventsListener")},
nil,
now)
acc.AddFields("docker",
map[string]interface{}{"memory_total": info.GetInt64("MemTotal")},
map[string]string{"unit": "bytes"},
now)
// Get storage metrics
driverStatusRaw := []byte(info.Get("DriverStatus"))
json.Unmarshal(driverStatusRaw, &driverStatus)
for _, rawData := range driverStatus {
// Try to convert string to int (bytes)
value, err := parseSize(rawData[1])
if err != nil {
continue
}
name := strings.ToLower(strings.Replace(rawData[0], " ", "_", -1))
if name == "pool_blocksize" {
// pool blocksize
acc.AddFields("docker",
map[string]interface{}{"pool_blocksize": value},
map[string]string{"unit": "bytes"},
now)
} else if strings.HasPrefix(name, "data_space_") {
// data space
field_name := strings.TrimPrefix(name, "data_space_")
dataFields[field_name] = value
} else if strings.HasPrefix(name, "metadata_space_") {
// metadata space
field_name := strings.TrimPrefix(name, "metadata_space_")
metadataFields[field_name] = value
}
}
if len(dataFields) > 0 {
acc.AddFields("docker_data",
dataFields,
map[string]string{"unit": "bytes"},
now)
}
if len(metadataFields) > 0 {
acc.AddFields("docker_metadata",
metadataFields,
map[string]string{"unit": "bytes"},
now)
}
return nil
}

func (d *Docker) gatherContainer(
container docker.APIContainers,
acc telegraf.Accumulator,
Expand Down Expand Up @@ -334,6 +447,27 @@ func sliceContains(in string, sl []string) bool {
return false
}

// Parses the human-readable size string into the amount it represents.
func parseSize(sizeStr string) (int64, error) {
matches := sizeRegex.FindStringSubmatch(sizeStr)
if len(matches) != 4 {
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
}

size, err := strconv.ParseFloat(matches[1], 64)
if err != nil {
return -1, err
}

uMap := map[string]int64{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
unitPrefix := strings.ToLower(matches[3])
if mul, ok := uMap[unitPrefix]; ok {
size *= float64(mul)
}

return int64(size), nil
}

func init() {
inputs.Add("docker", func() telegraf.Input {
return &Docker{}
Expand Down
Loading

0 comments on commit 7da18f4

Please sign in to comment.