Compare commits

...

94 Commits

Author SHA1 Message Date
Daniel Nelson
4f61d2a09c Add idea for an output that aggregates before adding to metric buffer 2018-05-01 16:47:16 -07:00
Daniel Nelson
c03e8918a2 Update changelog, add mcrouter to README 2018-05-01 12:01:08 -07:00
Craig Thayer
83345ec2b3 Add input plugin for McRouter (#4077) 2018-05-01 11:58:15 -07:00
Daniel Nelson
f094f83da5 Update changelog 2018-04-30 19:21:12 -07:00
Mariusz Brzeski
0768022240 Support busybox ping in the ping input (#3877) 2018-04-30 19:20:13 -07:00
Daniel Nelson
92956104d6 Update changelog 2018-04-30 17:51:04 -07:00
Daniel Nelson
964856eb5f Fix win_perf_counters to collect counters per instance (#4036) 2018-04-30 17:48:45 -07:00
Daniel Nelson
377547aa4c Document one field per line requirement in logparser 2018-04-30 16:15:51 -07:00
Grégoire Bellon-Gervais
1662b6feb9 Metrics values have same names as old cassandra plugin (#4080) 2018-04-27 15:12:59 -07:00
Daniel Nelson
908170b207 Update changelog 2018-04-27 14:56:31 -07:00
Vincent Caron
ec47cab950 Use same timestamp for fields in system input (#4078) 2018-04-27 14:55:10 -07:00
Daniel Nelson
06671777e9 Update changelog 2018-04-25 19:02:00 -07:00
Adrián López
46a8bdbfe5 Add parameter to force the interval of gather for sysstat (#4068) 2018-04-25 18:59:42 -07:00
Daniel Nelson
abdff033cc Note options that only work with influxdb HTTP 2018-04-25 13:47:16 -07:00
Daniel Nelson
535e9e9a68 Update changelog 2018-04-25 13:47:16 -07:00
Jack Zampolin
c256f17870 Fix timeout parsing error in nvidia_smi (#4070) 2018-04-24 14:40:19 -07:00
Yosuke Hara
b8d5df2076 Add support for LeoFS v1.4 to leofs input (#4044) 2018-04-24 14:14:31 -07:00
Daniel Nelson
538baee8a4 Fix nightly build 2018-04-24 13:42:42 -07:00
Daniel Nelson
d3d8d52e2f Fix links to jolokia example configs 2018-04-24 12:46:40 -07:00
Daniel Nelson
286f14f730 Update changelog 2018-04-23 15:15:08 -07:00
Daniel Nelson
9f4752ba12 Add docker input server version (#4035) 2018-04-23 15:09:04 -07:00
Daniel Nelson
f639f994b5 Ignore writer error in file output (#4055) 2018-04-23 15:08:04 -07:00
Daniel Nelson
911f0e4b57 Deprecate the cassandra input plugin (#4050) 2018-04-23 15:06:26 -07:00
Daniel Nelson
86a3b8cad4 Update changelog 2018-04-23 14:01:38 -07:00
Daniel Nelson
a3500cc33a Fix handling of floats with multiple leading zeroes (#4065) 2018-04-23 13:29:49 -07:00
Daniel Nelson
bf0c59f56c Return errors in mongodb SSL/TLS configuration (#4066) 2018-04-23 13:29:12 -07:00
Fred Cox
c7b3667ac4 Add server argument as first argument in unbound input (#4062) 2018-04-23 13:27:29 -07:00
Daniel Nelson
638853be05 Update changelog 2018-04-20 18:49:55 -07:00
Daniel Nelson
ee9a2f73a1 Fix duplicate tags when overriding tag (#4056) 2018-04-20 18:39:31 -07:00
Daniel Nelson
648d7ae922 Run 32-bit tests in CircleCI 2018-04-20 15:10:22 -07:00
Daniel Nelson
13937d511d Update changelog 2018-04-20 15:05:39 -07:00
Daniel Nelson
fe4d3cd117 Fix ints being capped at 32-bits on 32-bit archs (#4054) 2018-04-20 14:56:28 -07:00
Leandro Piccilli
eacf11fcd8 Update gopsutils to include fixes for #4037 and #3750 (#4045) 2018-04-20 14:32:19 -07:00
Daniel Nelson
3a8ca4d08d Update changelog 2018-04-19 16:58:59 -07:00
Daniel Nelson
00e3363d45 Add only valid field types in cassandra input (#4048) 2018-04-19 16:56:46 -07:00
Daniel Nelson
29b37e67c2 Allow metrics to be unserializable in influx.Reader (#4047)
Metrics that are unserializable will be logged at debug level, but the
rest of the batch will be sent.  Unserializable metrics can occur during
normal operation such as if you remove all fields from a metric or the
metric cannot fit within the line size limit.
2018-04-19 16:24:31 -07:00
Daniel Nelson
42fee824f8 Update changelog 2018-04-18 16:57:15 -07:00
Daniel Nelson
120be7e87b Report available fields if utmp is unreadable (#4043) 2018-04-18 16:55:18 -07:00
Daniel Nelson
9e4a330ee5 Update github.com/gorilla/mux version (#4042) 2018-04-18 16:55:02 -07:00
Daniel Nelson
78d4a95ce6 Test using Go 1.8-1.10; official builds with 1.10 (#4041) 2018-04-18 16:14:06 -07:00
Daniel Nelson
571ce86d10 Update changelog 2018-04-18 12:14:58 -07:00
Daniel Nelson
dd2c60e620 Fix graphite serialization of unsigned ints (#4033) 2018-04-18 12:13:25 -07:00
Daniel Nelson
1486ae25c0 Tidy up last change to socket listener/writer 2018-04-17 17:48:30 -07:00
Daniel Nelson
da5b46e770 Update changelog 2018-04-17 17:36:35 -07:00
Matt
9ef902f4a1 Add snmp input option to strip non fixed length index suffixes (#4025) 2018-04-17 17:34:39 -07:00
Daniel Nelson
058510464c Update changelog 2018-04-17 17:03:18 -07:00
Bob Shannon
0b4f4b089f Add TLS support to socket_writer and socket_listener plugins (#4021) 2018-04-17 17:02:04 -07:00
Daniel Nelson
7c592558d8 Update changelog 2018-04-17 15:45:49 -07:00
James Maidment
1e1d9e8acb Update mem values to gauge (#4034) 2018-04-17 15:43:10 -07:00
Daniel Nelson
3b3d16273d Update changelog adding nvidia_smi 2018-04-17 13:43:36 -07:00
Jack Zampolin
3046f957d5 Add nvidia_smi input to monitor nvidia GPUs (#4026) 2018-04-17 13:40:55 -07:00
Daniel Nelson
bcf1cf59c1 Fix docs about outputs and fieldpass/fielddrop
This has been allowed since 1.1.0
2018-04-17 13:35:27 -07:00
Daniel Nelson
c8d2ba2bc8 Remove RateLimiter tests due to race conditions
These tests are fundamentally racy, removing to improve reliability of
test cases.
2018-04-16 18:52:52 -07:00
Daniel Nelson
04ab9a4fe4 Set 1.6 release date in changelog 2018-04-16 12:04:31 -07:00
Daniel Nelson
e4009234e9 Fix HashID conflicts in pathological cases
Use "\n" as delimiter as it cannot occur in the series name.
2018-04-12 18:09:31 -07:00
Daniel Nelson
8d516d26e9 Fix MQTT sample config 2018-04-12 14:34:55 -07:00
Daniel Nelson
0a02363c03 Update changelog 2018-04-11 16:52:40 -07:00
jvassev
2c19d74829 Prevent loading config twice in K8S (#3999)
When config dir is mounted from configmap, filepath.Walk() finds the same
.conf file twice as 20-acme.conf is a link to ..data/20-acme.conf for example.

This patch skips all folder names starting with '..' which is pretty
uncommon and mainly used by Kubernetes mounts.
2018-04-11 16:51:19 -07:00
Daniel Nelson
3f4e1af222 Add --console and --service to usage message in Windows (#3993) 2018-04-11 16:44:55 -07:00
Daniel Nelson
10c7324d74 Update changelog 2018-04-10 18:18:27 -07:00
Daniel Nelson
55cfc383f3 Allow grok pattern to contain newlines (#4005) 2018-04-10 18:16:21 -07:00
Daniel Nelson
7b8f12b377 Update changelog 2018-04-10 18:15:02 -07:00
Daniel Nelson
15f19375e7 Typesetting changes to fibaro README 2018-04-10 18:14:27 -07:00
Pierrick Brossin
93e2381f42 Add Fibaro input plugin (#2741) 2018-04-10 18:04:58 -07:00
Daniel Nelson
387bae9b9f Fix host ordering in mongodb unit tests 2018-04-10 17:24:40 -07:00
Daniel Nelson
34416e0da8 Updated changelog 2018-04-10 17:11:25 -07:00
Jake Champlin
32f56140a3 Add per-host shard metrics in mongodb input (#3819) 2018-04-10 17:10:29 -07:00
Boris Schrijver
64a23c0b18 Fix make test-ci run (#4002) 2018-04-10 15:35:58 -07:00
Daniel Nelson
af68975e2f Document that InfluxDB input metrics vary with version 2018-04-09 19:30:18 -07:00
Daniel Nelson
0223b22b3e Update changelog 2018-04-09 17:06:34 -07:00
Daniel Nelson
1890efbb70 Rename repl_oplog_window_s to repl_oplog_window_sec
To match existing metric style.
2018-04-09 17:05:45 -07:00
Daniel Nelson
e4f8a82ee6 Fix newline escaping in line protocol (#3992) 2018-04-09 15:29:52 -07:00
Daniel Nelson
a28de4b5cd Update changelog 2018-04-06 16:45:07 -07:00
Daniel Nelson
caac224276 Add details about MongoDB permissions 2018-04-06 16:43:03 -07:00
Daniel Nelson
fe31ce9d7d Modernize mongodb docs 2018-04-06 16:36:03 -07:00
Matvey Kruglov
01ede2ea0b Add repl_oplog_window_s metric to mongodb input (#3964) 2018-04-06 16:34:47 -07:00
alekseyp
fb6390e7ab Fix typo in phpfpm README (#3985) 2018-04-06 16:20:36 -07:00
Mark Wilkinson - m82labs
ff40da6019 Use explicit casts to avoid datatype issues (#3980) 2018-04-06 14:58:33 -07:00
Daniel Nelson
43a044542e Update changelog 2018-04-06 13:19:02 -07:00
Daniel Nelson
00203fa889 Export all vars defined in /etc/default/telegraf (#3981)
This keeps the format of this file the same between systemd and
sysvinit.
2018-04-06 13:17:24 -07:00
Daniel Nelson
7177e0473f Fix conversion of unsigned ints in prometheus output (#3978) 2018-04-05 16:38:41 -07:00
Daniel Nelson
252101b7c6 Update changelog 2018-04-05 11:19:01 -07:00
Daniel Nelson
efdf36746c Update gosnmp revision (#3973) 2018-04-05 11:15:20 -07:00
Daniel Nelson
df78133bf3 Log error if scheme is unsupported 2018-04-05 11:08:31 -07:00
Jeff Ashton
bf915fa79c Fix https in InfluxDB output (#3976) 2018-04-05 10:50:32 -07:00
Daniel Nelson
c160b56229 Fix build.py next_version 2018-04-04 21:53:20 -07:00
Daniel Nelson
627f0e5d9d Use automatic extension naming when running go build 2018-04-04 19:00:28 -07:00
Daniel Nelson
4551b4c5d2 Enable ntpq tests on Windows (#3972) 2018-04-04 18:35:05 -07:00
Daniel Nelson
a9afd2f030 Add config-directory documentation for Windows service 2018-04-04 16:30:22 -07:00
Daniel Nelson
caf860bc88 Don't print name of plugin or interval size during --test 2018-04-04 16:30:22 -07:00
Daniel Nelson
beeab2c509 Sort field names when running --test 2018-04-04 16:30:22 -07:00
Scott Anderson
a50acadc44 Add details about why not all logstash patterns are supported (#3971) 2018-04-04 14:42:58 -07:00
Daniel Nelson
265d0e6d84 Fix bug preventing database from being recreated (#3962) 2018-04-02 16:18:33 -07:00
Daniel Nelson
413cf6dd23 Set next version to 1.7 on master 2018-04-02 14:44:09 -07:00
106 changed files with 19099 additions and 15405 deletions

View File

@@ -1,49 +1,105 @@
---
defaults: &defaults
docker:
- image: 'circleci/golang:1.9.4'
working_directory: '/go/src/github.com/influxdata/telegraf'
defaults:
defaults: &defaults
working_directory: '/go/src/github.com/influxdata/telegraf'
go-1_8: &go-1_8
docker:
- image: 'circleci/golang:1.8.7'
go-1_9: &go-1_9
docker:
- image: 'circleci/golang:1.9.5'
go-1_10: &go-1_10
docker:
- image: 'circleci/golang:1.10.1'
version: 2
jobs:
build:
<<: *defaults
deps:
<<: [ *defaults, *go-1_10 ]
steps:
- checkout
- run: 'make deps'
- run: 'make test-ci'
release:
<<: *defaults
- persist_to_workspace:
root: '/go/src'
paths:
- '*'
test-go-1.8:
<<: [ *defaults, *go-1_8 ]
steps:
- checkout
- attach_workspace:
at: '/go/src'
- run: 'make test-ci'
test-go-1.9:
<<: [ *defaults, *go-1_9 ]
steps:
- attach_workspace:
at: '/go/src'
- run: 'make test-ci'
test-go-1.10:
<<: [ *defaults, *go-1_10 ]
steps:
- attach_workspace:
at: '/go/src'
- run: 'make test-ci'
- run: 'GOARCH=386 make test-ci'
release:
<<: [ *defaults, *go-1_10 ]
steps:
- attach_workspace:
at: '/go/src'
- run: './scripts/release.sh'
- store_artifacts:
path: './artifacts'
destination: '.'
nightly:
<<: *defaults
<<: [ *defaults, *go-1_10 ]
steps:
- checkout
- attach_workspace:
at: '/go/src'
- run: './scripts/release.sh'
- store_artifacts:
path: './artifacts'
destination: '.'
workflows:
version: 2
build_and_release:
jobs:
- 'build'
- 'deps'
- 'test-go-1.8':
requires:
- 'deps'
- 'test-go-1.9':
requires:
- 'deps'
- 'test-go-1.10':
requires:
- 'deps'
- 'release':
requires:
- 'build'
- 'test-go-1.8'
- 'test-go-1.9'
- 'test-go-1.10'
nightly:
jobs:
- 'build'
- 'deps'
- 'test-go-1.8':
requires:
- 'deps'
- 'test-go-1.9':
requires:
- 'deps'
- 'test-go-1.10':
requires:
- 'deps'
- 'nightly':
requires:
- 'build'
- 'test-go-1.8'
- 'test-go-1.9'
- 'test-go-1.10'
triggers:
- schedule:
cron: "0 18 * * *"
cron: "0 7 * * *"
filters:
branches:
only:

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/build
/telegraf
/telegraf.exe
/telegraf.gz

View File

@@ -1,4 +1,58 @@
## v1.6 [unreleased]
## v1.7 [unreleased]
### Release Notes
- The `cassandra` input plugin has been deprecated in favor of the `jolokia2`
input plugin which is much more configurable and more performant. There is
an [example configuration](./plugins/inputs/jolokia2/examples) to help you
get started.
### New Inputs
- [fibaro](./plugins/inputs/fibaro/README.md) - Contributed by @dynek
- [mcrouter](./plugins/inputs/mcrouter/README.md) - Contributed by @cthayer
- [nvidia_smi](./plugins/inputs/nvidia_smi/README.md) - Contributed by @jackzampolin
### Features
- [#3964](https://github.com/influxdata/telegraf/pull/3964): Add repl_oplog_window_sec metric to mongodb input.
- [#3819](https://github.com/influxdata/telegraf/pull/3819): Add per-host shard metrics in mongodb input.
- [#3999](https://github.com/influxdata/telegraf/pull/3999): Skip files with leading `..` in config directory.
- [#4021](https://github.com/influxdata/telegraf/pull/4021): Add TLS support to socket_writer and socket_listener plugins.
- [#4025](https://github.com/influxdata/telegraf/pull/4025): Add snmp input option to strip non fixed length index suffixes.
- [#4035](https://github.com/influxdata/telegraf/pull/4035): Add server version tag to docker input.
- [#4044](https://github.com/influxdata/telegraf/pull/4044): Add support for LeoFS 1.4 to leofs input.
- [#4068](https://github.com/influxdata/telegraf/pull/4068): Add parameter to force the interval of gather for sysstat.
- [#3877](https://github.com/influxdata/telegraf/pull/3877): Support busybox ping in the ping input.
- [#4077](https://github.com/influxdata/telegraf/pull/4077): Add input plugin for McRouter.
### Bugfixes
- [#4018](https://github.com/influxdata/telegraf/pull/4018): Write to working file outputs if any files are not writeable.
- [#4036](https://github.com/influxdata/telegraf/pull/4036): Add all win_perf_counters fields for a series in a single metric.
## v1.6.2 [unreleased]
### Bugfixes
- [#4078](https://github.com/influxdata/telegraf/pull/4078): Use same timestamp for fields in system input.
## v1.6.1 [2018-04-23]
### Bugfixes
- [#3835](https://github.com/influxdata/telegraf/issues/3835): Report mem input fields as gauges instead counters.
- [#4030](https://github.com/influxdata/telegraf/issues/4030): Fix graphite outputs unsigned integers in wrong format.
- [#4043](https://github.com/influxdata/telegraf/issues/4043): Report available fields if utmp is unreadable.
- [#4039](https://github.com/influxdata/telegraf/issues/4039): Fix potential "no fields" error writing to outputs.
- [#4037](https://github.com/influxdata/telegraf/issues/4037): Fix uptime reporting in system input when ran inside docker.
- [#3750](https://github.com/influxdata/telegraf/issues/3750): Fix mem input "cannot allocate memory" error on FreeBSD based systems.
- [#4056](https://github.com/influxdata/telegraf/pull/4056): Fix duplicate tags when overriding an existing tag.
- [#4062](https://github.com/influxdata/telegraf/pull/4062): Add server argument as first argument in unbound input.
- [#4063](https://github.com/influxdata/telegraf/issues/4063): Fix handling of floats with multiple leading zeroes.
- [#4064](https://github.com/influxdata/telegraf/issues/4064): Return errors in mongodb SSL/TLS configuration.
## v1.6 [2018-04-16]
### Release Notes
@@ -105,6 +159,9 @@
- [#3648](https://github.com/influxdata/telegraf/issues/3648): Fix InfluxDB output not able to reconnect when server address changes.
- [#3957](https://github.com/influxdata/telegraf/issues/3957): Fix parsing of dos line endings in the smart input.
- [#3754](https://github.com/influxdata/telegraf/issues/3754): Fix precision truncation when no timestamp included.
- [#3655](https://github.com/influxdata/telegraf/issues/3655): Fix SNMPv3 connection with Cisco ASA 5515 in snmp input.
- [#3981](https://github.com/influxdata/telegraf/pull/3981): Export all vars defined in /etc/default/telegraf.
- [#4004](https://github.com/influxdata/telegraf/issues/4004): Allow grok pattern to contain newlines.
## v1.5.3 [2018-03-14]

6
Godeps
View File

@@ -26,7 +26,7 @@ github.com/golang/protobuf 8ee79997227bf9b34611aee7946ae64735e6fd93
github.com/golang/snappy 7db9049039a047d955fe8c19b83c8ff5abd765c7
github.com/go-ole/go-ole be49f7c07711fcb603cff39e1de7c67926dc0ba7
github.com/google/go-cmp f94e52cad91c65a63acc1e75d4be223ea22e99bc
github.com/gorilla/mux 392c28fe23e1c45ddba891b0320b3b5df220beea
github.com/gorilla/mux 53c1911da2b537f792e7cafcb446b05ffe33b996
github.com/go-redis/redis 73b70592cdaa9e6abdfcfbf97b4a90d80728c836
github.com/go-sql-driver/mysql 2e00b5cd70399450106cec6431c2e2ce3cae5034
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
@@ -66,11 +66,11 @@ github.com/prometheus/procfs 1878d9fbb537119d24b21ca07effd591627cd160
github.com/rcrowley/go-metrics 1f30fe9094a513ce4c700b9a54458bbb0c96996c
github.com/samuel/go-zookeeper 1d7be4effb13d2d908342d349d71a284a7542693
github.com/satori/go.uuid 5bf94b69c6b68ee1b541973bb8e1144db23a194b
github.com/shirou/gopsutil fc04d2dd9a512906a2604242b35275179e250eda
github.com/shirou/gopsutil a5c2888e464b14fa882c2a059e0f95716bd45cf1
github.com/shirou/w32 3c9377fc6748f222729a8270fe2775d149a249ad
github.com/Shopify/sarama 3b1b38866a79f06deddf0487d5c27ba0697ccd65
github.com/Sirupsen/logrus 61e43dc76f7ee59a82bdf3d71033dc12bea4c77d
github.com/soniah/gosnmp 5ad50dc75ab389f8a1c9f8a67d3a1cd85f67ed15
github.com/soniah/gosnmp f15472a4cd6f6ea7929e4c7d9f163c49f059924f
github.com/StackExchange/wmi f3e2bae1e0cb5aef83e319133eabfee30013a4a5
github.com/streadway/amqp 63795daa9a446c920826655f26ba31c81c860fd6
github.com/stretchr/objx facf9a85c22f48d2f52f2380e4efce1768749a89

View File

@@ -4,7 +4,7 @@ BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
COMMIT := $(shell git rev-parse --short HEAD)
GOFILES ?= $(shell git ls-files '*.go')
GOFMT ?= $(shell gofmt -l $(filter-out plugins/parsers/influx/machine.go, $(GOFILES)))
BUILDFLAGS ?=
BUILDFLAGS ?=
ifdef GOBIN
PATH := $(GOBIN):$(PATH)
@@ -12,8 +12,6 @@ else
PATH := $(subst :,/bin:,$(GOPATH))/bin:$(PATH)
endif
TELEGRAF := telegraf$(shell go tool dist env | grep -q 'GOOS=.windows.' && echo .exe)
LDFLAGS := $(LDFLAGS) -X main.commit=$(COMMIT) -X main.branch=$(BRANCH)
ifdef VERSION
LDFLAGS += -X main.version=$(VERSION)
@@ -29,7 +27,7 @@ deps:
gdm restore
telegraf:
go build -i -o $(TELEGRAF) -ldflags "$(LDFLAGS)" ./cmd/telegraf/telegraf.go
go build -i -ldflags "$(LDFLAGS)" ./cmd/telegraf
go-install:
go install -ldflags "-w -s $(LDFLAGS)" ./cmd/telegraf
@@ -46,7 +44,7 @@ fmt:
fmtcheck:
@echo '[INFO] running gofmt to identify incorrectly formatted code...'
@if [ ! -z $(GOFMT) ]; then \
@if [ ! -z "$(GOFMT)" ]; then \
echo "[ERROR] gofmt has found errors in the following files:" ; \
echo "$(GOFMT)" ; \
echo "" ;\
@@ -60,12 +58,13 @@ test-windows:
go test ./plugins/inputs/win_perf_counters/...
go test ./plugins/inputs/win_services/...
go test ./plugins/inputs/procstat/...
go test ./plugins/inputs/ntpq/...
# vet runs the Go source code static analysis tool `vet` to find
# any common errors.
vet:
@echo 'go vet $$(go list ./... | grep -v ./plugins/parsers/influx)'
@go vet $$(go list ./... | grep -v ./plugins/parsers/influx) ; if [ $$? -eq 1 ]; then \
@go vet $$(go list ./... | grep -v ./plugins/parsers/influx) ; if [ $$? -ne 0 ]; then \
echo ""; \
echo "go vet has found suspicious constructs. Please remediate any reported errors"; \
echo "to fix them before submitting code for review."; \
@@ -73,7 +72,7 @@ vet:
fi
test-ci: fmtcheck vet
go test -short./...
go test -short ./...
test-all: fmtcheck vet
go test ./...

View File

@@ -5,7 +5,7 @@ and writing metrics.
Design goals are to have a minimal memory footprint with a plugin system so
that developers in the community can easily add support for collecting metrics
from local or remote services.
. For an example configuration referencet from local or remote services.
Telegraf is plugin-driven and has the concept of 4 distinct plugins:
@@ -130,7 +130,7 @@ configuration options.
* [aws cloudwatch](./plugins/inputs/cloudwatch)
* [bcache](./plugins/inputs/bcache)
* [bond](./plugins/inputs/bond)
* [cassandra](./plugins/inputs/cassandra)
* [cassandra](./plugins/inputs/cassandra) (deprecated, use [jolokia2](./plugins/inputs/jolokia2))
* [ceph](./plugins/inputs/ceph)
* [cgroup](./plugins/inputs/cgroup)
* [chrony](./plugins/inputs/chrony)
@@ -147,6 +147,7 @@ configuration options.
* [elasticsearch](./plugins/inputs/elasticsearch)
* [exec](./plugins/inputs/exec) (generic executable plugin, support JSON, influx, graphite and nagios)
* [fail2ban](./plugins/inputs/fail2ban)
* [fibaro](./plugins/inputs/fibaro)
* [filestat](./plugins/inputs/filestat)
* [fluentd](./plugins/inputs/fluentd)
* [graylog](./plugins/inputs/graylog)
@@ -162,12 +163,13 @@ configuration options.
* [iptables](./plugins/inputs/iptables)
* [ipset](./plugins/inputs/ipset)
* [jolokia](./plugins/inputs/jolokia) (deprecated, use [jolokia2](./plugins/inputs/jolokia2))
* [jolokia2](./plugins/inputs/jolokia2)
* [jolokia2](./plugins/inputs/jolokia2) (java, cassandra, kafka)
* [kapacitor](./plugins/inputs/kapacitor)
* [kubernetes](./plugins/inputs/kubernetes)
* [leofs](./plugins/inputs/leofs)
* [lustre2](./plugins/inputs/lustre2)
* [mailchimp](./plugins/inputs/mailchimp)
* [mcrouter](./plugins/inputs/mcrouter)
* [memcached](./plugins/inputs/memcached)
* [mesos](./plugins/inputs/mesos)
* [minecraft](./plugins/inputs/minecraft)
@@ -180,6 +182,7 @@ configuration options.
* [nsq](./plugins/inputs/nsq)
* [nstat](./plugins/inputs/nstat)
* [ntpq](./plugins/inputs/ntpq)
* [nvidia_smi](./plugins/inputs/nvidia_smi)
* [openldap](./plugins/inputs/openldap)
* [opensmtpd](./plugins/inputs/opensmtpd)
* [pf](./plugins/inputs/pf)

View File

@@ -203,11 +203,6 @@ func (a *Agent) Test() error {
input.SetTrace(true)
input.SetDefaultTags(a.Config.Tags)
fmt.Printf("* Plugin: %s, Collection 1\n", input.Name())
if input.Config.Interval != 0 {
fmt.Printf("* Internal: %s\n", input.Config.Interval)
}
if err := input.Input.Gather(acc); err != nil {
return err
}
@@ -217,7 +212,6 @@ func (a *Agent) Test() error {
switch input.Name() {
case "inputs.cpu", "inputs.mongodb", "inputs.procstat":
time.Sleep(500 * time.Millisecond)
fmt.Printf("* Plugin: %s, Collection 2\n", input.Name())
if err := input.Input.Gather(acc); err != nil {
return err
}

View File

@@ -13,11 +13,11 @@ platform: x64
install:
- IF NOT EXIST "C:\Cache" mkdir C:\Cache
- IF NOT EXIST "C:\Cache\go1.9.4.msi" curl -o "C:\Cache\go1.9.4.msi" https://storage.googleapis.com/golang/go1.9.4.windows-amd64.msi
- IF NOT EXIST "C:\Cache\go1.10.1.msi" curl -o "C:\Cache\go1.10.1.msi" https://storage.googleapis.com/golang/go1.10.1.windows-amd64.msi
- IF NOT EXIST "C:\Cache\gnuwin32-bin.zip" curl -o "C:\Cache\gnuwin32-bin.zip" https://dl.influxdata.com/telegraf/ci/make-3.81-bin.zip
- IF NOT EXIST "C:\Cache\gnuwin32-dep.zip" curl -o "C:\Cache\gnuwin32-dep.zip" https://dl.influxdata.com/telegraf/ci/make-3.81-dep.zip
- IF EXIST "C:\Go" rmdir /S /Q C:\Go
- msiexec.exe /i "C:\Cache\go1.9.4.msi" /quiet
- msiexec.exe /i "C:\Cache\go1.10.1.msi" /quiet
- 7z x "C:\Cache\gnuwin32-bin.zip" -oC:\GnuWin32 -y
- 7z x "C:\Cache\gnuwin32-dep.zip" -oC:\GnuWin32 -y
- go version

View File

@@ -57,7 +57,7 @@ var fService = flag.String("service", "",
var fRunAsConsole = flag.Bool("console", false, "run as console application (windows only)")
var (
nextVersion = "1.6.0"
nextVersion = "1.7.0"
version string
commit string
branch string
@@ -73,48 +73,6 @@ func init() {
}
}
const usage = `Telegraf, The plugin-driven server agent for collecting and reporting metrics.
Usage:
telegraf [commands|flags]
The commands & flags are:
config print out full sample configuration to stdout
version print the version to stdout
--config <file> configuration file to load
--test gather metrics once, print them to stdout, and exit
--config-directory directory containing additional *.conf files
--input-filter filter the input plugins to enable, separator is :
--output-filter filter the output plugins to enable, separator is :
--usage print usage for a plugin, ie, 'telegraf --usage mysql'
--debug print metrics as they're generated to stdout
--pprof-addr pprof address to listen on, format: localhost:6060 or :6060
--quiet run in quiet mode
Examples:
# generate a telegraf config file:
telegraf config > telegraf.conf
# generate config with only cpu input & influxdb output plugins defined
telegraf --input-filter cpu --output-filter influxdb config
# run a single telegraf collection, outputing metrics to stdout
telegraf --config telegraf.conf --test
# run telegraf with all plugins defined in config file
telegraf --config telegraf.conf
# run telegraf, enabling the cpu & memory input, and influxdb output plugins
telegraf --config telegraf.conf --input-filter cpu:mem --output-filter influxdb
# run telegraf with pprof
telegraf --config telegraf.conf --pprof-addr localhost:6060
`
var stop chan struct{}
func reloadLoop(
@@ -365,7 +323,7 @@ func main() {
DisplayName: "Telegraf Data Collector Service",
Description: "Collects data using a series of plugins and publishes it to" +
"another series of plugins.",
Arguments: []string{"-config", "C:\\Program Files\\Telegraf\\telegraf.conf"},
Arguments: []string{"--config", "C:\\Program Files\\Telegraf\\telegraf.conf"},
}
prg := &program{
@@ -378,14 +336,14 @@ func main() {
if err != nil {
log.Fatal("E! " + err.Error())
}
// Handle the -service flag here to prevent any issues with tooling that
// Handle the --service flag here to prevent any issues with tooling that
// may not have an interactive session, e.g. installing from Ansible.
if *fService != "" {
if *fConfig != "" {
(*svcConfig).Arguments = []string{"-config", *fConfig}
(*svcConfig).Arguments = []string{"--config", *fConfig}
}
if *fConfigDirectory != "" {
(*svcConfig).Arguments = append((*svcConfig).Arguments, "-config-directory", *fConfigDirectory)
(*svcConfig).Arguments = append((*svcConfig).Arguments, "--config-directory", *fConfigDirectory)
}
err := service.Control(s, *fService)
if err != nil {

45
cmd/telegraf/usage.go Normal file
View File

@@ -0,0 +1,45 @@
// +build !windows
package main
const usage = `Telegraf, The plugin-driven server agent for collecting and reporting metrics.
Usage:
telegraf [commands|flags]
The commands & flags are:
config print out full sample configuration to stdout
version print the version to stdout
--config <file> configuration file to load
--test gather metrics once, print them to stdout, and exit
--config-directory directory containing additional *.conf files
--input-filter filter the input plugins to enable, separator is :
--output-filter filter the output plugins to enable, separator is :
--usage print usage for a plugin, ie, 'telegraf --usage mysql'
--debug print metrics as they're generated to stdout
--pprof-addr pprof address to listen on, format: localhost:6060 or :6060
--quiet run in quiet mode
Examples:
# generate a telegraf config file:
telegraf config > telegraf.conf
# generate config with only cpu input & influxdb output plugins defined
telegraf --input-filter cpu --output-filter influxdb config
# run a single telegraf collection, outputing metrics to stdout
telegraf --config telegraf.conf --test
# run telegraf with all plugins defined in config file
telegraf --config telegraf.conf
# run telegraf, enabling the cpu & memory input, and influxdb output plugins
telegraf --config telegraf.conf --input-filter cpu:mem --output-filter influxdb
# run telegraf with pprof
telegraf --config telegraf.conf --pprof-addr localhost:6060
`

View File

@@ -0,0 +1,54 @@
// +build windows
package main
const usage = `Telegraf, The plugin-driven server agent for collecting and reporting metrics.
Usage:
telegraf [commands|flags]
The commands & flags are:
config print out full sample configuration to stdout
version print the version to stdout
--config <file> configuration file to load
--test gather metrics once, print them to stdout, and exit
--config-directory directory containing additional *.conf files
--input-filter filter the input plugins to enable, separator is :
--output-filter filter the output plugins to enable, separator is :
--usage print usage for a plugin, ie, 'telegraf --usage mysql'
--debug print metrics as they're generated to stdout
--pprof-addr pprof address to listen on, format: localhost:6060 or :6060
--quiet run in quiet mode
--console run as console application
--service operate on service, one of: install, uninstall, start, stop
Examples:
# generate a telegraf config file:
telegraf config > telegraf.conf
# generate config with only cpu input & influxdb output plugins defined
telegraf --input-filter cpu --output-filter influxdb config
# run a single telegraf collection, outputing metrics to stdout
telegraf --config telegraf.conf --test
# run telegraf with all plugins defined in config file
telegraf --config telegraf.conf
# run telegraf, enabling the cpu & memory input, and influxdb output plugins
telegraf --config telegraf.conf --input-filter cpu:mem --output-filter influxdb
# run telegraf with pprof
telegraf --config telegraf.conf --pprof-addr localhost:6060
# run telegraf without service controller
telegraf --console install --config "C:\Program Files\Telegraf\telegraf.conf"
# install telegraf service
telegraf --service install --config "C:\Program Files\Telegraf\telegraf.conf"
`

View File

@@ -153,11 +153,11 @@ The inverse of `namepass`. If a match is found the point is discarded. This
is tested on points after they have passed the `namepass` test.
* **fieldpass**:
An array of glob pattern strings. Only fields whose field key matches a
pattern in this list are emitted. Not available for outputs.
pattern in this list are emitted.
* **fielddrop**:
The inverse of `fieldpass`. Fields with a field key matching one of the
patterns will be discarded from the point. This is tested on points after
they have passed the `fieldpass` test. Not available for outputs.
they have passed the `fieldpass` test.
* **tagpass**:
A table mapping tag keys to arrays of glob pattern strings. Only points
that contain a tag key in the table and a tag value matching one of its

View File

@@ -5,7 +5,7 @@ the general steps to set it up.
1. Obtain the telegraf windows distribution
2. Create the directory `C:\Program Files\Telegraf` (if you install in a different
location simply specify the `-config` parameter with the desired location)
location simply specify the `--config` parameter with the desired location)
3. Place the telegraf.exe and the telegraf.conf config file into `C:\Program Files\Telegraf`
4. To install the service into the Windows Service Manager, run the following in PowerShell as an administrator (If necessary, you can wrap any spaces in the file paths in double quotes ""):
@@ -26,6 +26,15 @@ the general steps to set it up.
> net start telegraf
```
## Config Directory
You can also specify a `--config-directory` for the service to use:
1. Create a directory for config snippets: `C:\Program Files\Telegraf\telegraf.d`
2. Include the `--config-directory` option when registering the service:
```
> C:\"Program Files"\Telegraf\telegraf.exe --service install --config C:\"Program Files"\Telegraf\telegraf.conf --config-directory C:\"Program Files"\Telegraf\telegraf.d
```
## Other supported operations
Telegraf can manage its own service through the --service flag:
@@ -37,7 +46,6 @@ Telegraf can manage its own service through the --service flag:
| `telegraf.exe --service start` | Start the telegraf service |
| `telegraf.exe --service stop` | Stop the telegraf service |
Troubleshooting common error #1067
When installing as service in Windows, always double check to specify full path of the config file, otherwise windows service will fail to start

View File

@@ -519,7 +519,13 @@ func (c *Config) LoadDirectory(path string) error {
log.Printf("W! Telegraf is not permitted to read %s", thispath)
return nil
}
if info.IsDir() {
if strings.HasPrefix(info.Name(), "..") {
// skip Kubernetes mounts, prevening loading the same config twice
return filepath.SkipDir
}
return nil
}
name := info.Name()

View File

@@ -0,0 +1,4 @@
# This invalid config file should be skipped during testing
# as it is an ..data folder
[[outputs.influxdb

View File

@@ -112,9 +112,10 @@ func RandomString(n int) string {
return string(bytes)
}
// GetTLSConfig gets a tls.Config object from the given certs, key, and CA files.
// you must give the full path to the files.
// If all files are blank and InsecureSkipVerify=false, returns a nil pointer.
// GetTLSConfig gets a tls.Config object from the given certs, key, and CA files
// for use with a client.
// The full path to each file must be provided.
// Returns a nil pointer if all files are blank and InsecureSkipVerify=false.
func GetTLSConfig(
SSLCert, SSLKey, SSLCA string,
InsecureSkipVerify bool,
@@ -155,6 +156,50 @@ func GetTLSConfig(
return t, nil
}
// GetServerTLSConfig gets a tls.Config object from the given certs, key, and one or more CA files
// for use with a server.
// The full path to each file must be provided.
// Returns a nil pointer if all files are blank.
func GetServerTLSConfig(
TLSCert, TLSKey string,
TLSAllowedCACerts []string,
) (*tls.Config, error) {
if TLSCert == "" && TLSKey == "" && len(TLSAllowedCACerts) == 0 {
return nil, nil
}
t := &tls.Config{}
if len(TLSAllowedCACerts) != 0 {
caCertPool := x509.NewCertPool()
for _, cert := range TLSAllowedCACerts {
c, err := ioutil.ReadFile(cert)
if err != nil {
return nil, errors.New(fmt.Sprintf("Could not load TLS CA: %s",
err))
}
caCertPool.AppendCertsFromPEM(c)
}
t.ClientCAs = caCertPool
t.ClientAuth = tls.RequireAndVerifyClientCert
}
if TLSCert != "" && TLSKey != "" {
cert, err := tls.LoadX509KeyPair(TLSCert, TLSKey)
if err != nil {
return nil, errors.New(fmt.Sprintf(
"Could not load TLS client key/certificate from %s:%s: %s",
TLSKey, TLSCert, err))
}
t.Certificates = []tls.Certificate{cert}
}
t.BuildNameToCertificate()
return t, nil
}
// SnakeCase converts the given string to snake case following the Golang format:
// acronyms are converted to lower-case and preceded by an underscore.
func SnakeCase(in string) string {

View File

@@ -1,54 +0,0 @@
package limiter
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestRateLimiter(t *testing.T) {
r := NewRateLimiter(5, time.Second)
ticker := time.NewTicker(time.Millisecond * 75)
// test that we can only get 5 receives from the rate limiter
counter := 0
outer:
for {
select {
case <-r.C:
counter++
case <-ticker.C:
break outer
}
}
assert.Equal(t, 5, counter)
r.Stop()
// verify that the Stop function closes the channel.
_, ok := <-r.C
assert.False(t, ok)
}
func TestRateLimiterMultipleIterations(t *testing.T) {
r := NewRateLimiter(5, time.Millisecond*50)
ticker := time.NewTicker(time.Millisecond * 250)
// test that we can get 15 receives from the rate limiter
counter := 0
outer:
for {
select {
case <-ticker.C:
break outer
case <-r.C:
counter++
}
}
assert.True(t, counter > 10)
r.Stop()
// verify that the Stop function closes the channel.
_, ok := <-r.C
assert.False(t, ok)
}

View File

@@ -77,6 +77,7 @@ func (r *RunningInput) MakeMetric(
if r.trace && m != nil {
s := influx.NewSerializer()
s.SetFieldSortOrder(influx.SortFields)
octets, err := s.Serialize(m)
if err == nil {
fmt.Print("> " + string(octets))

View File

@@ -113,6 +113,11 @@ func (ro *RunningOutput) AddMetric(m telegraf.Metric) {
m, _ = metric.New(name, tags, fields, t)
}
if output, ok := ro.Output.(telegraf.AggregatingOutput); ok {
output.Add(m)
return
}
ro.metrics.Add(m)
if ro.metrics.Len() == ro.MetricBatchSize {
batch := ro.metrics.Batch(ro.MetricBatchSize)
@@ -125,6 +130,12 @@ func (ro *RunningOutput) AddMetric(m telegraf.Metric) {
// Write writes all cached points to this output.
func (ro *RunningOutput) Write() error {
if output, ok := ro.Output.(telegraf.AggregatingOutput); ok {
metrics := output.Push()
ro.metrics.Add(metrics...)
output.Reset()
}
nFails, nMetrics := ro.failMetrics.Len(), ro.metrics.Len()
ro.BufferSize.Set(int64(nFails + nMetrics))
log.Printf("D! Output [%s] buffer fullness: %d / %d metrics. ",

View File

@@ -123,6 +123,7 @@ func (m *metric) AddTag(key, value string) {
if key == tag.Key {
tag.Value = value
return
}
m.tags = append(m.tags, nil)
@@ -232,9 +233,12 @@ func (m *metric) IsAggregate() bool {
func (m *metric) HashID() uint64 {
h := fnv.New64a()
h.Write([]byte(m.name))
h.Write([]byte("\n"))
for _, tag := range m.tags {
h.Write([]byte(tag.Key))
h.Write([]byte("\n"))
h.Write([]byte(tag.Value))
h.Write([]byte("\n"))
}
return h.Sum64()
}

View File

@@ -63,6 +63,7 @@ func TestAddTagOverwrites(t *testing.T) {
value, ok := m.GetTag("host")
require.True(t, ok)
require.Equal(t, "example.org", value)
require.Equal(t, 1, len(m.TagList()))
}
func TestRemoveTagNoEffectOnMissingTags(t *testing.T) {
@@ -267,6 +268,32 @@ func TestHashID_Consistency(t *testing.T) {
assert.Equal(t, m2.HashID(), m3.HashID())
}
func TestHashID_Delimiting(t *testing.T) {
m1, _ := New(
"cpu",
map[string]string{
"a": "x",
"b": "y",
"c": "z",
},
map[string]interface{}{
"value": float64(1),
},
time.Now(),
)
m2, _ := New(
"cpu",
map[string]string{
"a": "xbycz",
},
map[string]interface{}{
"value": float64(1),
},
time.Now(),
)
assert.NotEqual(t, m1.HashID(), m2.HashID())
}
func TestSetName(t *testing.T) {
m := baseMetric()
m.SetName("foo")

View File

@@ -13,6 +13,12 @@ type Output interface {
Write(metrics []Metric) error
}
type AggregatingOutput interface {
Add(in Metric)
Push() []Metric
Reset()
}
type ServiceOutput interface {
// Connect to the Output
Connect() error

View File

@@ -24,6 +24,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/elasticsearch"
_ "github.com/influxdata/telegraf/plugins/inputs/exec"
_ "github.com/influxdata/telegraf/plugins/inputs/fail2ban"
_ "github.com/influxdata/telegraf/plugins/inputs/fibaro"
_ "github.com/influxdata/telegraf/plugins/inputs/filestat"
_ "github.com/influxdata/telegraf/plugins/inputs/fluentd"
_ "github.com/influxdata/telegraf/plugins/inputs/graylog"
@@ -49,6 +50,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/logparser"
_ "github.com/influxdata/telegraf/plugins/inputs/lustre2"
_ "github.com/influxdata/telegraf/plugins/inputs/mailchimp"
_ "github.com/influxdata/telegraf/plugins/inputs/mcrouter"
_ "github.com/influxdata/telegraf/plugins/inputs/memcached"
_ "github.com/influxdata/telegraf/plugins/inputs/mesos"
_ "github.com/influxdata/telegraf/plugins/inputs/minecraft"
@@ -64,6 +66,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/nsq_consumer"
_ "github.com/influxdata/telegraf/plugins/inputs/nstat"
_ "github.com/influxdata/telegraf/plugins/inputs/ntpq"
_ "github.com/influxdata/telegraf/plugins/inputs/nvidia_smi"
_ "github.com/influxdata/telegraf/plugins/inputs/openldap"
_ "github.com/influxdata/telegraf/plugins/inputs/opensmtpd"
_ "github.com/influxdata/telegraf/plugins/inputs/passenger"

View File

@@ -1,5 +1,8 @@
# Telegraf plugin: Cassandra
### **Deprecated in version 1.7**: Please use the [jolokia2](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/jolokia2) plugin with the [cassandra.conf](/plugins/inputs/jolokia2/examples/cassandra.conf) example configuration.
#### Plugin arguments:
- **context** string: Context root used for jolokia url
- **servers** []string: List of servers with the format "<user:passwd@><host>:port"

View File

@@ -4,12 +4,14 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
)
type JolokiaClient interface {
@@ -60,7 +62,8 @@ func newCassandraMetric(host string, metric string,
func addValuesAsFields(values map[string]interface{}, fields map[string]interface{},
mname string) {
for k, v := range values {
if v != nil {
switch v.(type) {
case int64, float64, string, bool:
fields[mname+"_"+k] = v
}
}
@@ -117,7 +120,7 @@ func (j javaMetric) addTagsFields(out map[string]interface{}) {
switch t := values.(type) {
case map[string]interface{}:
addValuesAsFields(values.(map[string]interface{}), fields, attribute)
case interface{}:
case int64, float64, string, bool:
fields[attribute] = t
}
j.acc.AddFields(tokens["class"]+tokens["type"], fields, tags)
@@ -172,7 +175,11 @@ func (c cassandraMetric) addTagsFields(out map[string]interface{}) {
func (j *Cassandra) SampleConfig() string {
return `
# This is the context root used to compose the jolokia url
## DEPRECATED: The cassandra plugin has been deprecated. Please use the
## jolokia2 plugin instead.
##
## see https://github.com/influxdata/telegraf/tree/master/plugins/inputs/jolokia2
context = "/jolokia/read"
## List of cassandra servers exposing jolokia read service
servers = ["myuser:mypassword@10.10.10.1:8778","10.10.10.2:8778",":8778"]
@@ -256,6 +263,16 @@ func parseServerTokens(server string) map[string]string {
return serverTokens
}
func (c *Cassandra) Start(acc telegraf.Accumulator) error {
log.Println("W! DEPRECATED: The cassandra plugin has been deprecated. " +
"Please use the jolokia2 plugin instead. " +
"https://github.com/influxdata/telegraf/tree/master/plugins/inputs/jolokia2")
return nil
}
func (c *Cassandra) Stop() {
}
func (c *Cassandra) Gather(acc telegraf.Accumulator) error {
context := c.Context
servers := c.Servers

View File

@@ -4,12 +4,11 @@ The docker plugin uses the Docker Engine API to gather metrics on running
docker containers.
The docker plugin uses the [Official Docker Client](https://github.com/moby/moby/tree/master/client)
to gather stats from the [Engine API](https://docs.docker.com/engine/api/v1.20/).
[Library Documentation](https://godoc.org/github.com/moby/moby/client)
to gather stats from the [Engine API](https://docs.docker.com/engine/api/v1.24/).
### Configuration:
```
```toml
# Read metrics about docker containers
[[inputs.docker]]
## Docker Endpoint
@@ -76,15 +75,57 @@ may prefer to exclude them:
```
### Measurements & Fields:
### Metrics:
Every effort was made to preserve the names based on the JSON response from the
docker API.
Note that the docker_container_cpu metric may appear multiple times per collection,
based on the availability of per-cpu stats on your system.
- docker
- tags:
- unit
- engine_host
- server_version
- fields:
- n_used_file_descriptors
- n_cpus
- n_containers
- n_containers_running
- n_containers_stopped
- n_containers_paused
- n_images
- n_goroutines
- n_listener_events
- memory_total
- pool_blocksize
- docker_data
- tags:
- unit
- engine_host
- server_version
- fields:
- available
- total
- used
- docker_metadata
- tags:
- unit
- engine_host
- server_version
- fields:
- available
- total
- used
- docker_container_mem
- tags:
- engine_host
- server_version
- container_image
- container_name
- container_version
- fields:
- total_pgmafault
- cache
- mapped_file
@@ -119,7 +160,16 @@ based on the availability of per-cpu stats on your system.
- failcnt
- limit
- container_id
- docker_container_cpu
- tags:
- engine_host
- server_version
- container_image
- container_name
- container_version
- cpu
- fields:
- throttling_periods
- throttling_throttled_periods
- throttling_throttled_time
@@ -129,7 +179,16 @@ based on the availability of per-cpu stats on your system.
- usage_total
- usage_percent
- container_id
- docker_container_net
- tags:
- engine_host
- server_version
- container_image
- container_name
- container_version
- network
- fields:
- rx_dropped
- rx_bytes
- rx_errors
@@ -139,7 +198,16 @@ based on the availability of per-cpu stats on your system.
- tx_errors
- tx_bytes
- container_id
- docker_container_blkio
- tags:
- engine_host
- server_version
- container_image
- container_name
- container_version
- device
- fields:
- io_service_bytes_recursive_async
- io_service_bytes_recursive_read
- io_service_bytes_recursive_sync
@@ -151,118 +219,38 @@ based on the availability of per-cpu stats on your system.
- io_serviced_recursive_total
- io_serviced_recursive_write
- container_id
- docker_
- n_used_file_descriptors
- n_cpus
- n_containers
- n_containers_running
- n_containers_stopped
- n_containers_paused
- n_images
- n_goroutines
- n_listener_events
- memory_total
- pool_blocksize
- docker_data
- available
- total
- used
- docker_metadata
- available
- total
- used
- docker_swarm
- tasks_desired
- tasks_running
### Tags:
#### Docker Engine tags
- docker (memory_total)
- unit=bytes
- engine_host
- docker (pool_blocksize)
- unit=bytes
- engine_host
- docker_data
- unit=bytes
- engine_host
- docker_metadata
- unit=bytes
- engine_host
#### Docker Container tags
- Tags on all containers:
- docker_container_health
- tags:
- engine_host
- server_version
- container_image
- container_name
- container_version
- docker_container_mem specific:
- docker_container_cpu specific:
- cpu
- docker_container_net specific:
- network
- docker_container_blkio specific:
- device
- docker_container_health specific:
- health_status
- failing_streak
- docker_swarm specific:
- fields:
- health_status (string)
- failing_streak (integer)
- docker_swarm
- tags:
- service_id
- service_name
- service_mode
- fields:
- tasks_desired
- tasks_running
### 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_containers_running=7i 1456926671065383978
> docker n_containers_stopped=3i 1456926671065383978
> docker n_containers_paused=0i 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_container_mem,
container_image=spotify/kafka,container_name=kafka \
active_anon=52568064i,active_file=6926336i,cache=12038144i,fail_count=0i,\
hierarchical_memory_limit=9223372036854771712i,inactive_anon=52707328i,\
inactive_file=5111808i,limit=1044578304i,mapped_file=10301440i,\
max_usage=140656640i,pgfault=63762i,pgmajfault=2837i,pgpgin=73355i,\
pgpgout=45736i,rss=105275392i,rss_huge=4194304i,total_active_anon=52568064i,\
total_active_file=6926336i,total_cache=12038144i,total_inactive_anon=52707328i,\
total_inactive_file=5111808i,total_mapped_file=10301440i,total_pgfault=63762i,\
total_pgmafault=0i,total_pgpgin=73355i,total_pgpgout=45736i,\
total_rss=105275392i,total_rss_huge=4194304i,total_unevictable=0i,\
total_writeback=0i,unevictable=0i,usage=117440512i,writeback=0i 1453409536840126713
> docker_container_cpu,
container_image=spotify/kafka,container_name=kafka,cpu=cpu-total \
throttling_periods=0i,throttling_throttled_periods=0i,\
throttling_throttled_time=0i,usage_in_kernelmode=440000000i,\
usage_in_usermode=2290000000i,usage_system=84795360000000i,\
usage_total=6628208865i 1453409536840126713
> docker_container_cpu,
container_image=spotify/kafka,container_name=kafka,cpu=cpu0 \
usage_total=6628208865i 1453409536840126713
> docker_container_net,\
container_image=spotify/kafka,container_name=kafka,network=eth0 \
rx_bytes=7468i,rx_dropped=0i,rx_errors=0i,rx_packets=94i,tx_bytes=946i,\
tx_dropped=0i,tx_errors=0i,tx_packets=13i 1453409536840126713
> docker_container_blkio,
container_image=spotify/kafka,container_name=kafka,device=8:0 \
io_service_bytes_recursive_async=80216064i,io_service_bytes_recursive_read=79925248i,\
io_service_bytes_recursive_sync=77824i,io_service_bytes_recursive_total=80293888i,\
io_service_bytes_recursive_write=368640i,io_serviced_recursive_async=6562i,\
io_serviced_recursive_read=6492i,io_serviced_recursive_sync=37i,\
io_serviced_recursive_total=6599i,io_serviced_recursive_write=107i 1453409536840126713
>docker_swarm,
service_id=xaup2o9krw36j2dy1mjx1arjw,service_mode=replicated,service_name=test,\
tasks_desired=3,tasks_running=3 1508968160000000000
docker,engine_host=debian-stretch-docker,server_version=17.09.0-ce n_containers=6i,n_containers_paused=0i,n_containers_running=1i,n_containers_stopped=5i,n_cpus=2i,n_goroutines=41i,n_images=2i,n_listener_events=0i,n_used_file_descriptors=27i 1524002041000000000
docker,engine_host=debian-stretch-docker,server_version=17.09.0-ce,unit=bytes memory_total=2101661696i 1524002041000000000
docker_container_mem,container_image=telegraf,container_name=zen_ritchie,container_version=unknown,engine_host=debian-stretch-docker,server_version=17.09.0-ce active_anon=8327168i,active_file=2314240i,cache=27402240i,container_id="adc4ba9593871bf2ab95f3ffde70d1b638b897bb225d21c2c9c84226a10a8cf4",hierarchical_memory_limit=9223372036854771712i,inactive_anon=0i,inactive_file=25088000i,limit=2101661696i,mapped_file=20582400i,max_usage=36646912i,pgfault=4193i,pgmajfault=214i,pgpgin=9243i,pgpgout=520i,rss=8327168i,rss_huge=0i,total_active_anon=8327168i,total_active_file=2314240i,total_cache=27402240i,total_inactive_anon=0i,total_inactive_file=25088000i,total_mapped_file=20582400i,total_pgfault=4193i,total_pgmajfault=214i,total_pgpgin=9243i,total_pgpgout=520i,total_rss=8327168i,total_rss_huge=0i,total_unevictable=0i,total_writeback=0i,unevictable=0i,usage=36528128i,usage_percent=0.4342225020025297,writeback=0i 1524002042000000000
docker_container_cpu,container_image=telegraf,container_name=zen_ritchie,container_version=unknown,cpu=cpu-total,engine_host=debian-stretch-docker,server_version=17.09.0-ce container_id="adc4ba9593871bf2ab95f3ffde70d1b638b897bb225d21c2c9c84226a10a8cf4",throttling_periods=0i,throttling_throttled_periods=0i,throttling_throttled_time=0i,usage_in_kernelmode=40000000i,usage_in_usermode=100000000i,usage_percent=0,usage_system=6394210000000i,usage_total=117319068i 1524002042000000000
docker_container_cpu,container_image=telegraf,container_name=zen_ritchie,container_version=unknown,cpu=cpu0,engine_host=debian-stretch-docker,server_version=17.09.0-ce container_id="adc4ba9593871bf2ab95f3ffde70d1b638b897bb225d21c2c9c84226a10a8cf4",usage_total=20825265i 1524002042000000000
docker_container_cpu,container_image=telegraf,container_name=zen_ritchie,container_version=unknown,cpu=cpu1,engine_host=debian-stretch-docker,server_version=17.09.0-ce container_id="adc4ba9593871bf2ab95f3ffde70d1b638b897bb225d21c2c9c84226a10a8cf4",usage_total=96493803i 1524002042000000000
docker_container_net,container_image=telegraf,container_name=zen_ritchie,container_version=unknown,engine_host=debian-stretch-docker,network=eth0,server_version=17.09.0-ce container_id="adc4ba9593871bf2ab95f3ffde70d1b638b897bb225d21c2c9c84226a10a8cf4",rx_bytes=1576i,rx_dropped=0i,rx_errors=0i,rx_packets=20i,tx_bytes=0i,tx_dropped=0i,tx_errors=0i,tx_packets=0i 1524002042000000000
docker_container_blkio,container_image=telegraf,container_name=zen_ritchie,container_version=unknown,device=254:0,engine_host=debian-stretch-docker,server_version=17.09.0-ce container_id="adc4ba9593871bf2ab95f3ffde70d1b638b897bb225d21c2c9c84226a10a8cf4",io_service_bytes_recursive_async=27398144i,io_service_bytes_recursive_read=27398144i,io_service_bytes_recursive_sync=0i,io_service_bytes_recursive_total=27398144i,io_service_bytes_recursive_write=0i,io_serviced_recursive_async=529i,io_serviced_recursive_read=529i,io_serviced_recursive_sync=0i,io_serviced_recursive_total=529i,io_serviced_recursive_write=0i 1524002042000000000
docker_container_health,container_image=telegraf,container_name=zen_ritchie,container_version=unknown,engine_host=debian-stretch-docker,server_version=17.09.0-ce failing_streak=0i,health_status="healthy" 1524007529000000000
docker_swarm,service_id=xaup2o9krw36j2dy1mjx1arjw,service_mode=replicated,service_name=test tasks_desired=3,tasks_running=3 1508968160000000000
```

View File

@@ -12,7 +12,7 @@ import (
)
var (
version string
version = "1.24"
defaultHeaders = map[string]string{"User-Agent": "engine-api-cli-1.0"}
)

View File

@@ -54,6 +54,7 @@ type Docker struct {
client Client
httpClient *http.Client
engine_host string
serverVersion string
filtersCreated bool
labelFilter filter.Filter
containerFilter filter.Filter
@@ -301,7 +302,14 @@ func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
if err != nil {
return err
}
d.engine_host = info.Name
d.serverVersion = info.ServerVersion
tags := map[string]string{
"engine_host": d.engine_host,
"server_version": d.serverVersion,
}
fields := map[string]interface{}{
"n_cpus": info.NCPU,
@@ -315,15 +323,13 @@ func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
"n_listener_events": info.NEventsListener,
}
// Add metrics
acc.AddFields("docker",
fields,
map[string]string{"engine_host": d.engine_host},
now)
acc.AddFields("docker", fields, tags, now)
acc.AddFields("docker",
map[string]interface{}{"memory_total": info.MemTotal},
map[string]string{"unit": "bytes", "engine_host": d.engine_host},
tags,
now)
// Get storage metrics
tags["unit"] = "bytes"
for _, rawData := range info.DriverStatus {
// Try to convert string to int (bytes)
value, err := parseSize(rawData[1])
@@ -335,7 +341,7 @@ func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
// pool blocksize
acc.AddFields("docker",
map[string]interface{}{"pool_blocksize": value},
map[string]string{"unit": "bytes", "engine_host": d.engine_host},
tags,
now)
} else if strings.HasPrefix(name, "data_space_") {
// data space
@@ -348,16 +354,10 @@ func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
}
}
if len(dataFields) > 0 {
acc.AddFields("docker_data",
dataFields,
map[string]string{"unit": "bytes", "engine_host": d.engine_host},
now)
acc.AddFields("docker_data", dataFields, tags, now)
}
if len(metadataFields) > 0 {
acc.AddFields("docker_metadata",
metadataFields,
map[string]string{"unit": "bytes", "engine_host": d.engine_host},
now)
acc.AddFields("docker_metadata", metadataFields, tags, now)
}
return nil
}
@@ -388,6 +388,7 @@ func (d *Docker) gatherContainer(
tags := map[string]string{
"engine_host": d.engine_host,
"server_version": d.serverVersion,
"container_name": cname,
"container_image": imageName,
"container_version": imageVersion,

View File

@@ -615,7 +615,10 @@ func TestDockerGatherInfo(t *testing.T) {
"n_images": int(199),
"n_goroutines": int(39),
},
map[string]string{"engine_host": "absol"},
map[string]string{
"engine_host": "absol",
"server_version": "17.09.0-ce",
},
)
acc.AssertContainsTaggedFields(t,
@@ -626,8 +629,9 @@ func TestDockerGatherInfo(t *testing.T) {
"available": int64(36530000000),
},
map[string]string{
"unit": "bytes",
"engine_host": "absol",
"unit": "bytes",
"engine_host": "absol",
"server_version": "17.09.0-ce",
},
)
acc.AssertContainsTaggedFields(t,
@@ -648,6 +652,7 @@ func TestDockerGatherInfo(t *testing.T) {
"ENVVAR7": "ENVVAR8=ENVVAR9",
"label1": "test_value_1",
"label2": "test_value_2",
"server_version": "17.09.0-ce",
},
)
acc.AssertContainsTaggedFields(t,
@@ -670,6 +675,7 @@ func TestDockerGatherInfo(t *testing.T) {
"ENVVAR7": "ENVVAR8=ENVVAR9",
"label1": "test_value_1",
"label2": "test_value_2",
"server_version": "17.09.0-ce",
},
)
}

View File

@@ -55,6 +55,7 @@ var info = types.Info{
DockerRootDir: "/var/lib/docker",
NoProxy: "",
BridgeNfIP6tables: true,
ServerVersion: "17.09.0-ce",
}
var containerList = []types.Container{

View File

@@ -0,0 +1,51 @@
# Fibaro Input Plugin
The Fibaro plugin makes HTTP calls to the Fibaro controller API to gather values of hooked devices.
Those values could be true (1) or false (0) for switches, percentage for dimmers, temperature, etc.
### Configuration:
```toml
# Read devices value(s) from a Fibaro controller
[[inputs.fibaro]]
## Required Fibaro controller address/hostname.
## Note: at the time of writing this plugin, Fibaro only implemented http - no https available
url = "http://<controller>:80"
## Required credentials to access the API (http://<controller/api/<component>)
username = "<username>"
password = "<password>"
## Amount of time allowed to complete the HTTP request
# timeout = "5s"
```
### Metrics:
- fibaro
- tags:
- section (section name)
- room (room name)
- name (device name)
- type (device type)
- fields:
- value (float)
- value2 (float, when available from device)
### Example Output:
```
fibaro,host=vm1,name=Escaliers,room=Dégagement,section=Pièces\ communes,type=com.fibaro.binarySwitch value=0 1523351010000000000
fibaro,host=vm1,name=Porte\ fenêtre,room=Salon,section=Pièces\ communes,type=com.fibaro.FGRM222 value=99,value2=99 1523351010000000000
fibaro,host=vm1,name=LED\ îlot\ central,room=Cuisine,section=Cuisine,type=com.fibaro.binarySwitch value=0 1523351010000000000
fibaro,host=vm1,name=Détérioration,room=Entrée,section=Pièces\ communes,type=com.fibaro.heatDetector value=0 1523351010000000000
fibaro,host=vm1,name=Température,room=Cave,section=Cave,type=com.fibaro.temperatureSensor value=17.87 1523351010000000000
fibaro,host=vm1,name=Présence,room=Garde-manger,section=Cuisine,type=com.fibaro.FGMS001 value=1 1523351010000000000
fibaro,host=vm1,name=Luminosité,room=Garde-manger,section=Cuisine,type=com.fibaro.lightSensor value=92 1523351010000000000
fibaro,host=vm1,name=Etat,room=Garage,section=Extérieur,type=com.fibaro.doorSensor value=0 1523351010000000000
fibaro,host=vm1,name=CO2\ (ppm),room=Salon,section=Pièces\ communes,type=com.fibaro.multilevelSensor value=880 1523351010000000000
fibaro,host=vm1,name=Humidité\ (%),room=Salon,section=Pièces\ communes,type=com.fibaro.humiditySensor value=53 1523351010000000000
fibaro,host=vm1,name=Pression\ (mb),room=Salon,section=Pièces\ communes,type=com.fibaro.multilevelSensor value=1006.9 1523351010000000000
fibaro,host=vm1,name=Bruit\ (db),room=Salon,section=Pièces\ communes,type=com.fibaro.multilevelSensor value=58 1523351010000000000
```

View File

@@ -0,0 +1,202 @@
package fibaro
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
const sampleConfig = `
## Required Fibaro controller address/hostname.
## Note: at the time of writing this plugin, Fibaro only implemented http - no https available
url = "http://<controller>:80"
## Required credentials to access the API (http://<controller/api/<component>)
username = "<username>"
password = "<password>"
## Amount of time allowed to complete the HTTP request
# timeout = "5s"
`
const description = "Read devices value(s) from a Fibaro controller"
// Fibaro contains connection information
type Fibaro struct {
URL string
// HTTP Basic Auth Credentials
Username string
Password string
Timeout internal.Duration
client *http.Client
}
// LinkRoomsSections links rooms to sections
type LinkRoomsSections struct {
Name string
SectionID uint16
}
// Sections contains sections informations
type Sections struct {
ID uint16 `json:"id"`
Name string `json:"name"`
}
// Rooms contains rooms informations
type Rooms struct {
ID uint16 `json:"id"`
Name string `json:"name"`
SectionID uint16 `json:"sectionID"`
}
// Devices contains devices informations
type Devices struct {
ID uint16 `json:"id"`
Name string `json:"name"`
RoomID uint16 `json:"roomID"`
Type string `json:"type"`
Enabled bool `json:"enabled"`
Properties struct {
Dead interface{} `json:"dead"`
Value interface{} `json:"value"`
Value2 interface{} `json:"value2"`
} `json:"properties"`
}
// Description returns a string explaining the purpose of this plugin
func (f *Fibaro) Description() string { return description }
// SampleConfig returns text explaining how plugin should be configured
func (f *Fibaro) SampleConfig() string { return sampleConfig }
// getJSON connects, authenticates and reads JSON payload returned by Fibaro box
func (f *Fibaro) getJSON(path string, dataStruct interface{}) error {
var requestURL = f.URL + path
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return err
}
req.SetBasicAuth(f.Username, f.Password)
resp, err := f.client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("Response from url \"%s\" has status code %d (%s), expected %d (%s)",
requestURL,
resp.StatusCode,
http.StatusText(resp.StatusCode),
http.StatusOK,
http.StatusText(http.StatusOK))
return err
}
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
err = dec.Decode(&dataStruct)
if err != nil {
return err
}
return nil
}
// Gather fetches all required information to output metrics
func (f *Fibaro) Gather(acc telegraf.Accumulator) error {
if f.client == nil {
f.client = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
Timeout: f.Timeout.Duration,
}
}
var tmpSections []Sections
err := f.getJSON("/api/sections", &tmpSections)
if err != nil {
return err
}
sections := map[uint16]string{}
for _, v := range tmpSections {
sections[v.ID] = v.Name
}
var tmpRooms []Rooms
err = f.getJSON("/api/rooms", &tmpRooms)
if err != nil {
return err
}
rooms := map[uint16]LinkRoomsSections{}
for _, v := range tmpRooms {
rooms[v.ID] = LinkRoomsSections{Name: v.Name, SectionID: v.SectionID}
}
var devices []Devices
err = f.getJSON("/api/devices", &devices)
if err != nil {
return err
}
for _, device := range devices {
// skip device in some cases
if device.RoomID == 0 ||
device.Enabled == false ||
device.Properties.Dead == "true" ||
device.Type == "com.fibaro.zwaveDevice" {
continue
}
tags := map[string]string{
"section": sections[rooms[device.RoomID].SectionID],
"room": rooms[device.RoomID].Name,
"name": device.Name,
"type": device.Type,
}
fields := make(map[string]interface{})
if device.Properties.Value != nil {
value := device.Properties.Value
switch value {
case "true":
value = "1"
case "false":
value = "0"
}
if fValue, err := strconv.ParseFloat(value.(string), 64); err == nil {
fields["value"] = fValue
}
}
if device.Properties.Value2 != nil {
if fValue, err := strconv.ParseFloat(device.Properties.Value2.(string), 64); err == nil {
fields["value2"] = fValue
}
}
acc.AddFields("fibaro", fields, tags)
}
return nil
}
func init() {
inputs.Add("fibaro", func() telegraf.Input {
return &Fibaro{}
})
}

View File

@@ -0,0 +1,204 @@
package fibaro
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const sectionsJSON = `
[
{
"id": 1,
"name": "Section 1",
"sortOrder": 1
},
{
"id": 2,
"name": "Section 2",
"sortOrder": 2
},
{
"id": 3,
"name": "Section 3",
"sortOrder": 3
}
]`
const roomsJSON = `
[
{
"id": 1,
"name": "Room 1",
"sectionID": 1,
"icon": "room_1",
"sortOrder": 1
},
{
"id": 2,
"name": "Room 2",
"sectionID": 2,
"icon": "room_2",
"sortOrder": 2
},
{
"id": 3,
"name": "Room 3",
"sectionID": 3,
"icon": "room_3",
"sortOrder": 3
},
{
"id": 4,
"name": "Room 4",
"sectionID": 3,
"icon": "room_4",
"sortOrder": 4
}
]`
const devicesJSON = `
[
{
"id": 1,
"name": "Device 1",
"roomID": 1,
"type": "com.fibaro.binarySwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "false"
},
"sortOrder": 1
},
{
"id": 2,
"name": "Device 2",
"roomID": 2,
"type": "com.fibaro.binarySwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "true"
},
"sortOrder": 2
},
{
"id": 3,
"name": "Device 3",
"roomID": 3,
"type": "com.fibaro.multilevelSwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "67"
},
"sortOrder": 3
},
{
"id": 4,
"name": "Device 4",
"roomID": 4,
"type": "com.fibaro.temperatureSensor",
"enabled": true,
"properties": {
"dead": "false",
"value": "22.80"
},
"sortOrder": 4
},
{
"id": 5,
"name": "Device 5",
"roomID": 4,
"type": "com.fibaro.FGRM222",
"enabled": true,
"properties": {
"dead": "false",
"value": "50",
"value2": "75"
},
"sortOrder": 5
}
]`
// TestUnauthorized validates that 401 (wrong credentials) is managed properly
func TestUnauthorized(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer ts.Close()
a := Fibaro{
URL: ts.URL,
Username: "user",
Password: "pass",
client: &http.Client{},
}
var acc testutil.Accumulator
err := acc.GatherError(a.Gather)
require.Error(t, err)
}
// TestJSONSuccess validates that module works OK with valid JSON payloads
func TestJSONSuccess(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
payload := ""
switch r.URL.Path {
case "/api/sections":
payload = sectionsJSON
case "/api/rooms":
payload = roomsJSON
case "/api/devices":
payload = devicesJSON
}
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, payload)
}))
defer ts.Close()
a := Fibaro{
URL: ts.URL,
Username: "user",
Password: "pass",
client: &http.Client{},
}
var acc testutil.Accumulator
err := acc.GatherError(a.Gather)
require.NoError(t, err)
// Gather should add 5 metrics
assert.Equal(t, uint64(5), acc.NMetrics())
// Ensure fields / values are correct - Device 1
tags := map[string]string{"section": "Section 1", "room": "Room 1", "name": "Device 1", "type": "com.fibaro.binarySwitch"}
fields := map[string]interface{}{"value": float64(0)}
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags)
// Ensure fields / values are correct - Device 2
tags = map[string]string{"section": "Section 2", "room": "Room 2", "name": "Device 2", "type": "com.fibaro.binarySwitch"}
fields = map[string]interface{}{"value": float64(1)}
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags)
// Ensure fields / values are correct - Device 3
tags = map[string]string{"section": "Section 3", "room": "Room 3", "name": "Device 3", "type": "com.fibaro.multilevelSwitch"}
fields = map[string]interface{}{"value": float64(67)}
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags)
// Ensure fields / values are correct - Device 4
tags = map[string]string{"section": "Section 3", "room": "Room 4", "name": "Device 4", "type": "com.fibaro.temperatureSensor"}
fields = map[string]interface{}{"value": float64(22.8)}
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags)
// Ensure fields / values are correct - Device 5
tags = map[string]string{"section": "Section 3", "room": "Room 4", "name": "Device 5", "type": "com.fibaro.FGRM222"}
fields = map[string]interface{}{"value": float64(50), "value2": float64(75)}
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags)
}

View File

@@ -33,6 +33,9 @@ InfluxDB-formatted endpoints. See below for more information.
### Measurements & Fields
**Note:** The measurements and fields are dynamically built from the InfluxDB source,
and may vary between versions.
- influxdb
- n_shards
- influxdb_database

View File

@@ -2,7 +2,9 @@
The [Jolokia](http://jolokia.org) _agent_ and _proxy_ input plugins collect JMX metrics from an HTTP endpoint using Jolokia's [JSON-over-HTTP protocol](https://jolokia.org/reference/html/protocol.html).
## Jolokia Agent Configuration
### Configuration:
#### Jolokia Agent Configuration
The `jolokia2_agent` input plugin reads JMX metrics from one or more [Jolokia agent](https://jolokia.org/agent/jvm.html) REST endpoints.
@@ -32,7 +34,7 @@ Optionally, specify SSL options for communicating with agents:
paths = ["Uptime"]
```
## Jolokia Proxy Configuration
#### Jolokia Proxy Configuration
The `jolokia2_proxy` input plugin reads JMX metrics from one or more _targets_ by interacting with a [Jolokia proxy](https://jolokia.org/features/proxy.html) REST endpoint.
@@ -77,7 +79,7 @@ Optionally, specify SSL options for communicating with proxies:
paths = ["Uptime"]
```
## Jolokia Metric Configuration
#### Jolokia Metric Configuration
Each `metric` declaration generates a Jolokia request to fetch telemetry from a JMX MBean.
@@ -167,3 +169,11 @@ Both `jolokia2_agent` and `jolokia2_proxy` plugins support default configuration
| `default_field_separator` | `.` | A character to use to join Mbean attributes when creating fields. |
| `default_field_prefix` | _None_ | A string to prepend to the field names produced by all `metric` declarations. |
| `default_tag_prefix` | _None_ | A string to prepend to the tag names produced by all `metric` declarations. |
### Example Configurations:
- [Java JVM](/plugins/inputs/jolokia2/examples/java.conf)
- [Kafka](/plugins/inputs/jolokia2/examples/kafka.conf)
- [Cassandra](/plugins/inputs/jolokia2/examples/cassandra.conf)
Please help improve this list and contribute new configuration files by opening an issue or pull request.

View File

@@ -0,0 +1,95 @@
[[inputs.jolokia2_agent]]
urls = ["http://localhost:8778/jolokia"]
name_prefix = "java_"
[[inputs.jolokia2_agent.metrics]]
name = "Memory"
mbean = "java.lang:type=Memory"
[[inputs.jolokia2_agent.metric]]
name = "GarbageCollector"
mbean = "java.lang:name=*,type=GarbageCollector"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent]]
urls = ["http://localhost:8778/jolokia"]
name_prefix = "cassandra_"
[[inputs.jolokia2_agent.metric]]
name = "Cache"
mbean = "org.apache.cassandra.metrics:name=*,scope=*,type=Cache"
tag_keys = ["name", "scope"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "Client"
mbean = "org.apache.cassandra.metrics:name=*,type=Client"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "ClientRequestMetrics"
mbean = "org.apache.cassandra.metrics:name=*,type=ClientRequestMetrics"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "ClientRequest"
mbean = "org.apache.cassandra.metrics:name=*,scope=*,type=ClientRequest"
tag_keys = ["name", "scope"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "ColumnFamily"
mbean = "org.apache.cassandra.metrics:keyspace=*,name=*,scope=*,type=ColumnFamily"
tag_keys = ["keyspace", "name", "scope"]
field_prefix = "$2_"
[[inputs.jolokia2_agent.metric]]
name = "CommitLog"
mbean = "org.apache.cassandra.metrics:name=*,type=CommitLog"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "Compaction"
mbean = "org.apache.cassandra.metrics:name=*,type=Compaction"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "CQL"
mbean = "org.apache.cassandra.metrics:name=*,type=CQL"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "DroppedMessage"
mbean = "org.apache.cassandra.metrics:name=*,scope=*,type=DroppedMessage"
tag_keys = ["name", "scope"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "FileCache"
mbean = "org.apache.cassandra.metrics:name=*,type=FileCache"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "ReadRepair"
mbean = "org.apache.cassandra.metrics:name=*,type=ReadRepair"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "Storage"
mbean = "org.apache.cassandra.metrics:name=*,type=Storage"
tag_keys = ["name"]
field_prefix = "$1_"
[[inputs.jolokia2_agent.metric]]
name = "ThreadPools"
mbean = "org.apache.cassandra.metrics:name=*,path=*,scope=*,type=ThreadPools"
tag_keys = ["name", "path", "scope"]
field_prefix = "$1_"

View File

@@ -56,13 +56,13 @@ func (jp *JolokiaProxy) SampleConfig() string {
## Add proxy targets to query
# default_target_username = ""
# default_target_password = ""
[[inputs.jolokia_proxy.target]]
[[inputs.jolokia2_proxy.target]]
url = "service:jmx:rmi:///jndi/rmi://targethost:9999/jmxrmi"
# username = ""
# password = ""
# username = ""
# password = ""
## Add metrics to read
[[inputs.jolokia_proxy.metric]]
[[inputs.jolokia2_proxy.metric]]
name = "java_runtime"
mbean = "java.lang:type=Runtime"
paths = ["Uptime"]

View File

@@ -0,0 +1,251 @@
# LeoFS Input Plugin
The LeoFS plugin gathers metrics of LeoGateway, LeoManager, and LeoStorage using SNMP. See [LeoFS Documentation / System Administration / System Monitoring](https://leo-project.net/leofs/docs/admin/system_admin/monitoring/).
## Configuration:
```toml
# Sample Config:
[[inputs.leofs]]
servers = ["127.0.0.1:4010"]
```
## Measurements & Fields:
### Statistics specific to the internals of LeoManager
#### Erlang VM
- 1 min Statistics
- num_of_processes
- total_memory_usage
- system_memory_usage
- processes_memory_usage
- ets_memory_usage
- used_allocated_memory
- allocated_memory
- 5 min Statistics
- num_of_processes_5min
- total_memory_usage_5min
- system_memory_usage_5min
- processes_memory_usage_5min
- ets_memory_usage_5min
- used_allocated_memory_5min
- allocated_memory_5min
### Statistics specific to the internals of LeoStorage
#### Erlang VM
- 1 min Statistics
- num_of_processes
- total_memory_usage
- system_memory_usage
- processes_memory_usage
- ets_memory_usage
- used_allocated_memory
- allocated_memory
- 5 min Statistics
- num_of_processes_5min
- total_memory_usage_5min
- system_memory_usage_5min
- processes_memory_usage_5min
- ets_memory_usage_5min
- used_allocated_memory_5min
- allocated_memory_5min
#### Total Number of Requests
- 1 min Statistics
- num_of_writes
- num_of_reads
- num_of_deletes
- 5 min Statistics
- num_of_writes_5min
- num_of_reads_5min
- num_of_deletes_5min
#### Total Number of Objects and Total Size of Objects
- num_of_active_objects
- total_objects
- total_size_of_active_objects
- total_size
#### Total Number of MQ Messages
- num_of_replication_messages,
- num_of_sync-vnode_messages,
- num_of_rebalance_messages,
- mq_num_of_msg_recovery_node
- mq_num_of_msg_deletion_dir
- mq_num_of_msg_async_deletion_dir
- mq_num_of_msg_req_deletion_dir
- mq_mdcr_num_of_msg_req_comp_metadata
- mq_mdcr_num_of_msg_req_sync_obj
Note: The following items are available since LeoFS v1.4.0:
- mq_num_of_msg_recovery_node
- mq_num_of_msg_deletion_dir
- mq_num_of_msg_async_deletion_dir
- mq_num_of_msg_req_deletion_dir
- mq_mdcr_num_of_msg_req_comp_metadata
- mq_mdcr_num_of_msg_req_sync_obj
#### Data Compaction
- comp_state
- comp_last_start_datetime
- comp_last_end_datetime
- comp_num_of_pending_targets
- comp_num_of_ongoing_targets
- comp_num_of_out_of_targets
Note: The all items are available since LeoFS v1.4.0.
### Statistics specific to the internals of LeoGateway
#### Erlang VM
- 1 min Statistics
- num_of_processes
- total_memory_usage
- system_memory_usage
- processes_memory_usage
- ets_memory_usage
- used_allocated_memory
- allocated_memory
- 5 min Statistics
- num_of_processes_5min
- total_memory_usage_5min
- system_memory_usage_5min
- processes_memory_usage_5min
- ets_memory_usage_5min
- used_allocated_memory_5min
- allocated_memory_5min
#### Total Number of Requests
- 1 min Statistics
- num_of_writes
- num_of_reads
- num_of_deletes
- 5 min Statistics
- num_of_writes_5min
- num_of_reads_5min
- num_of_deletes_5min
#### Object Cache
- count_of_cache-hit
- count_of_cache-miss
- total_of_files
- total_cached_size
### Tags:
All measurements have the following tags:
- node
### Example output:
#### LeoManager
```bash
$ ./telegraf --config ./plugins/inputs/leofs/leo_manager.conf --input-filter leofs --test
> leofs, host=manager_0, node=manager_0@127.0.0.1
allocated_memory=78255445,
allocated_memory_5min=78159025,
ets_memory_usage=4611900,
ets_memory_usage_5min=4632599,
num_of_processes=223,
num_of_processes_5min=223,
processes_memory_usage=20201316,
processes_memory_usage_5min=20186559,
system_memory_usage=37172701,
system_memory_usage_5min=37189213,
total_memory_usage=57373373,
total_memory_usage_5min=57374653,
used_allocated_memory=67,
used_allocated_memory_5min=67
1524105758000000000
```
#### LeoStorage
```bash
$ ./telegraf --config ./plugins/inputs/leofs/leo_storage.conf --input-filter leofs --test
> leofs,host=storage_0,node=storage_0@127.0.0.1
allocated_memory=63504384,
allocated_memory_5min=0,
comp_last_end_datetime=0,
comp_last_start_datetime=0,
comp_num_of_ongoing_targets=0,
comp_num_of_out_of_targets=0,
comp_num_of_pending_targets=8,
comp_state=0,
ets_memory_usage=3877824,
ets_memory_usage_5min=0,
mq_mdcr_num_of_msg_req_comp_metadata=0,
mq_mdcr_num_of_msg_req_sync_obj=0,
mq_num_of_msg_async_deletion_dir=0,
mq_num_of_msg_deletion_dir=0,
mq_num_of_msg_recovery_node=0,
mq_num_of_msg_req_deletion_dir=0,
num_of_active_objects=70,
num_of_deletes=0,
num_of_deletes_5min=0,
num_of_processes=577,
num_of_processes_5min=0,
num_of_reads=1,
num_of_reads_5min=0,
num_of_rebalance_messages=0,
num_of_replication_messages=0,
num_of_sync-vnode_messages=0,
num_of_writes=70,
num_of_writes_5min=0,
processes_memory_usage=20029464,
processes_memory_usage_5min=0,
system_memory_usage=25900472,
system_memory_usage_5min=0,
total_memory_usage=45920987,
total_memory_usage_5min=0,
total_objects=70,
total_size=2,
total_size_of_active_objects=2,
used_allocated_memory=69,
used_allocated_memory_5min=0
1524529826000000000
```
#### LeoGateway
```
$ ./telegraf --config ./plugins/inputs/leofs/leo_gateway.conf --input-filter leofs --test
> leofs, host=gateway_0, node=gateway_0@127.0.0.1
allocated_memory=87941120,
allocated_memory_5min=88067672,
count_of_cache-hit=0,
count_of_cache-miss=0,
ets_memory_usage=4843497,
ets_memory_usage_5min=4841574,
num_of_deletes=0,
num_of_deletes_5min=0,
num_of_processes=555,
num_of_processes_5min=555,
num_of_reads=0,
num_of_reads_5min=0,
num_of_writes=0,
num_of_writes_5min=0,
processes_memory_usage=17388052,
processes_memory_usage_5min=17413928,
system_memory_usage=49531263,
system_memory_usage_5min=49577819,
total_cached_size=0,
total_memory_usage=66917393,
total_memory_usage_5min=66989469,
total_of_files=0,
used_allocated_memory=69,
used_allocated_memory_5min=69 1524105894000000000
```

View File

@@ -93,6 +93,19 @@ var KeyMapping = map[ServerType][]string{
"allocated_memory",
"used_allocated_memory_5min",
"allocated_memory_5min",
// following items are since LeoFS v1.4.0
"mq_num_of_msg_recovery_node",
"mq_num_of_msg_deletion_dir",
"mq_num_of_msg_async_deletion_dir",
"mq_num_of_msg_req_deletion_dir",
"mq_mdcr_num_of_msg_req_comp_metadata",
"mq_mdcr_num_of_msg_req_sync_obj",
"comp_state",
"comp_last_start_datetime",
"comp_last_end_datetime",
"comp_num_of_pending_targets",
"comp_num_of_ongoing_targets",
"comp_num_of_out_of_targets",
},
ServerTypeGateway: {
"num_of_processes",

View File

@@ -42,34 +42,46 @@ package main
import "fmt"
const output = ` + "`" + `.1.3.6.1.4.1.35450.34.1.0 = STRING: "storage_0@127.0.0.1"
.1.3.6.1.4.1.35450.34.2.0 = Gauge32: 512
.1.3.6.1.4.1.35450.34.3.0 = Gauge32: 38126307
.1.3.6.1.4.1.35450.34.4.0 = Gauge32: 22308716
.1.3.6.1.4.1.35450.34.5.0 = Gauge32: 15816448
.1.3.6.1.4.1.35450.34.6.0 = Gauge32: 5232008
.1.3.6.1.4.1.35450.34.7.0 = Gauge32: 512
.1.3.6.1.4.1.35450.34.8.0 = Gauge32: 38113176
.1.3.6.1.4.1.35450.34.9.0 = Gauge32: 22313398
.1.3.6.1.4.1.35450.34.10.0 = Gauge32: 15798779
.1.3.6.1.4.1.35450.34.11.0 = Gauge32: 5237315
.1.3.6.1.4.1.35450.34.12.0 = Gauge32: 191
.1.3.6.1.4.1.35450.34.13.0 = Gauge32: 824
.1.3.6.1.4.1.35450.34.14.0 = Gauge32: 0
.1.3.6.1.4.1.35450.34.15.0 = Gauge32: 50105
.1.3.6.1.4.1.35450.34.16.0 = Gauge32: 196654
.1.3.6.1.4.1.35450.34.17.0 = Gauge32: 0
.1.3.6.1.4.1.35450.34.18.0 = Gauge32: 2052
.1.3.6.1.4.1.35450.34.19.0 = Gauge32: 50296
.1.3.6.1.4.1.35450.34.20.0 = Gauge32: 35
.1.3.6.1.4.1.35450.34.21.0 = Gauge32: 898
.1.3.6.1.4.1.35450.34.22.0 = Gauge32: 0
.1.3.6.1.4.1.35450.34.23.0 = Gauge32: 0
.1.3.6.1.4.1.35450.34.24.0 = Gauge32: 0
.1.3.6.1.4.1.35450.34.31.0 = Gauge32: 51
.1.3.6.1.4.1.35450.34.32.0 = Gauge32: 53219328
.1.3.6.1.4.1.35450.34.33.0 = Gauge32: 51
.1.3.6.1.4.1.35450.34.34.0 = Gauge32: 53351083` + "`" +
const output = ` + "`" + `.1.3.6.1.4.1.35450.56.1.0 = STRING: "storage_0@127.0.0.1"
.1.3.6.1.4.1.35450.56.2.0 = Gauge32: 512
.1.3.6.1.4.1.35450.56.3.0 = Gauge32: 38126307
.1.3.6.1.4.1.35450.56.4.0 = Gauge32: 22308716
.1.3.6.1.4.1.35450.56.5.0 = Gauge32: 15816448
.1.3.6.1.4.1.35450.56.6.0 = Gauge32: 5232008
.1.3.6.1.4.1.35450.56.7.0 = Gauge32: 512
.1.3.6.1.4.1.35450.56.8.0 = Gauge32: 38113176
.1.3.6.1.4.1.35450.56.9.0 = Gauge32: 22313398
.1.3.6.1.4.1.35450.56.10.0 = Gauge32: 15798779
.1.3.6.1.4.1.35450.56.11.0 = Gauge32: 5237315
.1.3.6.1.4.1.35450.56.12.0 = Gauge32: 191
.1.3.6.1.4.1.35450.56.13.0 = Gauge32: 824
.1.3.6.1.4.1.35450.56.14.0 = Gauge32: 0
.1.3.6.1.4.1.35450.56.15.0 = Gauge32: 50105
.1.3.6.1.4.1.35450.56.16.0 = Gauge32: 196654
.1.3.6.1.4.1.35450.56.17.0 = Gauge32: 0
.1.3.6.1.4.1.35450.56.18.0 = Gauge32: 2052
.1.3.6.1.4.1.35450.56.19.0 = Gauge32: 50296
.1.3.6.1.4.1.35450.56.20.0 = Gauge32: 35
.1.3.6.1.4.1.35450.56.21.0 = Gauge32: 898
.1.3.6.1.4.1.35450.56.22.0 = Gauge32: 0
.1.3.6.1.4.1.35450.56.23.0 = Gauge32: 0
.1.3.6.1.4.1.35450.56.24.0 = Gauge32: 0
.1.3.6.1.4.1.35450.56.31.0 = Gauge32: 51
.1.3.6.1.4.1.35450.56.32.0 = Gauge32: 53219328
.1.3.6.1.4.1.35450.56.33.0 = Gauge32: 51
.1.3.6.1.4.1.35450.56.34.0 = Gauge32: 53351083
.1.3.6.1.4.1.35450.56.41.0 = Gauge32: 101
.1.3.6.1.4.1.35450.56.42.0 = Gauge32: 216
.1.3.6.1.4.1.35450.56.43.0 = Gauge32: 313
.1.3.6.1.4.1.35450.56.44.0 = Gauge32: 421
.1.3.6.1.4.1.35450.56.45.0 = Gauge32: 597
.1.3.6.1.4.1.35450.56.46.0 = Gauge32: 628
.1.3.6.1.4.1.35450.56.51.0 = Gauge32: 1
.1.3.6.1.4.1.35450.56.52.0 = Gauge32: 1522154118
.1.3.6.1.4.1.35450.56.53.0 = Gauge32: 1522196496
.1.3.6.1.4.1.35450.56.54.0 = Gauge32: 1
.1.3.6.1.4.1.35450.56.55.0 = Gauge32: 7
.1.3.6.1.4.1.35450.56.56.0 = Gauge32: 0` + "`" +
`
func main() {
fmt.Println(output)

View File

@@ -80,6 +80,8 @@ Timestamp modifiers can be used to convert captures to the timestamp of the
parsed metric. If no timestamp is parsed the metric will be created using the
current time.
You must capture at least one field per line.
- Available modifiers:
- string (default if nothing is specified)
- int
@@ -108,10 +110,11 @@ CUSTOM time layouts must be within quotes and be the representation of the
"reference time", which is `Mon Jan 2 15:04:05 -0700 MST 2006`
See https://golang.org/pkg/time/#Parse for more details.
Telegraf has many of its own
[built-in patterns](./grok/patterns/influx-patterns),
as well as supporting
Telegraf has many of its own [built-in patterns](./grok/patterns/influx-patterns),
as well as support for most of
[logstash's builtin patterns](https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns).
_Golang regular expressions do not support lookahead or lookbehind.
logstash patterns that depend on these are not supported._
If you need help building patterns to match your logs,
you will find the https://grokdebug.herokuapp.com application quite useful!

View File

@@ -132,6 +132,7 @@ func (p *Parser) Compile() error {
// "custom patterns"
p.namedPatterns = make([]string, 0, len(p.Patterns))
for i, pattern := range p.Patterns {
pattern = strings.TrimSpace(pattern)
if pattern == "" {
continue
}

View File

@@ -958,3 +958,15 @@ func TestTimezoneLocalCompileFileAndParse(t *testing.T) {
assert.Equal(t, map[string]string{}, metricB.Tags())
assert.Equal(t, time.Date(2016, time.June, 4, 12, 41, 45, 0, time.Local).UnixNano(), metricB.Time().UnixNano())
}
func TestNewlineInPatterns(t *testing.T) {
p := &Parser{
Patterns: []string{`
%{SYSLOGTIMESTAMP:timestamp}
`},
}
require.NoError(t, p.Compile())
m, err := p.ParseLine("Apr 10 05:11:57")
require.NoError(t, err)
require.NotNil(t, m)
}

View File

@@ -0,0 +1,103 @@
# Mcrouter Input Plugin
This plugin gathers statistics data from a Mcrouter server.
### Configuration:
```toml
# Read metrics from one or many mcrouter servers.
[[inputs.mcrouter]]
## An array of address to gather stats about. Specify an ip or hostname
## with port. ie tcp://localhost:11211, tcp://10.0.0.1:11211, etc.
servers = ["tcp://localhost:11211", "unix:///var/run/mcrouter.sock"]
## Timeout for metric collections from all servers. Minimum timeout is "1s".
# timeout = "5s"
```
### Measurements & Fields:
The fields from this plugin are gathered in the *mcrouter* measurement.
Description of gathered fields can be found [here](https://github.com/facebook/mcrouter/wiki/Stats-list).
Fields:
* uptime
* num_servers
* num_servers_new
* num_servers_up
* num_servers_down
* num_servers_closed
* num_clients
* num_suspect_servers
* destination_batches_sum
* destination_requests_sum
* outstanding_route_get_reqs_queued
* outstanding_route_update_reqs_queued
* outstanding_route_get_avg_queue_size
* outstanding_route_update_avg_queue_size
* outstanding_route_get_avg_wait_time_sec
* outstanding_route_update_avg_wait_time_sec
* retrans_closed_connections
* destination_pending_reqs
* destination_inflight_reqs
* destination_batch_size
* asynclog_requests
* proxy_reqs_processing
* proxy_reqs_waiting
* client_queue_notify_period
* rusage_system
* rusage_user
* ps_num_minor_faults
* ps_num_major_faults
* ps_user_time_sec
* ps_system_time_sec
* ps_vsize
* ps_rss
* fibers_allocated
* fibers_pool_size
* fibers_stack_high_watermark
* successful_client_connections
* duration_us
* destination_max_pending_reqs
* destination_max_inflight_reqs
* retrans_per_kbyte_max
* cmd_get_count
* cmd_delete_out
* cmd_lease_get
* cmd_set
* cmd_get_out_all
* cmd_get_out
* cmd_lease_set_count
* cmd_other_out_all
* cmd_lease_get_out
* cmd_set_count
* cmd_lease_set_out
* cmd_delete_count
* cmd_other
* cmd_delete
* cmd_get
* cmd_lease_set
* cmd_set_out
* cmd_lease_get_count
* cmd_other_out
* cmd_lease_get_out_all
* cmd_set_out_all
* cmd_other_count
* cmd_delete_out_all
* cmd_lease_set_out_all
### Tags:
* Mcrouter measurements have the following tags:
- server (the host name from which metrics are gathered)
### Example Output:
```
$ ./telegraf --config telegraf.conf --input-filter mcrouter --test
mcrouter,server=localhost:11211 uptime=166,num_servers=1,num_servers_new=1,num_servers_up=0,num_servers_down=0,num_servers_closed=0,num_clients=1,num_suspect_servers=0,destination_batches_sum=0,destination_requests_sum=0,outstanding_route_get_reqs_queued=0,outstanding_route_update_reqs_queued=0,outstanding_route_get_avg_queue_size=0,outstanding_route_update_avg_queue_size=0,outstanding_route_get_avg_wait_time_sec=0,outstanding_route_update_avg_wait_time_sec=0,retrans_closed_connections=0,destination_pending_reqs=0,destination_inflight_reqs=0,destination_batch_size=0,asynclog_requests=0,proxy_reqs_processing=1,proxy_reqs_waiting=0,client_queue_notify_period=0,rusage_system=0.040966,rusage_user=0.020483,ps_num_minor_faults=2490,ps_num_major_faults=11,ps_user_time_sec=0.02,ps_system_time_sec=0.04,ps_vsize=697741312,ps_rss=10563584,fibers_allocated=0,fibers_pool_size=0,fibers_stack_high_watermark=0,successful_client_connections=18,duration_us=0,destination_max_pending_reqs=0,destination_max_inflight_reqs=0,retrans_per_kbyte_max=0,cmd_get_count=0,cmd_delete_out=0,cmd_lease_get=0,cmd_set=0,cmd_get_out_all=0,cmd_get_out=0,cmd_lease_set_count=0,cmd_other_out_all=0,cmd_lease_get_out=0,cmd_set_count=0,cmd_lease_set_out=0,cmd_delete_count=0,cmd_other=0,cmd_delete=0,cmd_get=0,cmd_lease_set=0,cmd_set_out=0,cmd_lease_get_count=0,cmd_other_out=0,cmd_lease_get_out_all=0,cmd_set_out_all=0,cmd_other_count=0,cmd_delete_out_all=0,cmd_lease_set_out_all=0 1453831884664956455
```

View File

@@ -0,0 +1,286 @@
package mcrouter
import (
"bufio"
"context"
"fmt"
"net"
"net/url"
"strconv"
"strings"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
// Mcrouter is a mcrouter plugin
type Mcrouter struct {
Servers []string
Timeout internal.Duration
}
// enum for statType
type statType int
const (
typeInt statType = iota
typeFloat statType = iota
)
var sampleConfig = `
## An array of address to gather stats about. Specify an ip or hostname
## with port. ie tcp://localhost:11211, tcp://10.0.0.1:11211, etc.
servers = ["tcp://localhost:11211", "unix:///var/run/mcrouter.sock"]
## Timeout for metric collections from all servers. Minimum timeout is "1s".
# timeout = "5s"
`
var defaultTimeout = 5 * time.Second
var defaultServerURL = url.URL{
Scheme: "tcp",
Host: "localhost:11211",
}
// The list of metrics that should be sent
var sendMetrics = map[string]statType{
"uptime": typeInt,
"num_servers": typeInt,
"num_servers_new": typeInt,
"num_servers_up": typeInt,
"num_servers_down": typeInt,
"num_servers_closed": typeInt,
"num_clients": typeInt,
"num_suspect_servers": typeInt,
"destination_batches_sum": typeInt,
"destination_requests_sum": typeInt,
"outstanding_route_get_reqs_queued": typeInt,
"outstanding_route_update_reqs_queued": typeInt,
"outstanding_route_get_avg_queue_size": typeInt,
"outstanding_route_update_avg_queue_size": typeInt,
"outstanding_route_get_avg_wait_time_sec": typeInt,
"outstanding_route_update_avg_wait_time_sec": typeInt,
"retrans_closed_connections": typeInt,
"destination_pending_reqs": typeInt,
"destination_inflight_reqs": typeInt,
"destination_batch_size": typeInt,
"asynclog_requests": typeInt,
"proxy_reqs_processing": typeInt,
"proxy_reqs_waiting": typeInt,
"client_queue_notify_period": typeInt,
"rusage_system": typeFloat,
"rusage_user": typeFloat,
"ps_num_minor_faults": typeInt,
"ps_num_major_faults": typeInt,
"ps_user_time_sec": typeFloat,
"ps_system_time_sec": typeFloat,
"ps_vsize": typeInt,
"ps_rss": typeInt,
"fibers_allocated": typeInt,
"fibers_pool_size": typeInt,
"fibers_stack_high_watermark": typeInt,
"successful_client_connections": typeInt,
"duration_us": typeInt,
"destination_max_pending_reqs": typeInt,
"destination_max_inflight_reqs": typeInt,
"retrans_per_kbyte_max": typeInt,
"cmd_get_count": typeInt,
"cmd_delete_out": typeInt,
"cmd_lease_get": typeInt,
"cmd_set": typeInt,
"cmd_get_out_all": typeInt,
"cmd_get_out": typeInt,
"cmd_lease_set_count": typeInt,
"cmd_other_out_all": typeInt,
"cmd_lease_get_out": typeInt,
"cmd_set_count": typeInt,
"cmd_lease_set_out": typeInt,
"cmd_delete_count": typeInt,
"cmd_other": typeInt,
"cmd_delete": typeInt,
"cmd_get": typeInt,
"cmd_lease_set": typeInt,
"cmd_set_out": typeInt,
"cmd_lease_get_count": typeInt,
"cmd_other_out": typeInt,
"cmd_lease_get_out_all": typeInt,
"cmd_set_out_all": typeInt,
"cmd_other_count": typeInt,
"cmd_delete_out_all": typeInt,
"cmd_lease_set_out_all": typeInt,
}
// SampleConfig returns sample configuration message
func (m *Mcrouter) SampleConfig() string {
return sampleConfig
}
// Description returns description of Mcrouter plugin
func (m *Mcrouter) Description() string {
return "Read metrics from one or many mcrouter servers"
}
// Gather reads stats from all configured servers accumulates stats
func (m *Mcrouter) Gather(acc telegraf.Accumulator) error {
ctx := context.Background()
if m.Timeout.Duration < 1*time.Second {
m.Timeout.Duration = defaultTimeout
}
ctx, cancel := context.WithTimeout(ctx, m.Timeout.Duration)
defer cancel()
if len(m.Servers) == 0 {
m.Servers = []string{defaultServerURL.String()}
}
for _, serverAddress := range m.Servers {
acc.AddError(m.gatherServer(ctx, serverAddress, acc))
}
return nil
}
// ParseAddress parses an address string into 'host:port' and 'protocol' parts
func (m *Mcrouter) ParseAddress(address string) (string, string, error) {
var protocol string
var host string
var port string
u, parseError := url.Parse(address)
if parseError != nil {
return "", "", fmt.Errorf("Invalid server address")
}
if u.Scheme != "tcp" && u.Scheme != "unix" {
return "", "", fmt.Errorf("Invalid server protocol")
}
protocol = u.Scheme
if protocol == "unix" {
if u.Path == "" {
return "", "", fmt.Errorf("Invalid unix socket path")
}
address = u.Path
} else {
if u.Host == "" {
return "", "", fmt.Errorf("Invalid host")
}
host = u.Hostname()
port = u.Port()
if host == "" {
host = defaultServerURL.Hostname()
}
if port == "" {
port = defaultServerURL.Port()
}
address = host + ":" + port
}
return address, protocol, nil
}
func (m *Mcrouter) gatherServer(ctx context.Context, address string, acc telegraf.Accumulator) error {
var conn net.Conn
var err error
var protocol string
var dialer net.Dialer
address, protocol, err = m.ParseAddress(address)
conn, err = dialer.DialContext(ctx, protocol, address)
if err != nil {
return err
}
defer conn.Close()
// Extend connection
deadline, ok := ctx.Deadline()
if ok {
conn.SetDeadline(deadline)
}
// Read and write buffer
reader := bufio.NewReader(conn)
scanner := bufio.NewScanner(reader)
// Send command
if _, err := fmt.Fprint(conn, "stats\r\n"); err != nil {
return err
}
values, err := parseResponse(scanner)
if err != nil {
return err
}
// Add server address as a tag
tags := map[string]string{"server": address}
// Process values
fields := make(map[string]interface{})
for key, sType := range sendMetrics {
if value, ok := values[key]; ok {
switch sType {
case typeInt:
if v, errParse := strconv.ParseInt(value, 10, 64); errParse == nil {
fields[key] = v
}
case typeFloat:
if v, errParse := strconv.ParseFloat(value, 64); errParse == nil {
fields[key] = v
}
default:
}
}
}
acc.AddFields("mcrouter", fields, tags)
return nil
}
func parseResponse(r *bufio.Scanner) (map[string]string, error) {
values := make(map[string]string)
for r.Scan() {
// Read line
line := r.Text()
// Done
if line == "END" {
break
}
// Read values
s := strings.SplitN(line, " ", 3)
if len(s) != 3 || s[0] != "STAT" {
return nil, fmt.Errorf("unexpected line in stats response: %s", line)
}
// Save values
values[s[1]] = s[2]
}
return values, nil
}
func init() {
inputs.Add("mcrouter", func() telegraf.Input {
return &Mcrouter{}
})
}

View File

@@ -0,0 +1,250 @@
package mcrouter
import (
"bufio"
"strings"
"testing"
"github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAddressParsing(t *testing.T) {
m := &Mcrouter{
Servers: []string{"tcp://" + testutil.GetLocalHost()},
}
var acceptTests = [][3]string{
{"tcp://localhost:8086", "localhost:8086", "tcp"},
{"tcp://localhost", "localhost:" + defaultServerURL.Port(), "tcp"},
{"tcp://localhost:", "localhost:" + defaultServerURL.Port(), "tcp"},
{"tcp://:8086", defaultServerURL.Hostname() + ":8086", "tcp"},
{"tcp://:", defaultServerURL.Host, "tcp"},
}
var rejectTests = []string{
"tcp://",
}
for _, args := range acceptTests {
address, protocol, err := m.ParseAddress(args[0])
assert.Nil(t, err, args[0])
assert.True(t, address == args[1], args[0])
assert.True(t, protocol == args[2], args[0])
}
for _, addr := range rejectTests {
address, protocol, err := m.ParseAddress(addr)
assert.NotNil(t, err, addr)
assert.Empty(t, address, addr)
assert.Empty(t, protocol, addr)
}
}
func TestMcrouterGeneratesMetrics(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
m := &Mcrouter{
Servers: []string{"tcp://" + testutil.GetLocalHost()},
}
var acc testutil.Accumulator
err := acc.GatherError(m.Gather)
require.NoError(t, err)
intMetrics := []string{"uptime", "num_servers", "num_servers_new", "num_servers_up",
"num_servers_down", "num_servers_closed", "num_clients",
"num_suspect_servers", "destination_batches_sum", "destination_requests_sum",
"outstanding_route_get_reqs_queued", "outstanding_route_update_reqs_queued",
"outstanding_route_get_avg_queue_size", "outstanding_route_update_avg_queue_size",
"outstanding_route_get_avg_wait_time_sec", "outstanding_route_update_avg_wait_time_sec",
"retrans_closed_connections", "destination_pending_reqs", "destination_inflight_reqs",
"destination_batch_size", "asynclog_requests", "proxy_reqs_processing",
"proxy_reqs_waiting", "client_queue_notify_period",
"ps_num_minor_faults", "ps_num_major_faults",
"ps_vsize", "ps_rss", "fibers_allocated", "fibers_pool_size", "fibers_stack_high_watermark",
"successful_client_connections", "duration_us", "destination_max_pending_reqs",
"destination_max_inflight_reqs", "retrans_per_kbyte_max", "cmd_get_count", "cmd_delete_out",
"cmd_lease_get", "cmd_set", "cmd_get_out_all", "cmd_get_out", "cmd_lease_set_count",
"cmd_other_out_all", "cmd_lease_get_out", "cmd_set_count", "cmd_lease_set_out",
"cmd_delete_count", "cmd_other", "cmd_delete", "cmd_get", "cmd_lease_set", "cmd_set_out",
"cmd_lease_get_count", "cmd_other_out", "cmd_lease_get_out_all", "cmd_set_out_all",
"cmd_other_count", "cmd_delete_out_all", "cmd_lease_set_out_all"}
floatMetrics := []string{"rusage_system", "rusage_user", "ps_user_time_sec", "ps_system_time_sec"}
for _, metric := range intMetrics {
assert.True(t, acc.HasInt64Field("mcrouter", metric), metric)
}
for _, metric := range floatMetrics {
assert.True(t, acc.HasFloatField("mcrouter", metric), metric)
}
}
func TestMcrouterParseMetrics(t *testing.T) {
r := bufio.NewReader(strings.NewReader(mcrouterStats))
scanner := bufio.NewScanner(r)
values, err := parseResponse(scanner)
require.NoError(t, err, "Error parsing mcrouter response")
tests := []struct {
key string
value string
}{
{"uptime", "166"},
{"num_servers", "1"},
{"num_servers_new", "1"},
{"num_servers_up", "0"},
{"num_servers_down", "0"},
{"num_servers_closed", "0"},
{"num_clients", "1"},
{"num_suspect_servers", "0"},
{"destination_batches_sum", "0"},
{"destination_requests_sum", "0"},
{"outstanding_route_get_reqs_queued", "0"},
{"outstanding_route_update_reqs_queued", "0"},
{"outstanding_route_get_avg_queue_size", "0"},
{"outstanding_route_update_avg_queue_size", "0"},
{"outstanding_route_get_avg_wait_time_sec", "0"},
{"outstanding_route_update_avg_wait_time_sec", "0"},
{"retrans_closed_connections", "0"},
{"destination_pending_reqs", "0"},
{"destination_inflight_reqs", "0"},
{"destination_batch_size", "0"},
{"asynclog_requests", "0"},
{"proxy_reqs_processing", "1"},
{"proxy_reqs_waiting", "0"},
{"client_queue_notify_period", "0"},
{"rusage_system", "0.040966"},
{"rusage_user", "0.020483"},
{"ps_num_minor_faults", "2490"},
{"ps_num_major_faults", "11"},
{"ps_user_time_sec", "0.02"},
{"ps_system_time_sec", "0.04"},
{"ps_vsize", "697741312"},
{"ps_rss", "10563584"},
{"fibers_allocated", "0"},
{"fibers_pool_size", "0"},
{"fibers_stack_high_watermark", "0"},
{"successful_client_connections", "18"},
{"duration_us", "0"},
{"destination_max_pending_reqs", "0"},
{"destination_max_inflight_reqs", "0"},
{"retrans_per_kbyte_max", "0"},
{"cmd_get_count", "0"},
{"cmd_delete_out", "0"},
{"cmd_lease_get", "0"},
{"cmd_set", "0"},
{"cmd_get_out_all", "0"},
{"cmd_get_out", "0"},
{"cmd_lease_set_count", "0"},
{"cmd_other_out_all", "0"},
{"cmd_lease_get_out", "0"},
{"cmd_set_count", "0"},
{"cmd_lease_set_out", "0"},
{"cmd_delete_count", "0"},
{"cmd_other", "0"},
{"cmd_delete", "0"},
{"cmd_get", "0"},
{"cmd_lease_set", "0"},
{"cmd_set_out", "0"},
{"cmd_lease_get_count", "0"},
{"cmd_other_out", "0"},
{"cmd_lease_get_out_all", "0"},
{"cmd_set_out_all", "0"},
{"cmd_other_count", "0"},
{"cmd_delete_out_all", "0"},
{"cmd_lease_set_out_all", "0"},
}
for _, test := range tests {
value, ok := values[test.key]
if !ok {
t.Errorf("Did not find key for metric %s in values", test.key)
continue
}
if value != test.value {
t.Errorf("Metric: %s, Expected: %s, actual: %s",
test.key, test.value, value)
}
}
}
var mcrouterStats = `STAT version 36.0.0 mcrouter
STAT commandargs --port 11211 --config-file /etc/mcrouter/mcrouter.json --async-dir /var/spool/mcrouter --log-path /var/log/mcrouter/mcrouter.log --stats-root /var/mcrouter/stats --server-timeout 100 --reset-inactive-connection-interval 10000 --proxy-threads auto
STAT pid 21357
STAT parent_pid 1
STAT time 1524673265
STAT uptime 166
STAT num_servers 1
STAT num_servers_new 1
STAT num_servers_up 0
STAT num_servers_down 0
STAT num_servers_closed 0
STAT num_clients 1
STAT num_suspect_servers 0
STAT destination_batches_sum 0
STAT destination_requests_sum 0
STAT outstanding_route_get_reqs_queued 0
STAT outstanding_route_update_reqs_queued 0
STAT outstanding_route_get_avg_queue_size 0
STAT outstanding_route_update_avg_queue_size 0
STAT outstanding_route_get_avg_wait_time_sec 0
STAT outstanding_route_update_avg_wait_time_sec 0
STAT retrans_closed_connections 0
STAT destination_pending_reqs 0
STAT destination_inflight_reqs 0
STAT destination_batch_size 0
STAT asynclog_requests 0
STAT proxy_reqs_processing 1
STAT proxy_reqs_waiting 0
STAT client_queue_notify_period 0
STAT rusage_system 0.040966
STAT rusage_user 0.020483
STAT ps_num_minor_faults 2490
STAT ps_num_major_faults 11
STAT ps_user_time_sec 0.02
STAT ps_system_time_sec 0.04
STAT ps_vsize 697741312
STAT ps_rss 10563584
STAT fibers_allocated 0
STAT fibers_pool_size 0
STAT fibers_stack_high_watermark 0
STAT successful_client_connections 18
STAT duration_us 0
STAT destination_max_pending_reqs 0
STAT destination_max_inflight_reqs 0
STAT retrans_per_kbyte_max 0
STAT cmd_get_count 0
STAT cmd_delete_out 0
STAT cmd_lease_get 0
STAT cmd_set 0
STAT cmd_get_out_all 0
STAT cmd_get_out 0
STAT cmd_lease_set_count 0
STAT cmd_other_out_all 0
STAT cmd_lease_get_out 0
STAT cmd_set_count 0
STAT cmd_lease_set_out 0
STAT cmd_delete_count 0
STAT cmd_other 0
STAT cmd_delete 0
STAT cmd_get 0
STAT cmd_lease_set 0
STAT cmd_set_out 0
STAT cmd_lease_get_count 0
STAT cmd_other_out 0
STAT cmd_lease_get_out_all 0
STAT cmd_set_out_all 0
STAT cmd_other_count 0
STAT cmd_delete_out_all 0
STAT cmd_lease_set_out_all 0
END
`

View File

@@ -1,6 +1,6 @@
# Telegraf plugin: MongoDB
# MongoDB Input Plugin
#### Configuration
### Configuration:
```toml
[[inputs.mongodb]]
@@ -10,7 +10,9 @@
## mongodb://user:auth_key@10.10.3.30:27017,
## mongodb://10.10.3.33:18832,
servers = ["mongodb://127.0.0.1:27017"]
gather_perdb_stats = false
## When true, collect per database stats
# gather_perdb_stats = false
## Optional SSL Config
# ssl_ca = "/etc/telegraf/ca.pem"
@@ -19,53 +21,106 @@
## Use SSL but skip chain & host verification
# insecure_skip_verify = false
```
This connection uri may be different based on your environment and mongodb
setup. If the user doesn't have the required privilege to execute serverStatus
command the you will get this error on telegraf
#### Permissions:
If your MongoDB instance has access control enabled you will need to connect
as a user with sufficient rights.
With MongoDB 3.4 and higher, the `clusterMonitor` role can be used. In
version 3.2 you may also need these additional permissions:
```
> db.grantRolesToUser("user", [{role: "read", actions: "find", db: "local"}])
```
If the user is missing required privileges you may see an error in the
Telegraf logs similar to:
```
Error in input [mongodb]: not authorized on admin to execute command { serverStatus: 1, recordStats: 0 }
```
#### Description
### Metrics:
The telegraf plugin collects mongodb stats exposed by serverStatus and few more
and create a single measurement containing values e.g.
* active_reads
* active_writes
* commands_per_sec
* deletes_per_sec
* flushes_per_sec
* getmores_per_sec
* inserts_per_sec
* net_in_bytes
* net_out_bytes
* open_connections
* percent_cache_dirty
* percent_cache_used
* queries_per_sec
* queued_reads
* queued_writes
* resident_megabytes
* updates_per_sec
* vsize_megabytes
* total_in_use
* total_available
* total_created
* total_refreshing
* ttl_deletes_per_sec
* ttl_passes_per_sec
* repl_lag
* jumbo_chunks (only if mongos or mongo config)
- mongodb
- tags:
- hostname
- fields:
- active_reads (integer)
- active_writes (integer)
- commands_per_sec (integer)
- deletes_per_sec (integer)
- flushes_per_sec (integer)
- getmores_per_sec (integer)
- inserts_per_sec (integer)
- jumbo_chunks (integer)
- member_status (string)
- net_in_bytes (integer)
- net_out_bytes (integer)
- open_connections (integer)
- percent_cache_dirty (float)
- percent_cache_used (float)
- queries_per_sec (integer)
- queued_reads (integer)
- queued_writes (integer)
- repl_commands_per_sec (integer)
- repl_deletes_per_sec (integer)
- repl_getmores_per_sec (integer)
- repl_inserts_per_sec (integer)
- repl_lag (integer)
- repl_queries_per_sec (integer)
- repl_updates_per_sec (integer)
- repl_oplog_window_sec (integer)
- resident_megabytes (integer)
- state (string)
- total_available (integer)
- total_created (integer)
- total_in_use (integer)
- total_refreshing (integer)
- ttl_deletes_per_sec (integer)
- ttl_passes_per_sec (integer)
- updates_per_sec (integer)
- vsize_megabytes (integer)
- wtcache_app_threads_page_read_count (integer)
- wtcache_app_threads_page_read_time (integer)
- wtcache_app_threads_page_write_count (integer)
- wtcache_bytes_read_into (integer)
- wtcache_bytes_written_from (integer)
- wtcache_current_bytes (integer)
- wtcache_max_bytes_configured (integer)
- wtcache_pages_evicted_by_app_thread (integer)
- wtcache_pages_queued_for_eviction (integer)
- wtcache_server_evicting_pages (integer)
- wtcache_tracked_dirty_bytes (integer)
- wtcache_worker_thread_evictingpages (integer)
If gather_db_stats is set to true, it will also collect per database stats exposed by db.stats()
creating another measurement called mongodb_db_stats and containing values:
* collections
* objects
* avg_obj_size
* data_size
* storage_size
* num_extents
* indexes
* index_size
* ok
- mongodb_db_stats
- tags:
- db_name
- hostname
- fields:
- avg_obj_size (float)
- collections (integer)
- data_size (integer)
- index_size (integer)
- indexes (integer)
- num_extents (integer)
- objects (integer)
- ok (integer)
- storage_size (integer)
- type (string)
- mongodb_shard_stats
- tags:
- hostname
- fields:
- in_use (integer)
- available (integer)
- created (integer)
- refreshing (integer)
### Example Output:
```
mongodb,hostname=127.0.0.1:27017 active_reads=0i,active_writes=0i,commands_per_sec=6i,deletes_per_sec=0i,flushes_per_sec=0i,getmores_per_sec=1i,inserts_per_sec=0i,jumbo_chunks=0i,member_status="PRI",net_in_bytes=851i,net_out_bytes=23904i,open_connections=6i,percent_cache_dirty=0,percent_cache_used=0,queries_per_sec=2i,queued_reads=0i,queued_writes=0i,repl_commands_per_sec=0i,repl_deletes_per_sec=0i,repl_getmores_per_sec=0i,repl_inserts_per_sec=0i,repl_lag=0i,repl_queries_per_sec=0i,repl_updates_per_sec=0i,resident_megabytes=67i,state="PRIMARY",total_available=0i,total_created=0i,total_in_use=0i,total_refreshing=0i,ttl_deletes_per_sec=0i,ttl_passes_per_sec=0i,updates_per_sec=0i,vsize_megabytes=729i,wtcache_app_threads_page_read_count=4i,wtcache_app_threads_page_read_time=18i,wtcache_app_threads_page_write_count=6i,wtcache_bytes_read_into=10075i,wtcache_bytes_written_from=115711i,wtcache_current_bytes=86038i,wtcache_max_bytes_configured=1073741824i,wtcache_pages_evicted_by_app_thread=0i,wtcache_pages_queued_for_eviction=0i,wtcache_server_evicting_pages=0i,wtcache_tracked_dirty_bytes=0i,wtcache_worker_thread_evictingpages=0i 1522798796000000000
mongodb_db_stats,db_name=local,hostname=127.0.0.1:27017 avg_obj_size=818.625,collections=5i,data_size=6549i,index_size=86016i,indexes=4i,num_extents=0i,objects=8i,ok=1i,storage_size=118784i,type="db_stat" 1522799074000000000
mongodb_shard_stats,hostname=127.0.0.1:27017,in_use=3i,available=3i,created=4i,refreshing=0i 1522799074000000000
```

View File

@@ -45,7 +45,9 @@ var sampleConfig = `
## mongodb://user:auth_key@10.10.3.30:27017,
## mongodb://10.10.3.33:18832,
servers = ["mongodb://127.0.0.1:27017"]
gather_perdb_stats = false
## When true, collect per database stats
# gather_perdb_stats = false
## Optional SSL Config
# ssl_ca = "/etc/telegraf/ca.pem"
@@ -149,6 +151,9 @@ func (m *MongoDB) gatherServer(server *Server, acc telegraf.Accumulator) error {
} else {
tlsConfig, err = internal.GetTLSConfig(
m.SSLCert, m.SSLKey, m.SSLCA, m.InsecureSkipVerify)
if err != nil {
return err
}
}
// If configured to use TLS, add a dial function

View File

@@ -9,10 +9,11 @@ import (
)
type MongodbData struct {
StatLine *StatLine
Fields map[string]interface{}
Tags map[string]string
DbData []DbData
StatLine *StatLine
Fields map[string]interface{}
Tags map[string]string
DbData []DbData
ShardHostData []DbData
}
type DbData struct {
@@ -60,6 +61,7 @@ var DefaultReplStats = map[string]string{
"member_status": "NodeType",
"state": "NodeState",
"repl_lag": "ReplLag",
"repl_oplog_window_sec": "OplogTimeDiff",
}
var DefaultClusterStats = map[string]string{
@@ -73,6 +75,13 @@ var DefaultShardStats = map[string]string{
"total_refreshing": "TotalRefreshing",
}
var ShardHostStats = map[string]string{
"in_use": "InUse",
"available": "Available",
"created": "Created",
"refreshing": "Refreshing",
}
var MmapStats = map[string]string{
"mapped_megabytes": "Mapped",
"non-mapped_megabytes": "NonMapped",
@@ -127,6 +136,22 @@ func (d *MongodbData) AddDbStats() {
}
}
func (d *MongodbData) AddShardHostStats() {
for host, hostStat := range d.StatLine.ShardHostStatsLines {
hostStatLine := reflect.ValueOf(&hostStat).Elem()
newDbData := &DbData{
Name: host,
Fields: make(map[string]interface{}),
}
newDbData.Fields["type"] = "shard_host_stat"
for k, v := range ShardHostStats {
val := hostStatLine.FieldByName(v).Interface()
newDbData.Fields[k] = val
}
d.ShardHostData = append(d.ShardHostData, *newDbData)
}
}
func (d *MongodbData) AddDefaultStats() {
statLine := reflect.ValueOf(d.StatLine).Elem()
d.addStat(statLine, DefaultStats)
@@ -178,4 +203,14 @@ func (d *MongodbData) flush(acc telegraf.Accumulator) {
)
db.Fields = make(map[string]interface{})
}
for _, host := range d.ShardHostData {
d.Tags["hostname"] = host.Name
acc.AddFields(
"mongodb_shard_stats",
host.Fields,
d.Tags,
d.StatLine.Time,
)
host.Fields = make(map[string]interface{})
}
}

View File

@@ -1,6 +1,7 @@
package mongodb
import (
"sort"
"testing"
"time"
@@ -120,6 +121,43 @@ func TestAddShardStats(t *testing.T) {
}
}
func TestAddShardHostStats(t *testing.T) {
expectedHosts := []string{"hostA", "hostB"}
hostStatLines := map[string]ShardHostStatLine{}
for _, host := range expectedHosts {
hostStatLines[host] = ShardHostStatLine{
InUse: 0,
Available: 0,
Created: 0,
Refreshing: 0,
}
}
d := NewMongodbData(
&StatLine{
ShardHostStatsLines: hostStatLines,
},
map[string]string{}, // Use empty tags, so we don't break existing tests
)
var acc testutil.Accumulator
d.AddShardHostStats()
d.flush(&acc)
var hostsFound []string
for host, _ := range hostStatLines {
for key, _ := range ShardHostStats {
assert.True(t, acc.HasInt64Field("mongodb_shard_stats", key))
}
assert.True(t, acc.HasTag("mongodb_shard_stats", "hostname"))
hostsFound = append(hostsFound, host)
}
sort.Strings(hostsFound)
sort.Strings(expectedHosts)
assert.Equal(t, hostsFound, expectedHosts)
}
func TestStateTag(t *testing.T) {
d := NewMongodbData(
&StatLine{
@@ -162,6 +200,7 @@ func TestStateTag(t *testing.T) {
"repl_queries_per_sec": int64(0),
"repl_updates_per_sec": int64(0),
"repl_lag": int64(0),
"repl_oplog_window_sec": int64(0),
"resident_megabytes": int64(0),
"updates_per_sec": int64(0),
"vsize_megabytes": int64(0),

View File

@@ -22,6 +22,41 @@ func (s *Server) getDefaultTags() map[string]string {
return tags
}
type oplogEntry struct {
Timestamp bson.MongoTimestamp `bson:"ts"`
}
func (s *Server) gatherOplogStats() *OplogStats {
stats := &OplogStats{}
localdb := s.Session.DB("local")
op_first := oplogEntry{}
op_last := oplogEntry{}
query := bson.M{"ts": bson.M{"$exists": true}}
for _, collection_name := range []string{"oplog.rs", "oplog.$main"} {
if err := localdb.C(collection_name).Find(query).Sort("$natural").Limit(1).One(&op_first); err != nil {
if err == mgo.ErrNotFound {
continue
}
log.Println("E! Error getting first oplog entry (" + err.Error() + ")")
return stats
}
if err := localdb.C(collection_name).Find(query).Sort("-$natural").Limit(1).One(&op_last); err != nil {
if err == mgo.ErrNotFound {
continue
}
log.Println("E! Error getting last oplog entry (" + err.Error() + ")")
return stats
}
}
op_first_time := time.Unix(int64(op_first.Timestamp>>32), 0)
op_last_time := time.Unix(int64(op_last.Timestamp>>32), 0)
stats.TimeDiff = int64(op_last_time.Sub(op_first_time).Seconds())
return stats
}
func (s *Server) gatherData(acc telegraf.Accumulator, gatherDbStats bool) error {
s.Session.SetMode(mgo.Eventual, true)
s.Session.SetSocketTimeout(0)
@@ -66,6 +101,8 @@ func (s *Server) gatherData(acc telegraf.Accumulator, gatherDbStats bool) error
log.Println("E! Error getting database shard stats (" + err.Error() + ")")
}
oplogStats := s.gatherOplogStats()
result_db_stats := &DbStats{}
if gatherDbStats == true {
names := []string{}
@@ -99,6 +136,7 @@ func (s *Server) gatherData(acc telegraf.Accumulator, gatherDbStats bool) error
ClusterStatus: result_cluster,
DbStats: result_db_stats,
ShardStats: resultShards,
OplogStats: oplogStats,
}
defer func() {
@@ -118,6 +156,7 @@ func (s *Server) gatherData(acc telegraf.Accumulator, gatherDbStats bool) error
)
data.AddDefaultStats()
data.AddDbStats()
data.AddShardHostStats()
data.flush(acc)
}
return nil

View File

@@ -35,6 +35,7 @@ type MongoStatus struct {
ClusterStatus *ClusterStatus
DbStats *DbStats
ShardStats *ShardStats
OplogStats *OplogStats
}
type ServerStatus struct {
@@ -102,6 +103,11 @@ type ReplSetStatus struct {
MyState int64 `bson:"myState"`
}
// OplogStatus stores information from getReplicationInfo
type OplogStats struct {
TimeDiff int64
}
// ReplSetMember stores information related to a replica set member
type ReplSetMember struct {
Name string `bson:"name"`
@@ -119,12 +125,27 @@ type WiredTiger struct {
// ShardStats stores information from shardConnPoolStats.
type ShardStats struct {
ShardStatsData `bson:",inline"`
Hosts map[string]ShardHostStatsData `bson:"hosts"`
}
// ShardStatsData is the total Shard Stats from shardConnPoolStats database command.
type ShardStatsData struct {
TotalInUse int64 `bson:"totalInUse"`
TotalAvailable int64 `bson:"totalAvailable"`
TotalCreated int64 `bson:"totalCreated"`
TotalRefreshing int64 `bson:"totalRefreshing"`
}
// ShardHostStatsData is the host-specific stats
// from shardConnPoolStats database command.
type ShardHostStatsData struct {
InUse int64 `bson:"inUse"`
Available int64 `bson:"available"`
Created int64 `bson:"created"`
Refreshing int64 `bson:"refreshing"`
}
type ConcurrentTransactions struct {
Write ConcurrentTransStats `bson:"write"`
Read ConcurrentTransStats `bson:"read"`
@@ -442,6 +463,7 @@ type StatLine struct {
// Replicated Opcounter fields
InsertR, QueryR, UpdateR, DeleteR, GetMoreR, CommandR int64
ReplLag int64
OplogTimeDiff int64
Flushes int64
Mapped, Virtual, Resident, NonMapped int64
Faults int64
@@ -462,6 +484,9 @@ type StatLine struct {
// Shard stats
TotalInUse, TotalAvailable, TotalCreated, TotalRefreshing int64
// Shard Hosts stats field
ShardHostStatsLines map[string]ShardHostStatLine
}
type DbStatLine struct {
@@ -477,6 +502,13 @@ type DbStatLine struct {
Ok int64
}
type ShardHostStatLine struct {
InUse int64
Available int64
Created int64
Refreshing int64
}
func parseLocks(stat ServerStatus) map[string]LockUsage {
returnVal := map[string]LockUsage{}
for namespace, lockInfo := range stat.Locks {
@@ -772,6 +804,7 @@ func NewStatLine(oldMongo, newMongo MongoStatus, key string, all bool, sampleSec
newClusterStat := *newMongo.ClusterStatus
returnVal.JumboChunksCount = newClusterStat.JumboChunksCount
returnVal.OplogTimeDiff = newMongo.OplogStats.TimeDiff
newDbStats := *newMongo.DbStats
for _, db := range newDbStats.Dbs {
@@ -801,6 +834,17 @@ func NewStatLine(oldMongo, newMongo MongoStatus, key string, all bool, sampleSec
returnVal.TotalAvailable = newShardStats.TotalAvailable
returnVal.TotalCreated = newShardStats.TotalCreated
returnVal.TotalRefreshing = newShardStats.TotalRefreshing
returnVal.ShardHostStatsLines = map[string]ShardHostStatLine{}
for host, stats := range newShardStats.Hosts {
shardStatLine := &ShardHostStatLine{
InUse: stats.InUse,
Available: stats.Available,
Created: stats.Created,
Refreshing: stats.Refreshing,
}
returnVal.ShardHostStatsLines[host] = *shardStatLine
}
return returnVal
}

View File

@@ -1,5 +1,3 @@
// +build !windows
package ntpq
import (

View File

@@ -0,0 +1,47 @@
# `nvidia-smi` Input Plugin
This plugin uses a query on the [`nvidia-smi`](https://developer.nvidia.com/nvidia-system-management-interface) binary to pull GPU stats including memory and GPU usage, temp and other.
### Configuration
```toml
# Pulls statistics from nvidia GPUs attached to the host
[[inputs.nvidia_smi]]
## Optional: path to nvidia-smi binary, defaults to $PATH via exec.LookPath
# bin_path = /usr/bin/nvidia-smi
## Optional: timeout for GPU polling
# timeout = 5s
```
### Metrics
- measurement: `nvidia_smi`
- tags
- `name` (type of GPU e.g. `GeForce GTX 170 Ti`)
- `compute_mode` (The compute mode of the GPU e.g. `Default`)
- `index` (The port index where the GPU is connected to the motherboard e.g. `1`)
- `pstate` (Overclocking state for the GPU e.g. `P0`)
- `uuid` (A unique identifier for the GPU e.g. `GPU-f9ba66fc-a7f5-94c5-da19-019ef2f9c665`)
- fields
- `fan_speed` (integer, percentage)
- `memory_free` (integer, KB)
- `memory_used` (integer, KB)
- `memory_total` (integer, KB)
- `temperature_gpu` (integer, degrees C)
- `utilization_gpu` (integer, percentage)
- `utilization_memory` (integer, percentage)
### Sample Query
The below query could be used to alert on the average temperature of the your GPUs over the last minute
```
SELECT mean("temperature_gpu") FROM "nvidia_smi" WHERE time > now() - 5m GROUP BY time(1m), "index", "name", "host"
```
### Example Output
```
nvidia_smi,compute_mode=Default,host=8218cf,index=0,name=GeForce\ GTX\ 1070,pstate=P2,uuid=GPU-823bc202-6279-6f2c-d729-868a30f14d96 fan_speed=100i,memory_free=7563i,memory_total=8112i,memory_used=549i,temperature_gpu=53i,utilization_gpu=100i,utilization_memory=90i 1523991122000000000
nvidia_smi,compute_mode=Default,host=8218cf,index=1,name=GeForce\ GTX\ 1080,pstate=P2,uuid=GPU-f9ba66fc-a7f5-94c5-da19-019ef2f9c665 fan_speed=100i,memory_free=7557i,memory_total=8114i,memory_used=557i,temperature_gpu=50i,utilization_gpu=100i,utilization_memory=85i 1523991122000000000
nvidia_smi,compute_mode=Default,host=8218cf,index=2,name=GeForce\ GTX\ 1080,pstate=P2,uuid=GPU-d4cfc28d-0481-8d07-b81a-ddfc63d74adf fan_speed=100i,memory_free=7557i,memory_total=8114i,memory_used=557i,temperature_gpu=58i,utilization_gpu=100i,utilization_memory=86i 1523991122000000000
```

View File

@@ -0,0 +1,149 @@
package nvidia_smi
import (
"bufio"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
var (
measurement = "nvidia_smi"
metrics = "fan.speed,memory.total,memory.used,memory.free,pstate,temperature.gpu,name,uuid,compute_mode,utilization.gpu,utilization.memory,index"
metricNames = [][]string{
[]string{"fan_speed", "field"},
[]string{"memory_total", "field"},
[]string{"memory_used", "field"},
[]string{"memory_free", "field"},
[]string{"pstate", "tag"},
[]string{"temperature_gpu", "field"},
[]string{"name", "tag"},
[]string{"uuid", "tag"},
[]string{"compute_mode", "tag"},
[]string{"utilization_gpu", "field"},
[]string{"utilization_memory", "field"},
[]string{"index", "tag"},
}
)
// NvidiaSMI holds the methods for this plugin
type NvidiaSMI struct {
BinPath string
Timeout internal.Duration
metrics string
}
// Description returns the description of the NvidiaSMI plugin
func (smi *NvidiaSMI) Description() string {
return "Pulls statistics from nvidia GPUs attached to the host"
}
// SampleConfig returns the sample configuration for the NvidiaSMI plugin
func (smi *NvidiaSMI) SampleConfig() string {
return `
## Optional: path to nvidia-smi binary, defaults to $PATH via exec.LookPath
# bin_path = /usr/bin/nvidia-smi
## Optional: timeout for GPU polling
# timeout = 5s
`
}
// Gather implements the telegraf interface
func (smi *NvidiaSMI) Gather(acc telegraf.Accumulator) error {
if _, err := os.Stat(smi.BinPath); os.IsNotExist(err) {
return fmt.Errorf("nvidia-smi binary not at path %s, cannot gather GPU data", smi.BinPath)
}
data, err := smi.pollSMI()
if err != nil {
return err
}
err = gatherNvidiaSMI(data, acc)
if err != nil {
return err
}
return nil
}
func init() {
inputs.Add("nvidia_smi", func() telegraf.Input {
return &NvidiaSMI{
BinPath: "/usr/bin/nvidia-smi",
Timeout: internal.Duration{Duration: 5 * time.Second},
metrics: metrics,
}
})
}
func (smi *NvidiaSMI) pollSMI() (string, error) {
// Construct and execute metrics query
opts := []string{"--format=noheader,nounits,csv", fmt.Sprintf("--query-gpu=%s", smi.metrics)}
ret, err := internal.CombinedOutputTimeout(exec.Command(smi.BinPath, opts...), smi.Timeout.Duration)
if err != nil {
return "", err
}
return string(ret), nil
}
func gatherNvidiaSMI(ret string, acc telegraf.Accumulator) error {
// First split the lines up and handle each one
scanner := bufio.NewScanner(strings.NewReader(ret))
for scanner.Scan() {
tags, fields, err := parseLine(scanner.Text())
if err != nil {
return err
}
acc.AddFields(measurement, fields, tags)
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("Error scanning text %s", ret)
}
return nil
}
func parseLine(line string) (map[string]string, map[string]interface{}, error) {
tags := make(map[string]string, 0)
fields := make(map[string]interface{}, 0)
// Next split up the comma delimited metrics
met := strings.Split(line, ",")
// Make sure there are as many metrics in the line as there were queried.
if len(met) == len(metricNames) {
for i, m := range metricNames {
// First handle the tags
if m[1] == "tag" {
tags[m[0]] = strings.TrimSpace(met[i])
continue
}
// Then parse the integers out of the fields
out, err := strconv.ParseInt(strings.TrimSpace(met[i]), 10, 64)
if err != nil {
return tags, fields, err
}
fields[m[0]] = out
}
// Return the tags and fields
return tags, fields, nil
}
// If the line is empty return an emptyline error
return tags, fields, fmt.Errorf("Different number of metrics returned (%d) than expeced (%d)", len(met), len(metricNames))
}

View File

@@ -0,0 +1,35 @@
package nvidia_smi
import (
"testing"
)
func TestParseLineStandard(t *testing.T) {
line := "85, 8114, 553, 7561, P2, 61, GeForce GTX 1070 Ti, GPU-d1911b8a-f5c8-5e66-057c-486561269de8, Default, 100, 93, 1\n"
tags, fields, err := parseLine(line)
if err != nil {
t.Fail()
}
if tags["name"] != "GeForce GTX 1070 Ti" {
t.Fail()
}
if temp, ok := fields["temperature_gpu"].(int); ok && temp == 61 {
t.Fail()
}
}
func TestParseLineEmptyLine(t *testing.T) {
line := "\n"
_, _, err := parseLine(line)
if err == nil {
t.Fail()
}
}
func TestParseLineBad(t *testing.T) {
line := "the quick brown fox jumped over the lazy dog"
_, _, err := parseLine(line)
if err == nil {
t.Fail()
}
}

View File

@@ -24,7 +24,7 @@ Get phpfpm stats using either HTTP status page or fpm socket.
## "fcgi://10.0.0.12:9000/status"
## "cgi://10.0.10.12:9001/status"
##
## Example of multiple gathering from local socket and remove host
## Example of multiple gathering from local socket and remote host
## urls = ["http://192.168.1.20/status", "/tmp/fpm.sock"]
urls = ["http://localhost/status"]
```

View File

@@ -171,17 +171,17 @@ func (p *Ping) args(url string) []string {
// Build the ping command args based on toml config
args := []string{"-c", strconv.Itoa(p.Count), "-n", "-s", "16"}
if p.PingInterval > 0 {
args = append(args, "-i", strconv.FormatFloat(p.PingInterval, 'f', 1, 64))
args = append(args, "-i", strconv.FormatFloat(p.PingInterval, 'f', -1, 64))
}
if p.Timeout > 0 {
switch runtime.GOOS {
case "darwin":
args = append(args, "-W", strconv.FormatFloat(p.Timeout*1000, 'f', 1, 64))
args = append(args, "-W", strconv.FormatFloat(p.Timeout*1000, 'f', -1, 64))
case "linux":
args = append(args, "-W", strconv.FormatFloat(p.Timeout, 'f', 1, 64))
args = append(args, "-W", strconv.FormatFloat(p.Timeout, 'f', -1, 64))
default:
// Not sure the best option here, just assume GNU ping?
args = append(args, "-W", strconv.FormatFloat(p.Timeout, 'f', 1, 64))
args = append(args, "-W", strconv.FormatFloat(p.Timeout, 'f', -1, 64))
}
}
if p.Deadline > 0 {
@@ -243,21 +243,24 @@ func processPingOutput(out string) (int, int, float64, float64, float64, float64
}
} else if strings.Contains(line, "min/avg/max") {
stats := strings.Split(line, " ")[3]
min, err = strconv.ParseFloat(strings.Split(stats, "/")[0], 64)
data := strings.Split(stats, "/")
min, err = strconv.ParseFloat(data[0], 64)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
avg, err = strconv.ParseFloat(strings.Split(stats, "/")[1], 64)
avg, err = strconv.ParseFloat(data[1], 64)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
max, err = strconv.ParseFloat(strings.Split(stats, "/")[2], 64)
max, err = strconv.ParseFloat(data[2], 64)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
stddev, err = strconv.ParseFloat(strings.Split(stats, "/")[3], 64)
if err != nil {
return trans, recv, min, avg, max, stddev, err
if len(data) == 4 {
stddev, err = strconv.ParseFloat(data[3], 64)
if err != nil {
return trans, recv, min, avg, max, stddev, err
}
}
}
}

View File

@@ -41,6 +41,19 @@ PING www.google.com (216.58.218.164) 56(84) bytes of data.
rtt min/avg/max/mdev = 35.225/43.628/51.806/5.325 ms
`
// BusyBox v1.24.1 (2017-02-28 03:28:13 CET) multi-call binary
var busyBoxPingOutput = `
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=56 time=22.559 ms
64 bytes from 8.8.8.8: seq=1 ttl=56 time=15.810 ms
64 bytes from 8.8.8.8: seq=2 ttl=56 time=16.262 ms
64 bytes from 8.8.8.8: seq=3 ttl=56 time=15.815 ms
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 15.810/17.611/22.559 ms
`
// Fatal ping output (invalid argument)
var fatalPingOutput = `
ping: -i interval too short: Operation not permitted
@@ -65,6 +78,15 @@ func TestProcessPingOutput(t *testing.T) {
assert.InDelta(t, 43.628, avg, 0.001)
assert.InDelta(t, 51.806, max, 0.001)
assert.InDelta(t, 5.325, stddev, 0.001)
trans, rec, min, avg, max, stddev, err = processPingOutput(busyBoxPingOutput)
assert.NoError(t, err)
assert.Equal(t, 4, trans, "4 packets were transmitted")
assert.Equal(t, 4, rec, "4 packets were transmitted")
assert.InDelta(t, 15.810, min, 0.001)
assert.InDelta(t, 17.611, avg, 0.001)
assert.InDelta(t, 22.559, max, 0.001)
assert.InDelta(t, -1.0, stddev, 0.001)
}
// Test that processPingOutput returns an error when 'ping' fails to run, such
@@ -106,7 +128,7 @@ func TestArgs(t *testing.T) {
"12000.0", "www.google.com"}
default:
expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-W",
"12.0", "www.google.com"}
"12", "www.google.com"}
}
p.Deadline = 24
@@ -117,7 +139,7 @@ func TestArgs(t *testing.T) {
"12000.0", "-t", "24", "www.google.com"}
default:
expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-W",
"12.0", "-w", "24", "www.google.com"}
"12", "-w", "24", "www.google.com"}
}
sort.Strings(actual)
@@ -133,7 +155,7 @@ func TestArgs(t *testing.T) {
"12000.0", "-t", "24", "-i", "1.2", "www.google.com"}
default:
expected = []string{"-c", "2", "-n", "-s", "16", "-I", "eth0", "-W",
"12.0", "-w", "24", "-i", "1.2", "www.google.com"}
"12", "-w", "24", "-i", "1.2", "www.google.com"}
}
sort.Strings(actual)
sort.Strings(expected)

View File

@@ -141,6 +141,9 @@ OID to get. May be a numeric or textual OID.
* `oid_index_suffix`:
The OID sub-identifier to strip off so that the index can be matched against other fields in the table.
* `oid_index_length`:
Specifies the length of the index after the supplied table OID (in OID path segments). Truncates the index after this point to remove non-fixed value or length index suffixes.
* `name`:
Output field/tag name.
If not specified, it defaults to the value of `oid`. If `oid` is numeric, an attempt to translate the numeric OID into a texual OID will be made.

View File

@@ -237,6 +237,8 @@ type Field struct {
Oid string
// OidIndexSuffix is the trailing sub-identifier on a table record OID that will be stripped off to get the record's index.
OidIndexSuffix string
// OidIndexLength specifies the length of the index in OID path segments. It can be used to remove sub-identifiers that vary in content or length.
OidIndexLength int
// IsTag controls whether this OID is output as a tag or a value.
IsTag bool
// Conversion controls any type conversion that is done on the value.
@@ -462,6 +464,18 @@ func (t Table) Build(gs snmpConnection, walk bool) (*RTable, error) {
}
idx = idx[:len(idx)-len(f.OidIndexSuffix)]
}
if f.OidIndexLength != 0 {
i := f.OidIndexLength + 1 // leading separator
idx = strings.Map(func(r rune) rune {
if r == '.' {
i -= 1
}
if i < 1 {
return -1
}
return r
}, idx)
}
fv, err := fieldConvert(f.Conversion, ent.Value)
if err != nil {

View File

@@ -453,6 +453,11 @@ func TestTableBuild_walk(t *testing.T) {
Oid: ".1.0.0.2.1.5",
OidIndexSuffix: ".9.9",
},
{
Name: "myfield5",
Oid: ".1.0.0.2.1.5",
OidIndexLength: 1,
},
},
}
@@ -469,6 +474,7 @@ func TestTableBuild_walk(t *testing.T) {
"myfield2": 1,
"myfield3": float64(0.123),
"myfield4": 11,
"myfield5": 11,
},
}
rtr2 := RTableRow{
@@ -480,6 +486,7 @@ func TestTableBuild_walk(t *testing.T) {
"myfield2": 2,
"myfield3": float64(0.456),
"myfield4": 22,
"myfield5": 22,
},
}
rtr3 := RTableRow{

View File

@@ -35,6 +35,13 @@ This is a sample configuration for the plugin.
## 0 (default) is unlimited.
# read_timeout = "30s"
## Optional TLS configuration.
## Only applies to stream sockets (e.g. TCP).
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Enables client authentication if set.
# tls_allowed_cacerts = ["/etc/telegraf/clientca.pem"]
## Maximum socket buffer size in bytes.
## For stream sockets, once the buffer fills up, the sender will start backing up.
## For datagram sockets, once the buffer fills up, metrics will start dropping.

View File

@@ -12,6 +12,8 @@ import (
"time"
"crypto/tls"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
@@ -122,9 +124,9 @@ func (ssl *streamSocketListener) read(c net.Conn) {
}
if err := scnr.Err(); err != nil {
if err, ok := err.(net.Error); ok && err.Timeout() {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
log.Printf("D! Timeout in plugin [input.socket_listener]: %s", err)
} else if !strings.HasSuffix(err.Error(), ": use of closed network connection") {
} else if netErr != nil && !strings.HasSuffix(err.Error(), ": use of closed network connection") {
ssl.AddError(err)
}
}
@@ -159,11 +161,14 @@ func (psl *packetSocketListener) listen() {
}
type SocketListener struct {
ServiceAddress string
MaxConnections int
ReadBufferSize int
ReadTimeout *internal.Duration
KeepAlivePeriod *internal.Duration
ServiceAddress string `toml:"service_address"`
MaxConnections int `toml:"max_connections"`
ReadBufferSize int `toml:"read_buffer_size"`
ReadTimeout *internal.Duration `toml:"read_timeout"`
TLSAllowedCACerts []string `toml:"tls_allowed_cacerts"`
TLSCert string `toml:"tls_cert"`
TLSKey string `toml:"tls_key"`
KeepAlivePeriod *internal.Duration `toml:"keep_alive_period"`
parsers.Parser
telegraf.Accumulator
@@ -198,6 +203,13 @@ func (sl *SocketListener) SampleConfig() string {
## 0 (default) is unlimited.
# read_timeout = "30s"
## Optional TLS configuration.
## Only applies to stream sockets (e.g. TCP).
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Enables client authentication if set.
# tls_allowed_cacerts = ["/etc/telegraf/clientca.pem"]
## Maximum socket buffer size in bytes.
## For stream sockets, once the buffer fills up, the sender will start backing up.
## For datagram sockets, once the buffer fills up, metrics will start dropping.
@@ -242,7 +254,21 @@ func (sl *SocketListener) Start(acc telegraf.Accumulator) error {
switch spl[0] {
case "tcp", "tcp4", "tcp6", "unix", "unixpacket":
l, err := net.Listen(spl[0], spl[1])
var (
err error
l net.Listener
)
tlsCfg, err := internal.GetServerTLSConfig(sl.TLSCert, sl.TLSKey, sl.TLSAllowedCACerts)
if err != nil {
return nil
}
if tlsCfg == nil {
l, err = net.Listen(spl[0], spl[1])
} else {
l, err = tls.Listen(spl[0], spl[1], tlsCfg)
}
if err != nil {
return err
}

View File

@@ -2,12 +2,14 @@ package socket_listener
import (
"bytes"
"crypto/tls"
"log"
"net"
"os"
"testing"
"time"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -25,6 +27,52 @@ func testEmptyLog(t *testing.T) func() {
}
}
func TestSocketListener_tcp_tls(t *testing.T) {
defer testEmptyLog(t)()
sl := newSocketListener()
sl.ServiceAddress = "tcp://127.0.0.1:0"
sl.TLSCert = "testdata/server.pem"
sl.TLSKey = "testdata/server.key"
sl.TLSAllowedCACerts = []string{"testdata/ca.pem"}
acc := &testutil.Accumulator{}
err := sl.Start(acc)
require.NoError(t, err)
defer sl.Stop()
tlsCfg, err := internal.GetTLSConfig("testdata/client.pem", "testdata/client.key", "testdata/ca.pem", true)
require.NoError(t, err)
secureClient, err := tls.Dial("tcp", sl.Closer.(net.Listener).Addr().String(), tlsCfg)
require.NoError(t, err)
testSocketListener(t, sl, secureClient)
}
func TestSocketListener_unix_tls(t *testing.T) {
defer testEmptyLog(t)()
sl := newSocketListener()
sl.ServiceAddress = "unix:///tmp/telegraf_test.sock"
sl.TLSCert = "testdata/server.pem"
sl.TLSKey = "testdata/server.key"
sl.TLSAllowedCACerts = []string{"testdata/ca.pem"}
acc := &testutil.Accumulator{}
err := sl.Start(acc)
require.NoError(t, err)
defer sl.Stop()
tlsCfg, err := internal.GetTLSConfig("testdata/client.pem", "testdata/client.key", "testdata/ca.pem", true)
require.NoError(t, err)
secureClient, err := tls.Dial("unix", "/tmp/telegraf_test.sock", tlsCfg)
require.NoError(t, err)
testSocketListener(t, sl, secureClient)
}
func TestSocketListener_tcp(t *testing.T) {
defer testEmptyLog(t)()

View File

@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFVTCCAz2gAwIBAgIJAOhLvwv6zUf+MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsG
A1UECgwEVGVzdDAeFw0xODA0MTcwNDIwNDZaFw0yMTAyMDQwNDIwNDZaMEExCzAJ
BgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEN
MAsGA1UECgwEVGVzdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKwE
Xy814CDH03G3Fg2/XSpYZXVMzwp6oq/bUe3iLhkOpA6C4+j07AxAAa22qEPlvYkb
W7oxVJiL0ih1od2FeAxvroBTmjG54j/Syb8OeQsZaJLNp1rRmwYGBIVi284ScaIc
dn+2bfmfpSLjK3SbU5XygtwIE3gh/B7x02UJRNJmJ1faRT2CfTeg/56xnTE4bcR5
HRrlojoN5laJngowLWAEAvWljCR8oge+ciNYB3xoK8Hgc9+WgTy95G1RBCNkaFFI
73nrcHl6dGOH9UgIqfbHJYxNEarI3o/JAr8DIBS0W4r8r4aY4JQ4LoN3bg4mLHQq
THKkVW5hyBeWe47qmlL0m4F6/+mzVi95NAWG2BQDCZJAWJNc+PbSRHi81838m7ff
O4rixd/F53LUUas8/zVca3vtv+XjOHZzIQLIy1bM4MhzpHlRcSmS9kqxxZ3S70e3
ZIWFdM0iRrtlBbJeoHIJRDpgPRYIWdRc6XotljTTi6/lN4Bj/0NK4E3iONcDsscN
kiqEHRAWZ4ptCqdVPgYR0S096Fx6OaC3ASODE0Cjb18ylZQRsQi8TiYSihGzuoio
wJwSLdIifDbbSUkjT1384cA/HsOjFQ9xHXYa6cQnAg3TUZyG1lAMJyFWYke+rxmG
srfL/EtIzgbzmEOC5anQjA2pdgUO9Pk2SinJaMApAgMBAAGjUDBOMB0GA1UdDgQW
BBQNJctDLjj8bVKNCYANaOcboPQnmzAfBgNVHSMEGDAWgBQNJctDLjj8bVKNCYAN
aOcboPQnmzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQATSr26Kc8g
3l2zuccoKWM57DQcgRmzSYwEOKA2jn3FWmrAdwozEIkLaTK0OXz0zh2dZxh9V3GR
w0WFCynbGNy/9s33MSi+zIWJOU/MZvt6zGE5CTcTgZ+u5IZyvSubMkPcwQi3Yvcg
AHmWzpF42kT2J5C5MfrSU65hrhPX7hT/CUoV3gN7oxFzj+/ED4kgNorO8SUUJCmq
DJNFbjgsD63EhnvAhn1AeM35GmKdl2enEKqcZsRkE4ZLpU7ibrThEm1aOQuJUtHk
gDAx49QMdQpWnxWxnfoiwpLu7ufR7ls8O9oA8ZJux/SVHEmtkOdRsuMtY5MElFZg
dANlQsdFWDko4ixaxFYzppuPNnRlqjGNnaEFJrNc2KR0Dxgmp28Yh2VyLd4r3fLT
nLVBYF8KzFchUdXYYPNBXwAf/N52jGfugDx8snLxOfzxoUZ4y64qMCpYhntGgBJ1
Rrk2trcn3Dw19gi8p3ylbdoz/Ch1INDDrO35pd0bZpcwASc/UNU72W5v2kGL0H7o
nJzgtrqeHcoIzNBmBhHlMlnTF5GMfrYGsf5d30KyKv7UL6qJTvT641dpKpB/FFrk
y3AQbKmKRDI+aVzeOlwdy/eJAwt7FikD4bR9GZ4PBX9n9jd4u/PHZNfxtgzplqo1
oy7kJv0cB/vRKOblmn/vPUfTFtAX7M3GkQ==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAmRuY+9Gg5V4e9hCd2mYek1jKeoaZijz89EPvox78XzoGdxPf
RoukUcTVS9VWN7HyJBjRA9P+KuHI9dX47skxyxH53uXZvRmGQAJBY4cE07JHvGkZ
eK1heXoWlBzYtivckha7bLBfn1ttAzcFCblUfJdzsn9XDuC4Jfn4oSaKn1o8Rzy1
KRvyLgvsYxMA/XzhyBzVMyoUOulye7EZx4f+AwSNmNHD4OgtxxPofrrMOtXZ2tC6
xNOexIZXbsB9dyrUW+4pWXYaadU7fl2V+arAJj+NVxV+3tmGGjmd1MiIypPx6BbP
g7xH20nJ/Y0U6V7gklZpYO1i84RbtR/kqBgi9QIDAQABAoIBAEONJJM+KyHnw/tG
246HbcgO7c7fYhDW1bgj3S/4NNsC6+VP1Dv40nftQzphFtgd37rDZDyvJL3gvlyQ
mnMoO5rgBIGuocHH6C6HkDgMUznft7zOFhnjTVVeY2XX0FmXwoqGEw1iR940ZUV8
2fEvXrJV1AsWGeALj9PZlTPsoE6rv5sUk9Lh3wCD73m7GSg7DzBRE+6bBze8Lmwn
ZzTvmimhgPJw8LR5rRpYbDbhAJLAfgA7/yPgYEPxA/ffry6Ba4epj8tVNUNOAcOf
PURF+uuIF7RceI2PkdvoNuQyVR5oxQUPUfidfVK5ClUmnHECSgb/FFnYC+nU2vSi
IAnmC6ECgYEAyrUFHyxxuIQAiinjBxa0OQ3ynvMxDnF/+zvWe8536Y61lz9dblKb
0xvFhpOEMfiG/zFdZdWJ+xdq7VQVNMHu4USoskG8sZs5zImMTu50kuDNln7xYqVf
SUuN1U7cp7JouI1qkZAOsytPfAgZN/83hLObd07lAvL44jKYaHVeMmkCgYEAwVxZ
wKXpboHwQawA+4ubsnZ36IlOk21/+FlGJiDg/LB643BS+QhgVNxuB2gL1gOCYkhl
6BBcIhWMvZOIIo5uwnv4fQ+WfFwntU9POFViZgbZvkitQtorB7MXc/NU2BDrNYx2
TBCiRn/9BaZ4fziW8I3Fx3xQ3rKDBXrexmrJQq0CgYEAvYGQYT12r47Qxlo0gcsL
AA/3E/y9jwgzItglQ6eZ2ULup5C4s0wNm8Zp2s+Mlf8HjgpDi9Gf5ptU/r1N+f2Y
awd6QvRMCSraVUr+Xkh1uV7rNNhGqPd75pT460OH7EtRtb+XsrAf3gcOjyEvGnfC
GpCjNl4OobwvS6ELdRTM1IkCgYAHUGX4uo3k5zdeVJJI8ZP3ITIR8retLfQsQbw8
jvvTsx1C4ynQT7fNHfVvhEkGVGWnMBPivlOt2mDTfvQkUnzwEF5q5J8NnzLFUfWu
LNSnBVVRNFCRec0s4mJduXOZJLKw+No0sGBjCE5a21wte8eB2+sCS7qHYftAxtAM
c1eflQKBgQDGTFsMvpM8BEPTreinTllFBdjeYchcdY/Ov9DZ3mMVopjAWRD81MKM
zM1RCqwLkgv9FvF79B1FLJ1Inr8e/XIGdcrhE1a4sZdIWdqTWQ4xFrlDgxCquq66
da09WVBRdvq2kVLAMaBViH2/GP1G4ZV9a8+JHuWKj+Arrr52Qeazjw==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEEjCCAfoCCQCmcronmMSqXTANBgkqhkiG9w0BAQsFADBBMQswCQYDVQQGEwJV
UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoM
BFRlc3QwHhcNMTgwNDE3MDQyNDMwWhcNNDUwOTAyMDQyNDMwWjBVMQswCQYDVQQG
EwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAJkbmPvRoOVeHvYQndpmHpNYynqGmYo8/PRD76Me/F86BncT
30aLpFHE1UvVVjex8iQY0QPT/irhyPXV+O7JMcsR+d7l2b0ZhkACQWOHBNOyR7xp
GXitYXl6FpQc2LYr3JIWu2ywX59bbQM3BQm5VHyXc7J/Vw7guCX5+KEmip9aPEc8
tSkb8i4L7GMTAP184cgc1TMqFDrpcnuxGceH/gMEjZjRw+DoLccT6H66zDrV2drQ
usTTnsSGV27AfXcq1FvuKVl2GmnVO35dlfmqwCY/jVcVft7Zhho5ndTIiMqT8egW
z4O8R9tJyf2NFOle4JJWaWDtYvOEW7Uf5KgYIvUCAwEAATANBgkqhkiG9w0BAQsF
AAOCAgEACJkccOvBavtagiMQc9OLsbo0PkHv7Qk9uTm5Sg9+LjLGUsu+3WLjAAmj
YScHyGbvQzXlwpgo8JuwY0lMNoPfwGuydlJPfOBCbaoAqFp6Vpc/E49J9YovCsqa
2HJUJeuxpf6SiH1Vc1SECjzwzKo03t8ul7t7SNVqA0r9fV4I936FlJOeQ4d5U+Wv
H7c2LmAqbHi2Mwf+m+W6ziOvzp+szspcP2gJDX7hsKEtIlqmHYm2bzZ4fsCuU9xN
3quewBVQUOuParO632yaLgzpGmfzzxLmCPO84lxarJKCxjHG2Q2l30TO/wA44m+r
Wd17HpCT3PkCDG5eSNCSnYqfLm8DE1hLGfHiXxKmrgU94q4wvwVGOlcYa+CQeP9Q
ZW3Tj0Axz0Mqlg1iLLo12+Z/yocSY2nFnFntBFT4qBKNCeD0xH3PxC0HJdK66xBv
MVDE/OE2hBtTTts+vC9yjx4W8thtMSA4VCOgtt5sHjt3ZekiYYh5VZK47Bx/a0uc
8CouRdyppWyPp/cNC+PcGW3YnXpAkxe/bSY/qgfK5kmbeOf+HzvZAIwAH/d9VK0g
AoLNp46eP6U2E2lVvtc/HJ1C/gsiC/1TSIq/kBbYtuIJjhhH3u6IVet7WSD22Akv
o5gOpcoKwy8IPDRC5lJEAAVYUKt7ORo2en3OVg6I4FaQmeBFp5s=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAzkEDLijGOqXNQPAqUjOz5TLuM28SENauknLtcfIyEN/N6PwZ
re5DjokxtDPp+c9yP/9qtn7+dBfdUXg2Mu7HQz8lAKniir2ZH+axkjp5LUE6vYJd
I1W8lOOc0kXDjozBetgriE0jkgc3v9oDBbLhN5waKR86jpQaNkfnI7/4U3yrlymK
yaT3uD6L1ldUJubdQ/xc1HxdmX8VewBnkK1urYyiRbju2iL9YmtSM72yWXvFsD1O
I4fP/XuiaymicBmXKL4cu6KYdfn1qeLAV3U35xG597M031WmR5o67rc63sqs+Q//
V3dbGqnFXRMkLhoOnuKK0DD28ujY1kctbNQWVQIDAQABAoIBAHFxFJy41H7BXulO
rxhTU6jGoHktqBQW4CGwkKTRf3QEhK6WqlEd8Y5eKzZgL1q1HLPSehEyPCYCUjpT
EgxlhLeZ7XI1/mIs8iG3swconimj7Pj60Nt0dqq1njWRJYQsKua0Kw1m0B+rVKBy
+qKRxondlA32HTD6iIg+eAUTuzO/KzimZcyL9hiT/g6aN9k0H5+qURi8dO7VV8fD
zvP8Y+oOGLwW2ccp+ZjFQizjTOkL4lgldr0hsGQXZJNHL94fA7jPdAxAUbnTicMJ
oXM++L3eCwIVabipGxxlqCMj9Dn8yfbQvRGzP2e76QDeROYZHX4osH6vLcZEjx9i
tJ4J+ekCgYEA82kKzkSKmFo4gZxnqAywlfZ2X2PADuMmHdqdiDFwt54orlMlKf/b
wVSvN/djLXwvFHuyzFmJeMFSHKFkYVTOsh8kPSETAIGkcJEMHD3viYn7DwjkQudY
vB/FpBWSiDT0T7qDUCzW3iMbx/JvTUSp7uO4ZuwOu6t6v3PEZwIChQ8CgYEA2Ov9
FXHmm7sS54HgvZd6Wk8zLMLIDnyMmECjtYOasJ9c40yQHpRlXsb+Dzn/2xhMMwth
Bln2hIiJ/e+G0bzFu4x0cItRPOQeRNyz5Pal8EsATeUwcX4KRKOZaUpDkV6XV1L0
r/HSk/wed+90B74sGoJY1qsFflOATIUVs7SIllsCgYEAwhGSB/sl9WqZet1U1+um
LyqeHlfNnREGJu9Sgm/Iyt1S2gp4qw/QCkiWmyym6nEEqHQnjj4lGR4pdaJIAkI3
ulSR9BsWp2S10voSicHn5eUZQld4hs8lNHiwf66jce2mjJrMb3QQrHOZhsWIcDa6
tjjhoU28QWzrJRIMGYTEtYkCgYA17NSJlDsj06mra5oXB6Ue9jlekz1wfH3nC4qn
AQRfi/5ncw0QzQs2OHnIBz8XlD69IcMI9SxXXioPuo/la+wr54q6v6d+X6c2rzb5
YGd4CO0WcDdOv2qGDbWBezi41q8AwlqZsqAKsc5ROnG5ywjjviufkfxXnyJx41O1
zNd3qQKBgGEy+EwUXD5iGeQxdCDnd6iVu14SoBscHO5SpIeDu3DIhnu+7gPq2VMg
Vp9j/iNVtEA3HyYCOeXc2rz9Di1wwt3YijED4birLAkC5YW6YB9rmLMfCNc1EyLh
BKAkUQN3D+XCN4pXdbKvbkOcfYRUHoD+pPBjRYH020OtPBUc6Wkl
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEJjCCAg4CCQCmcronmMSqXDANBgkqhkiG9w0BAQsFADBBMQswCQYDVQQGEwJV
UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoM
BFRlc3QwHhcNMTgwNDE3MDQyNDAwWhcNNDUwOTAyMDQyNDAwWjBpMQswCQYDVQQG
EwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJMTI3LjAuMC4x
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzkEDLijGOqXNQPAqUjOz
5TLuM28SENauknLtcfIyEN/N6PwZre5DjokxtDPp+c9yP/9qtn7+dBfdUXg2Mu7H
Qz8lAKniir2ZH+axkjp5LUE6vYJdI1W8lOOc0kXDjozBetgriE0jkgc3v9oDBbLh
N5waKR86jpQaNkfnI7/4U3yrlymKyaT3uD6L1ldUJubdQ/xc1HxdmX8VewBnkK1u
rYyiRbju2iL9YmtSM72yWXvFsD1OI4fP/XuiaymicBmXKL4cu6KYdfn1qeLAV3U3
5xG597M031WmR5o67rc63sqs+Q//V3dbGqnFXRMkLhoOnuKK0DD28ujY1kctbNQW
VQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCVgzqFrehoRAMFLMEL8avfokYtsSYc
50Yug4Es0ISo/PRWGeUnv8k1inyE3Y1iR/gbN5n/yjLXJKEflan6BuqGuukfr2eA
fRdDCyPvzQLABdxCx2n6ByQFxj92z82tizf35R2OMuHHWzTckta+7s5EvxwIiUsd
rUuXp+0ltJzlYYW9xTGFiJO9hAbRgMgZiwL8F7ayic8GmLQ1eRK/DfKDCOH3afeX
MNN5FulgjqNyhXHF33vwgIJynGDg2JEhkWjB1DkUAxll0+SMQoYyVGZVrQSGbGw1
JhOLc8C8bTzfK3qcJDuyldvjiut+To+lpu76R0u0+sn+wxQFL1uCWuAbMJgGsJgM
ARavu2XDeae9X+e8MgJuN1FYS3tihBplPjMJD3UYRybRvHAvQh26BZ7Ch3JNSNST
AL2l5T7JKU+XaWWeo+crV+AnGIJyqyh9Su/n97PEoZoEMGH4Kcl/n/w2Jms60+5s
K0FK2OGNL42ddUfQiVL9CwYQQo70hydjsIo1x8S6+tSFLMAAysQEToSjfAA6qxDu
fgGVMuIYHo0rSkpTVsHVwru08Z5o4m+XDAK0iHalZ4knKsO0lJ+9l7vFnQHlzwt7
JTjDhnyOKWPIANeWf3PrHPWE7kKpFVBqFBzOvWLJuxDu5NlgLo1PFahsahTqB9bz
qwUyMg/oYWnwqw==
-----END CERTIFICATE-----

View File

@@ -397,9 +397,7 @@ CROSS APPLY (
OPTION( RECOMPILE );
`
const sqlPerformanceCountersV2 string = `DECLARE @DynamicPerf NVARCHAR(MAX) = ''
SET @DynamicPerf += REPLACE('
const sqlPerformanceCountersV2 string = `
DECLARE @PCounters TABLE
(
object_name nvarchar(128),
@@ -409,140 +407,120 @@ DECLARE @PCounters TABLE
cntr_type INT,
Primary Key(object_name, counter_name, instance_name)
);
INSERT INTO @PCounters
SELECT DISTINCT
RTrim(spi.object_name) object_name,
RTrim(spi.counter_name) counter_name,
RTrim(spi.instance_name) instance_name,
spi.cntr_value,
CAST(spi.cntr_value AS BIGINT) AS cntr_value,
spi.cntr_type
FROM sys.dm_os_performance_counters AS spi
WHERE (
counter_name IN (
"SQL Compilations/sec",
"SQL Re-Compilations/sec",
"User Connections",
"Batch Requests/sec",
"Logouts/sec",
"Logins/sec",
"Processes blocked",
"Latch Waits/sec",
"Full Scans/sec",
"Index Searches/sec",
"Page Splits/sec",
"Page Lookups/sec",
"Page Reads/sec",
"Page Writes/sec",
"Readahead Pages/sec",
"Lazy Writes/sec",
"Checkpoint Pages/sec",
"Page life expectancy",
"Log File(s) Size (KB)",
"Log File(s) Used Size (KB)",
"Data File(s) Size (KB)",
"Transactions/sec",
"Write Transactions/sec",
"Active Temp Tables",
"Temp Tables Creation Rate",
"Temp Tables For Destruction",
"Free Space in tempdb (KB)",
"Version Store Size (KB)",
"Memory Grants Pending",
"Free list stalls/sec",
"Buffer cache hit ratio",
"Buffer cache hit ratio base",
"Backup/Restore Throughput/sec",
"Total Server Memory (KB)",
"Target Server Memory (KB)"
'SQL Compilations/sec',
'SQL Re-Compilations/sec',
'User Connections',
'Batch Requests/sec',
'Logouts/sec',
'Logins/sec',
'Processes blocked',
'Latch Waits/sec',
'Full Scans/sec',
'Index Searches/sec',
'Page Splits/sec',
'Page Lookups/sec',
'Page Reads/sec',
'Page Writes/sec',
'Readahead Pages/sec',
'Lazy Writes/sec',
'Checkpoint Pages/sec',
'Page life expectancy',
'Log File(s) Size (KB)',
'Log File(s) Used Size (KB)',
'Data File(s) Size (KB)',
'Transactions/sec',
'Write Transactions/sec',
'Active Temp Tables',
'Temp Tables Creation Rate',
'Temp Tables For Destruction',
'Free Space in tempdb (KB)',
'Version Store Size (KB)',
'Memory Grants Pending',
'Free list stalls/sec',
'Buffer cache hit ratio',
'Buffer cache hit ratio base',
'Backup/Restore Throughput/sec',
'Total Server Memory (KB)',
'Target Server Memory (KB)'
)
) OR (
instance_name IN ("_Total","Column store object pool")
instance_name IN ('_Total','Column store object pool')
AND counter_name IN (
"Log Flushes/sec",
"Log Flush Wait Time",
"Lock Timeouts/sec",
"Number of Deadlocks/sec",
"Lock Waits/sec",
"Latch Waits/sec",
"Memory broker clerk size",
"Log Bytes Flushed/sec",
"Bytes Sent to Replica/sec",
"Log Send Queue",
"Bytes Sent to Transport/sec",
"Sends to Replica/sec",
"Bytes Sent to Transport/sec",
"Sends to Transport/sec",
"Bytes Received from Replica/sec",
"Receives from Replica/sec",
"Flow Control Time (ms/sec)",
"Flow Control/sec",
"Resent Messages/sec",
"Redone Bytes/sec",
"XTP Memory Used (KB)"
'Log Flushes/sec',
'Log Flush Wait Time',
'Lock Timeouts/sec',
'Number of Deadlocks/sec',
'Lock Waits/sec',
'Latch Waits/sec',
'Memory broker clerk size',
'Log Bytes Flushed/sec',
'Bytes Sent to Replica/sec',
'Log Send Queue',
'Bytes Sent to Transport/sec',
'Sends to Replica/sec',
'Bytes Sent to Transport/sec',
'Sends to Transport/sec',
'Bytes Received from Replica/sec',
'Receives from Replica/sec',
'Flow Control Time (ms/sec)',
'Flow Control/sec',
'Resent Messages/sec',
'Redone Bytes/sec',
'XTP Memory Used (KB)'
) OR (
counter_name IN (
"Log Bytes Received/sec",
"Log Apply Pending Queue",
"Redone Bytes/sec",
"Recovery Queue",
"Log Apply Ready Queue"
'Log Bytes Received/sec',
'Log Apply Pending Queue',
'Redone Bytes/sec',
'Recovery Queue',
'Log Apply Ready Queue'
)
AND instance_name = "_Total"
AND instance_name = '_Total'
)
) OR (
counter_name IN ("Transaction Delay")
counter_name IN ('Transaction Delay')
) OR (
counter_name IN (
"CPU usage %",
"CPU usage % base",
"Queued requests",
"Requests completed/sec",
"Blocked tasks"
'CPU usage %',
'CPU usage % base',
'Queued requests',
'Requests completed/sec',
'Blocked tasks'
)
) OR (
counter_name IN (
"Active memory grant amount (KB)",
"Disk Read Bytes/sec",
"Disk Read IO Throttled/sec",
"Disk Read IO/sec",
"Disk Write Bytes/sec",
"Disk Write IO Throttled/sec",
"Disk Write IO/sec",
"Used memory (KB)"
'Active memory grant amount (KB)',
'Disk Read Bytes/sec',
'Disk Read IO Throttled/sec',
'Disk Read IO/sec',
'Disk Write Bytes/sec',
'Disk Write IO Throttled/sec',
'Disk Write IO/sec',
'Used memory (KB)'
)
) OR (
object_name LIKE "%User Settable%"
OR object_name LIKE "%SQL Errors%"
object_name LIKE '%User Settable%'
OR object_name LIKE '%SQL Errors%'
)
'
,'"','''')
SET @DynamicPerf += REPLACE('
SELECT "sqlserver_performance" AS [measurement],
REPLACE(@@SERVERNAME,"\",":") AS [sql_instance],
pc.object_name AS [object],
pc.counter_name AS [counter],
CASE pc.instance_name WHEN "_Total" THEN "Total" ELSE ISNULL(pc.instance_name,"") END AS [instance],
CASE WHEN pc.cntr_type = 537003264 AND pc1.cntr_value > 0 THEN (pc.cntr_value * 1.0) / (pc1.cntr_value * 1.0) * 100 ELSE pc.cntr_value END AS [value]
FROM @PCounters AS pc
LEFT OUTER JOIN @PCounters AS pc1
ON (
pc.counter_name = REPLACE(pc1.counter_name," base","")
OR pc.counter_name = REPLACE(pc1.counter_name," base"," (ms)")
)
AND pc.object_name = pc1.object_name
AND pc.instance_name = pc1.instance_name
AND pc1.counter_name LIKE "%base"
WHERE pc.counter_name NOT LIKE "% base"
UNION ALL
DECLARE @SQL NVARCHAR(MAX)
SET @SQL = REPLACE('
SELECT
"sqlserver_performance" As [measurement],
REPLACE(@@SERVERNAME,"\",":") AS [sql_instance],
"SQLServer:Workload Group Stats" AS object,
counter,
instance,
vs.value
CAST(vs.value AS BIGINT) AS value,
1
FROM
(
SELECT
@@ -561,11 +539,29 @@ FROM
) AS rg
UNPIVOT (
value FOR counter IN ( [Request Count], [Queued Request Count], [CPU Limit Violation Count], [CPU Usage (time)], ' + CASE WHEN SERVERPROPERTY('ProductMajorVersion') > 10 THEN '[Premptive CPU Usage (time)], ' ELSE '' END + '[Lock Wait Count], [Lock Wait Time], [Reduced Memory Grant Count] )
) AS vs
OPTION(RECOMPILE);'
) AS vs'
,'"','''')
EXEC(@DynamicPerf)
INSERT INTO @PCounters
EXEC( @SQL )
SELECT 'sqlserver_performance' AS [measurement],
REPLACE(@@SERVERNAME,'\',':') AS [sql_instance],
pc.object_name AS [object],
pc.counter_name AS [counter],
CASE pc.instance_name WHEN '_Total' THEN 'Total' ELSE ISNULL(pc.instance_name,'') END AS [instance],
CAST(CASE WHEN pc.cntr_type = 537003264 AND pc1.cntr_value > 0 THEN (pc.cntr_value * 1.0) / (pc1.cntr_value * 1.0) * 100 ELSE pc.cntr_value END AS float(10)) AS [value]
FROM @PCounters AS pc
LEFT OUTER JOIN @PCounters AS pc1
ON (
pc.counter_name = REPLACE(pc1.counter_name,' base','')
OR pc.counter_name = REPLACE(pc1.counter_name,' base',' (ms)')
)
AND pc.object_name = pc1.object_name
AND pc.instance_name = pc1.instance_name
AND pc1.counter_name LIKE '%base'
WHERE pc.counter_name NOT LIKE '% base'
OPTION(RECOMPILE);
`
const sqlWaitStatsCategorizedV2 string = `SELECT

View File

@@ -59,7 +59,7 @@ func TestConcurrentConns(t *testing.T) {
require.NoError(t, listener.Start(acc))
defer listener.Stop()
time.Sleep(time.Millisecond * 25)
time.Sleep(time.Millisecond * 250)
_, err := net.Dial("tcp", "127.0.0.1:8125")
assert.NoError(t, err)
_, err = net.Dial("tcp", "127.0.0.1:8125")
@@ -72,7 +72,7 @@ func TestConcurrentConns(t *testing.T) {
assert.NoError(t, err)
_, err = conn.Write([]byte(testMsg))
assert.NoError(t, err)
time.Sleep(time.Millisecond * 10)
time.Sleep(time.Millisecond * 100)
assert.Zero(t, acc.NFields())
}
@@ -89,7 +89,7 @@ func TestConcurrentConns1(t *testing.T) {
require.NoError(t, listener.Start(acc))
defer listener.Stop()
time.Sleep(time.Millisecond * 25)
time.Sleep(time.Millisecond * 250)
_, err := net.Dial("tcp", "127.0.0.1:8125")
assert.NoError(t, err)
@@ -100,7 +100,7 @@ func TestConcurrentConns1(t *testing.T) {
assert.NoError(t, err)
_, err = conn.Write([]byte(testMsg))
assert.NoError(t, err)
time.Sleep(time.Millisecond * 10)
time.Sleep(time.Millisecond * 100)
assert.Zero(t, acc.NFields())
}
@@ -116,7 +116,7 @@ func TestCloseConcurrentConns(t *testing.T) {
acc := &testutil.Accumulator{}
require.NoError(t, listener.Start(acc))
time.Sleep(time.Millisecond * 25)
time.Sleep(time.Millisecond * 250)
_, err := net.Dial("tcp", "127.0.0.1:8125")
assert.NoError(t, err)
_, err = net.Dial("tcp", "127.0.0.1:8125")
@@ -141,7 +141,7 @@ func BenchmarkUDP(b *testing.B) {
panic(err)
}
time.Sleep(time.Millisecond * 25)
time.Sleep(time.Millisecond * 250)
conn, err := net.Dial("udp", "127.0.0.1:8125")
if err != nil {
panic(err)
@@ -172,7 +172,7 @@ func BenchmarkTCP(b *testing.B) {
panic(err)
}
time.Sleep(time.Millisecond * 25)
time.Sleep(time.Millisecond * 250)
conn, err := net.Dial("tcp", "127.0.0.1:8125")
if err != nil {
panic(err)

View File

@@ -33,6 +33,9 @@ type Sysstat struct {
// Sadc represents the path to the sadc collector utility.
Sadc string `toml:"sadc_path"`
// Force the execution time of sadc
SadcInterval internal.Duration `toml:"sadc_interval"`
// Sadf represents the path to the sadf cmd.
Sadf string `toml:"sadf_path"`
@@ -136,6 +139,11 @@ func (*Sysstat) SampleConfig() string {
}
func (s *Sysstat) Gather(acc telegraf.Accumulator) error {
if s.SadcInterval.Duration != 0 {
// Collect interval is calculated as interval - parseInterval
s.interval = int(s.SadcInterval.Duration.Seconds()) + parseInterval
}
if s.interval == 0 {
if firstTimestamp.IsZero() {
firstTimestamp = time.Now()

View File

@@ -1,8 +1,7 @@
# System Input Plugin
The system plugin gathers general stats on system load, uptime,
and number of users logged in. It is basically equivalent
to the unix `uptime` command.
and number of users logged in. It is similar to the unix `uptime` command.
### Configuration:
@@ -11,29 +10,27 @@ to the unix `uptime` command.
[[inputs.system]]
# no configuration
```
#### Permissions:
### Measurements & Fields:
The `n_users` field requires read access to `/var/run/utmp`, and may require
the `telegraf` user to be added to the `utmp` group on some systems.
### Metrics:
- system
- load1 (float)
- load15 (float)
- load5 (float)
- n_users (integer)
- n_cpus (integer)
- uptime (integer, seconds)
- uptime_format (string)
### Tags:
None
- fields:
- load1 (float)
- load15 (float)
- load5 (float)
- n_users (integer)
- n_cpus (integer)
- uptime (integer, seconds)
- uptime_format (string)
### Example Output:
```
$ telegraf --config ~/ws/telegraf.conf --input-filter system --test
* Plugin: system, Collection 1
* Plugin: inputs.system, Collection 1
> system,host=tyrion load1=3.72,load5=2.4,load15=2.1,n_users=3i,n_cpus=4i 1483964144000000000
> system,host=tyrion uptime=1249632i 1483964144000000000
> system,host=tyrion uptime_format="14 days, 11:07" 1483964144000000000
system,host=tyrion load1=3.72,load5=2.4,load15=2.1,n_users=3i,n_cpus=4i 1483964144000000000
system,host=tyrion uptime=1249632i 1483964144000000000
system,host=tyrion uptime_format="14 days, 11:07" 1483964144000000000
```

View File

@@ -37,7 +37,7 @@ func (s *MemStats) Gather(acc telegraf.Accumulator) error {
"used_percent": 100 * float64(vm.Used) / float64(vm.Total),
"available_percent": 100 * float64(vm.Available) / float64(vm.Total),
}
acc.AddCounter("mem", fields, nil)
acc.AddGauge("mem", fields, nil)
return nil
}

View File

@@ -4,8 +4,10 @@ import (
"bufio"
"bytes"
"fmt"
"os"
"runtime"
"strings"
"time"
"github.com/shirou/gopsutil/host"
"github.com/shirou/gopsutil/load"
@@ -28,29 +30,34 @@ func (_ *SystemStats) Gather(acc telegraf.Accumulator) error {
return err
}
fields := map[string]interface{}{
"load1": loadavg.Load1,
"load5": loadavg.Load5,
"load15": loadavg.Load15,
"n_cpus": runtime.NumCPU(),
}
users, err := host.Users()
if err == nil {
fields["n_users"] = len(users)
} else if !os.IsPermission(err) {
return err
}
now := time.Now()
acc.AddGauge("system", fields, nil, now)
hostinfo, err := host.Info()
if err != nil {
return err
}
users, err := host.Users()
if err != nil {
return err
}
acc.AddGauge("system", map[string]interface{}{
"load1": loadavg.Load1,
"load5": loadavg.Load5,
"load15": loadavg.Load15,
"n_users": len(users),
"n_cpus": runtime.NumCPU(),
}, nil)
acc.AddCounter("system", map[string]interface{}{
"uptime": hostinfo.Uptime,
}, nil)
}, nil, now)
acc.AddFields("system", map[string]interface{}{
"uptime_format": format_uptime(hostinfo.Uptime),
}, nil)
}, nil, now)
return nil
}

View File

@@ -87,7 +87,7 @@ func unboundRunner(cmdName string, Timeout internal.Duration, UseSudo bool, Serv
server = server + "@" + port
}
cmdArgs = append(cmdArgs, "-s", server)
cmdArgs = append([]string{"-s", server}, cmdArgs...)
}
cmd := exec.Command(cmdName, cmdArgs...)
@@ -101,7 +101,7 @@ func unboundRunner(cmdName string, Timeout internal.Duration, UseSudo bool, Serv
cmd.Stdout = &out
err := internal.RunTimeout(cmd, Timeout.Duration)
if err != nil {
return &out, fmt.Errorf("error running unbound-control: %s", err)
return &out, fmt.Errorf("error running unbound-control: %s (%s %v)", err, cmdName, cmdArgs)
}
return &out, nil

View File

@@ -192,6 +192,14 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error {
var size uint32 = uint32(unsafe.Sizeof(PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
var emptyBuf [1]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
type InstanceGrouping struct {
name string
instance string
objectname string
}
var collectFields = make(map[InstanceGrouping]map[string]interface{})
// For iterate over the known metrics and get the samples.
for _, metric := range m.itemCache {
// collect
@@ -231,20 +239,22 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error {
}
if add {
fields := make(map[string]interface{})
tags := make(map[string]string)
if s != "" {
tags["instance"] = s
}
tags["objectname"] = metric.objectName
fields[sanitizedChars.Replace(metric.counter)] =
float32(c.FmtValue.DoubleValue)
measurement := sanitizedChars.Replace(metric.measurement)
if measurement == "" {
measurement = "win_perf_counters"
}
acc.AddFields(measurement, fields, tags)
var instance = InstanceGrouping{measurement, s, metric.objectName}
if collectFields[instance] == nil {
collectFields[instance] = make(map[string]interface{})
}
collectFields[instance][sanitizedChars.Replace(metric.counter)] = float32(c.FmtValue.DoubleValue)
}
}
@@ -257,6 +267,14 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error {
}
}
for instance, fields := range collectFields {
var tags = map[string]string{
"instance": instance.instance,
"objectname": instance.objectname,
}
acc.AddFields(instance.name, fields, tags)
}
return nil
}

View File

@@ -29,6 +29,11 @@ const (
DefaultShutdownTimeout = 5
)
var (
// DefaultNetwork is the network to listen on; use only in tests.
DefaultNetwork = "tcp"
)
// Recorder represents a type which can record zipkin trace data as well as
// any accompanying errors, and process that data.
type Recorder interface {
@@ -94,7 +99,7 @@ func (z *Zipkin) Start(acc telegraf.Accumulator) error {
}
addr := ":" + strconv.Itoa(z.Port)
ln, err := net.Listen("tcp", addr)
ln, err := net.Listen(DefaultNetwork, addr)
if err != nil {
return err
}

View File

@@ -557,6 +557,10 @@ func TestZipkinPlugin(t *testing.T) {
},
}
// Workaround for Go 1.8
// https://github.com/golang/go/issues/18806
DefaultNetwork = "tcp4"
z := &Zipkin{
Path: "/api/v1/spans",
Port: 0,

View File

@@ -13,7 +13,7 @@ import (
type File struct {
Files []string
writer io.Writer
writers []io.Writer
closers []io.Closer
serializer serializers.Serializer
@@ -35,15 +35,13 @@ func (f *File) SetSerializer(serializer serializers.Serializer) {
}
func (f *File) Connect() error {
writers := []io.Writer{}
if len(f.Files) == 0 {
f.Files = []string{"stdout"}
}
for _, file := range f.Files {
if file == "stdout" {
writers = append(writers, os.Stdout)
f.writers = append(f.writers, os.Stdout)
} else {
var of *os.File
var err error
@@ -56,11 +54,10 @@ func (f *File) Connect() error {
if err != nil {
return err
}
writers = append(writers, of)
f.writers = append(f.writers, of)
f.closers = append(f.closers, of)
}
}
f.writer = io.MultiWriter(writers...)
return nil
}
@@ -90,17 +87,21 @@ func (f *File) Write(metrics []telegraf.Metric) error {
return nil
}
var writeErr error = nil
for _, metric := range metrics {
b, err := f.serializer.Serialize(metric)
if err != nil {
return fmt.Errorf("failed to serialize message: %s", err)
}
_, err = f.writer.Write(b)
if err != nil {
return fmt.Errorf("failed to write message: %s, %s", b, err)
for _, writer := range f.writers {
_, err = writer.Write(b)
if err != nil && writer != os.Stdout {
writeErr = fmt.Errorf("E! failed to write message: %s, %s", b, err)
}
}
}
return nil
return writeErr
}
func init() {

View File

@@ -11,6 +11,7 @@ This InfluxDB output plugin writes metrics to the [InfluxDB](https://github.com/
##
## Multiple URLs can be specified for a single cluster, only ONE of the
## urls will be written to each interval.
# urls = ["unix:///var/run/influxdb.sock"]
# urls = ["udp://127.0.0.1:8089"]
# urls = ["http://127.0.0.1:8086"]
@@ -23,10 +24,11 @@ This InfluxDB output plugin writes metrics to the [InfluxDB](https://github.com/
# skip_database_creation = false
## Name of existing retention policy to write to. Empty string writes to
## the default retention policy.
## the default retention policy. Only takes effect when using HTTP.
# retention_policy = ""
## Write consistency (clusters only), can be: "any", "one", "quorum", "all"
## Write consistency (clusters only), can be: "any", "one", "quorum", "all".
## Only takes effect when using HTTP.
# write_consistency = "any"
## Timeout for HTTP messages.
@@ -42,7 +44,7 @@ This InfluxDB output plugin writes metrics to the [InfluxDB](https://github.com/
## UDP payload size is the maximum packet size to send.
# udp_payload = 512
## Optional SSL Config
## Optional SSL Config for use on HTTP connections.
# ssl_ca = "/etc/telegraf/ca.pem"
# ssl_cert = "/etc/telegraf/cert.pem"
# ssl_key = "/etc/telegraf/key.pem"

View File

@@ -159,12 +159,18 @@ func NewHTTPClient(config *HTTPConfig) (*httpClient, error) {
serializer = influx.NewSerializer()
}
writeURL := makeWriteURL(
writeURL, err := makeWriteURL(
config.URL,
database,
config.RetentionPolicy,
config.Consistency)
queryURL := makeQueryURL(config.URL)
if err != nil {
return nil, err
}
queryURL, err := makeQueryURL(config.URL)
if err != nil {
return nil, err
}
var transport *http.Transport
switch config.URL.Scheme {
@@ -399,7 +405,7 @@ func (c *httpClient) addHeaders(req *http.Request) {
}
}
func makeWriteURL(loc *url.URL, db, rp, consistency string) string {
func makeWriteURL(loc *url.URL, db, rp, consistency string) (string, error) {
params := url.Values{}
params.Set("db", db)
@@ -417,22 +423,26 @@ func makeWriteURL(loc *url.URL, db, rp, consistency string) string {
u.Scheme = "http"
u.Host = "127.0.0.1"
u.Path = "/write"
case "http":
case "http", "https":
u.Path = path.Join(u.Path, "write")
default:
return "", fmt.Errorf("unsupported scheme: %q", loc.Scheme)
}
u.RawQuery = params.Encode()
return u.String()
return u.String(), nil
}
func makeQueryURL(loc *url.URL) string {
func makeQueryURL(loc *url.URL) (string, error) {
u := *loc
switch u.Scheme {
case "unix":
u.Scheme = "http"
u.Host = "127.0.0.1"
u.Path = "/query"
case "http":
case "http", "https":
u.Path = path.Join(u.Path, "query")
default:
return "", fmt.Errorf("unsupported scheme: %q", loc.Scheme)
}
return u.String()
return u.String(), nil
}

View File

@@ -46,6 +46,17 @@ func TestHTTP_MinimalConfig(t *testing.T) {
require.NoError(t, err)
}
func TestHTTP_UnsupportedScheme(t *testing.T) {
config := &influxdb.HTTPConfig{
URL: &url.URL{
Scheme: "foo",
Host: "localhost",
},
}
_, err := influxdb.NewHTTPClient(config)
require.Error(t, err)
}
func TestHTTP_CreateDatabase(t *testing.T) {
ts := httptest.NewServer(http.NotFoundHandler())
defer ts.Close()
@@ -576,9 +587,6 @@ func TestHTTP_UnixSocket(t *testing.T) {
ts.Start()
defer ts.Close()
x, _ := url.Parse("unix://" + sock)
fmt.Println(x)
successResponse := []byte(`{"results": [{"statement_id": 0}]}`)
tests := []struct {

View File

@@ -84,10 +84,11 @@ var sampleConfig = `
# skip_database_creation = false
## Name of existing retention policy to write to. Empty string writes to
## the default retention policy.
## the default retention policy. Only takes effect when using HTTP.
# retention_policy = ""
## Write consistency (clusters only), can be: "any", "one", "quorum", "all"
## Write consistency (clusters only), can be: "any", "one", "quorum", "all".
## Only takes effect when using HTTP.
# write_consistency = "any"
## Timeout for HTTP messages.
@@ -103,7 +104,7 @@ var sampleConfig = `
## UDP payload size is the maximum packet size to send.
# udp_payload = 512
## Optional SSL Config
## Optional SSL Config for use on HTTP connections.
# ssl_ca = "/etc/telegraf/ca.pem"
# ssl_cert = "/etc/telegraf/cert.pem"
# ssl_key = "/etc/telegraf/key.pem"
@@ -210,7 +211,7 @@ func (i *InfluxDB) Write(metrics []telegraf.Metric) error {
}
switch apiError := err.(type) {
case APIError:
case *APIError:
if !i.SkipDatabaseCreation {
if apiError.Type == DatabaseNotFound {
err := client.CreateDatabase(ctx)

View File

@@ -2,11 +2,13 @@ package influxdb_test
import (
"context"
"net/http"
"testing"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/metric"
"github.com/influxdata/telegraf/plugins/outputs/influxdb"
"github.com/stretchr/testify/require"
)
@@ -37,7 +39,7 @@ func (c *MockClient) CreateDatabase(ctx context.Context) error {
func TestDeprecatedURLSupport(t *testing.T) {
var actual *influxdb.UDPConfig
output := influxdb.InfluxDB{
URL: "udp://localhost:8086",
URL: "udp://localhost:8089",
CreateUDPClientF: func(config *influxdb.UDPConfig) (influxdb.Client, error) {
actual = config
@@ -46,7 +48,7 @@ func TestDeprecatedURLSupport(t *testing.T) {
}
err := output.Connect()
require.NoError(t, err)
require.Equal(t, "udp://localhost:8086", actual.URL.String())
require.Equal(t, "udp://localhost:8089", actual.URL.String())
}
func TestDefaultURL(t *testing.T) {
@@ -70,7 +72,7 @@ func TestConnectUDPConfig(t *testing.T) {
var actual *influxdb.UDPConfig
output := influxdb.InfluxDB{
URLs: []string{"udp://localhost:8086"},
URLs: []string{"udp://localhost:8089"},
UDPPayload: 42,
CreateUDPClientF: func(config *influxdb.UDPConfig) (influxdb.Client, error) {
@@ -81,7 +83,7 @@ func TestConnectUDPConfig(t *testing.T) {
err := output.Connect()
require.NoError(t, err)
require.Equal(t, "udp://localhost:8086", actual.URL.String())
require.Equal(t, "udp://localhost:8089", actual.URL.String())
require.Equal(t, 42, actual.MaxPayloadSize)
require.NotNil(t, actual.Serializer)
}
@@ -90,7 +92,7 @@ func TestConnectHTTPConfig(t *testing.T) {
var actual *influxdb.HTTPConfig
output := influxdb.InfluxDB{
URLs: []string{"http://localhost:8089"},
URLs: []string{"http://localhost:8086"},
Database: "telegraf",
RetentionPolicy: "default",
WriteConsistency: "any",
@@ -98,7 +100,7 @@ func TestConnectHTTPConfig(t *testing.T) {
Username: "guy",
Password: "smiley",
UserAgent: "telegraf",
HTTPProxy: "http://localhost:8089",
HTTPProxy: "http://localhost:8086",
HTTPHeaders: map[string]string{
"x": "y",
},
@@ -133,3 +135,47 @@ func TestConnectHTTPConfig(t *testing.T) {
require.Equal(t, output.Database, actual.Database)
}
func TestWriteRecreateDatabaseIfDatabaseNotFound(t *testing.T) {
output := influxdb.InfluxDB{
URLs: []string{"http://localhost:8086"},
CreateHTTPClientF: func(config *influxdb.HTTPConfig) (influxdb.Client, error) {
return &MockClient{
CreateDatabaseF: func(ctx context.Context) error {
return nil
},
WriteF: func(ctx context.Context, metrics []telegraf.Metric) error {
return &influxdb.APIError{
StatusCode: http.StatusNotFound,
Title: "404 Not Found",
Description: `database not found "telegraf"`,
Type: influxdb.DatabaseNotFound,
}
},
URLF: func() string {
return "http://localhost:8086"
},
}, nil
},
}
err := output.Connect()
require.NoError(t, err)
m, err := metric.New(
"cpu",
map[string]string{},
map[string]interface{}{
"value": 42.0,
},
time.Unix(0, 0),
)
require.NoError(t, err)
metrics := []telegraf.Metric{m}
err = output.Write(metrics)
// We only have one URL, so we expect an error
require.Error(t, err)
}

View File

@@ -197,7 +197,7 @@ func TestUDP_SerializeError(t *testing.T) {
}
func TestUDP_WriteWithRealConn(t *testing.T) {
conn, err := net.ListenPacket("udp", ":0")
conn, err := net.ListenPacket("udp", "127.0.0.0:0")
require.NoError(t, err)
metrics := []telegraf.Metric{

View File

@@ -2,38 +2,36 @@
This plugin writes to a [MQTT Broker](http://http://mqtt.org/) acting as a mqtt Producer.
```
```toml
[[outputs.mqtt]]
## URLs of mqtt brokers
servers = ["localhost:1883"]
## topic for producer messages
topic = "telegraf"
topic_prefix = "telegraf"
## QoS policy for messages
qos = 2
## username and password to connect MQTT server.
# username = "telegraf"
# password = "metricsmetricsmetricsmetrics"
## client ID, if not set a random ID is generated
# client_id = ""
## Timeout for write operations. default: 5s
# timeout = "5s"
## Optional SSL Config
# ssl_ca = "/etc/telegraf/ca.pem"
# ssl_cert = "/etc/telegraf/cert.pem"
# ssl_key = "/etc/telegraf/key.pem"
## Use SSL but skip chain & host verification
# insecure_skip_verify = false
## Data format to output.
data_format = "influx"
```
### Required parameters:

View File

@@ -352,6 +352,8 @@ func (p *PrometheusClient) Write(metrics []telegraf.Metric) error {
switch fv := fv.(type) {
case int64:
value = float64(fv)
case uint64:
value = float64(fv)
case float64:
value = fv
default:
@@ -391,6 +393,8 @@ func (p *PrometheusClient) Write(metrics []telegraf.Metric) error {
switch fv := fv.(type) {
case int64:
value = float64(fv)
case uint64:
value = float64(fv)
case float64:
value = fv
default:
@@ -427,6 +431,8 @@ func (p *PrometheusClient) Write(metrics []telegraf.Metric) error {
switch fv := fv.(type) {
case int64:
value = float64(fv)
case uint64:
value = float64(fv)
case float64:
value = fv
default:

View File

@@ -151,6 +151,16 @@ func TestWrite_Counters(t *testing.T) {
metricName: "foo_other",
valueType: telegraf.Counter,
},
{
name: "uint64 fields are output",
args: args{
measurement: "foo",
fields: map[string]interface{}{"value": uint64(42)},
valueType: telegraf.Counter,
},
metricName: "foo",
valueType: telegraf.Counter,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -239,6 +249,16 @@ func TestWrite_Gauge(t *testing.T) {
metricName: "foo_other",
valueType: telegraf.Gauge,
},
{
name: "uint64 fields are output",
args: args{
measurement: "foo",
fields: map[string]interface{}{"value": uint64(42)},
valueType: telegraf.Counter,
},
metricName: "foo",
valueType: telegraf.Counter,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -19,6 +19,13 @@ It can output data in any of the [supported output formats](https://github.com/i
# address = "unix:///tmp/telegraf.sock"
# address = "unixgram:///tmp/telegraf.sock"
## Optional SSL Config
# ssl_ca = "/etc/telegraf/ca.pem"
# ssl_cert = "/etc/telegraf/cert.pem"
# ssl_key = "/etc/telegraf/key.pem"
## Use SSL but skip chain & host verification
# insecure_skip_verify = false
## Period between keep alive probes.
## Only applies to TCP sockets.
## 0 disables keep alive probes.

View File

@@ -6,6 +6,8 @@ import (
"net"
"strings"
"crypto/tls"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/outputs"
@@ -13,8 +15,12 @@ import (
)
type SocketWriter struct {
Address string
KeepAlivePeriod *internal.Duration
Address string
KeepAlivePeriod *internal.Duration
SSLCA string
SSLCert string
SSLKey string
InsecureSkipVerify bool
serializers.Serializer
@@ -39,6 +45,13 @@ func (sw *SocketWriter) SampleConfig() string {
# address = "unix:///tmp/telegraf.sock"
# address = "unixgram:///tmp/telegraf.sock"
## Optional SSL Config
# ssl_ca = "/etc/telegraf/ca.pem"
# ssl_cert = "/etc/telegraf/cert.pem"
# ssl_key = "/etc/telegraf/key.pem"
## Use SSL but skip chain & host verification
# insecure_skip_verify = false
## Period between keep alive probes.
## Only applies to TCP sockets.
## 0 disables keep alive probes.
@@ -63,7 +76,17 @@ func (sw *SocketWriter) Connect() error {
return fmt.Errorf("invalid address: %s", sw.Address)
}
c, err := net.Dial(spl[0], spl[1])
tlsCfg, err := internal.GetTLSConfig(sw.SSLCert, sw.SSLKey, sw.SSLCA, sw.InsecureSkipVerify)
if err != nil {
return err
}
var c net.Conn
if tlsCfg == nil {
c, err = net.Dial(spl[0], spl[1])
} else {
c, err = tls.Dial(spl[0], spl[1], tlsCfg)
}
if err != nil {
return err
}

File diff suppressed because it is too large Load Diff

View File

@@ -122,7 +122,7 @@ unsigned =
( digit | ( non_zero_digit digit* ) );
number =
( integer ( '.' digit* )? ) | ( '.' digit* );
'-'? (digit+ ('.' digit*)? | '.' digit+);
scientific =
number 'e'i ["\-+"]? digit+;
@@ -142,7 +142,7 @@ fieldfloat =
fieldinteger =
(integer 'i') >begin %integer;
fieldunsigned =
fieldunsigned =
(unsigned 'u') >begin %unsigned;
false =
@@ -155,7 +155,7 @@ fieldbool =
(true | false) >begin %bool;
fieldstringchar =
[^\\"] | '\\' [\\"];
[^\n\f\r\\"] | '\\' [\\"];
fieldstring =
fieldstringchar* >begin %string;

View File

@@ -517,6 +517,55 @@ var tests = []struct {
},
},
},
{
name: "float without integer digits negative",
input: []byte("cpu value=-.42"),
results: []Result{
Result{
Name: Measurement,
Value: []byte("cpu"),
},
Result{
Name: FieldKey,
Value: []byte("value"),
},
Result{
Name: FieldFloat,
Value: []byte("-.42"),
},
},
},
{
name: "float with multiple leading 0",
input: []byte("cpu value=00.42"),
results: []Result{
Result{
Name: Measurement,
Value: []byte("cpu"),
},
Result{
Name: FieldKey,
Value: []byte("value"),
},
Result{
Name: FieldFloat,
Value: []byte("00.42"),
},
},
},
{
name: "invalid float with only dot",
input: []byte("cpu value=."),
results: []Result{
Result{
Name: Measurement,
Value: []byte("cpu"),
},
Result{
err: ErrFieldParse,
},
},
},
{
name: "multiple fields",
input: []byte("cpu x=42,y=42"),
@@ -1154,6 +1203,22 @@ var tests = []struct {
},
},
},
{
name: "invalid newline in string field",
input: []byte("cpu value=\"4\n2\""),
results: []Result{
Result{
Name: Measurement,
Value: []byte("cpu"),
},
Result{
err: ErrFieldParse,
},
Result{
err: ErrFieldParse,
},
},
},
{
name: "invalid field value",
input: []byte(`cpu value=howdy`),

View File

@@ -282,7 +282,7 @@ var ptests = []struct {
"cpu",
map[string]string{},
map[string]interface{}{
"value": 9223372036854775807,
"value": int64(9223372036854775807),
},
time.Unix(42, 0),
),

View File

@@ -2,8 +2,10 @@ package graphite
import (
"fmt"
"math"
"regexp"
"sort"
"strconv"
"strings"
"github.com/influxdata/telegraf"
@@ -43,20 +45,14 @@ func (s *GraphiteSerializer) Serialize(metric telegraf.Metric) ([]byte, error) {
}
for fieldName, value := range metric.Fields() {
switch v := value.(type) {
case string:
fieldValue := formatValue(value)
if fieldValue == "" {
continue
case bool:
if v {
value = 1
} else {
value = 0
}
}
metricString := fmt.Sprintf("%s %#v %d\n",
metricString := fmt.Sprintf("%s %s %d\n",
// insert "field" section of template
sanitize(InsertField(bucket, fieldName)),
value,
fieldValue,
timestamp)
point := []byte(metricString)
out = append(out, point...)
@@ -64,6 +60,34 @@ func (s *GraphiteSerializer) Serialize(metric telegraf.Metric) ([]byte, error) {
return out, nil
}
func formatValue(value interface{}) string {
switch v := value.(type) {
case string:
return ""
case bool:
if v {
return "1"
} else {
return "0"
}
case uint64:
return strconv.FormatUint(v, 10)
case int64:
return strconv.FormatInt(v, 10)
case float64:
if math.IsNaN(v) {
return ""
}
if math.IsInf(v, 0) {
return ""
}
return strconv.FormatFloat(v, 'f', -1, 64)
}
return ""
}
// SerializeBucketName will take the given measurement name and tags and
// produce a graphite bucket. It will use the GraphiteSerializer.Template
// to generate this, or DEFAULT_TEMPLATE.

View File

@@ -218,6 +218,22 @@ func TestSerializeValueBoolean(t *testing.T) {
assert.Equal(t, expS, mS)
}
func TestSerializeValueUnsigned(t *testing.T) {
now := time.Unix(0, 0)
tags := map[string]string{}
fields := map[string]interface{}{
"free": uint64(42),
}
m, err := metric.New("mem", tags, fields, now)
require.NoError(t, err)
s := GraphiteSerializer{}
buf, err := s.Serialize(m)
require.NoError(t, err)
require.Equal(t, buf, []byte(".mem.free 42 0\n"))
}
// test that fields with spaces get fixed.
func TestSerializeFieldWithSpaces(t *testing.T) {
now := time.Now()

View File

@@ -3,30 +3,42 @@ package influx
import "strings"
const (
escapes = " ,="
nameEscapes = " ,"
stringFieldEscapes = `\"`
escapes = "\t\n\f\r ,="
nameEscapes = "\t\n\f\r ,"
stringFieldEscapes = "\t\n\f\r\\\""
)
var (
escaper = strings.NewReplacer(
"\t", `\t`,
"\n", `\n`,
"\f", `\f`,
"\r", `\r`,
`,`, `\,`,
`"`, `\"`, // ???
` `, `\ `,
`=`, `\=`,
)
nameEscaper = strings.NewReplacer(
"\t", `\t`,
"\n", `\n`,
"\f", `\f`,
"\r", `\r`,
`,`, `\,`,
` `, `\ `,
)
stringFieldEscaper = strings.NewReplacer(
"\t", `\t`,
"\n", `\n`,
"\f", `\f`,
"\r", `\r`,
`"`, `\"`,
`\`, `\\`,
)
)
// Escape a tagkey, tagvalue, or fieldkey
func escape(s string) string {
if strings.ContainsAny(s, escapes) {
return escaper.Replace(s)
@@ -35,6 +47,7 @@ func escape(s string) string {
}
}
// Escape a measurement name
func nameEscape(s string) string {
if strings.ContainsAny(s, nameEscapes) {
return nameEscaper.Replace(s)
@@ -43,6 +56,7 @@ func nameEscape(s string) string {
}
}
// Escape a string field
func stringFieldEscape(s string) string {
if strings.ContainsAny(s, stringFieldEscapes) {
return stringFieldEscaper.Replace(s)

View File

@@ -2,8 +2,9 @@ package influx
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"math"
"sort"
"strconv"
@@ -11,7 +12,7 @@ import (
"github.com/influxdata/telegraf"
)
const MaxInt = int(^uint(0) >> 1)
const MaxInt64 = int64(^uint64(0) >> 1)
type FieldSortOrder int
@@ -26,14 +27,28 @@ const (
UintSupport FieldTypeSupport = 1 << iota
)
// MetricError is an error causing a metric to be unserializable.
type MetricError struct {
s string
}
func (e MetricError) Error() string {
return e.s
}
// FieldError is an error causing a field to be unserializable.
type FieldError struct {
s string
}
func (e FieldError) Error() string {
return e.s
}
var (
ErrNeedMoreSpace = errors.New("need more space")
ErrInvalidName = errors.New("invalid name")
ErrInvalidFieldKey = errors.New("invalid field key")
ErrInvalidFieldType = errors.New("invalid field type")
ErrFieldIsNaN = errors.New("is NaN")
ErrFieldIsInf = errors.New("is Inf")
ErrNoFields = errors.New("no fields")
ErrNeedMoreSpace = &MetricError{"need more space"}
ErrInvalidName = &MetricError{"invalid name"}
ErrNoFields = &MetricError{"no serializable fields"}
)
// Serializer is a serializer for line protocol.
@@ -148,7 +163,7 @@ func (s *Serializer) buildFieldPair(key string, value interface{}) error {
// Some keys are not encodeable as line protocol, such as those with a
// trailing '\' or empty strings.
if key == "" {
return ErrInvalidFieldKey
return &FieldError{"invalid field key"}
}
s.pair = append(s.pair, key...)
@@ -182,6 +197,9 @@ func (s *Serializer) writeMetric(w io.Writer, m telegraf.Metric) error {
for _, field := range m.FieldList() {
err = s.buildFieldPair(field.Key, field.Value)
if err != nil {
log.Printf(
"D! [serializers.influx] could not serialize field %q: %v; discarding field",
field.Key, err)
continue
}
@@ -252,21 +270,21 @@ func (s *Serializer) appendFieldValue(buf []byte, value interface{}) ([]byte, er
if s.fieldTypeSupport&UintSupport != 0 {
return appendUintField(buf, v), nil
} else {
if v <= uint64(MaxInt) {
if v <= uint64(MaxInt64) {
return appendIntField(buf, int64(v)), nil
} else {
return appendIntField(buf, int64(MaxInt)), nil
return appendIntField(buf, int64(MaxInt64)), nil
}
}
case int64:
return appendIntField(buf, v), nil
case float64:
if math.IsNaN(v) {
return nil, ErrFieldIsNaN
return nil, &FieldError{"is NaN"}
}
if math.IsInf(v, 0) {
return nil, ErrFieldIsInf
return nil, &FieldError{"is Inf"}
}
return appendFloatField(buf, v), nil
@@ -274,8 +292,9 @@ func (s *Serializer) appendFieldValue(buf []byte, value interface{}) ([]byte, er
return appendStringField(buf, v), nil
case bool:
return appendBoolField(buf, v), nil
default:
return buf, &FieldError{fmt.Sprintf("invalid value type: %T", v)}
}
return buf, ErrInvalidFieldType
}
func appendUintField(buf []byte, value uint64) []byte {

Some files were not shown because too many files have changed in this diff Show More