Compare commits
5 Commits
1.3.5
...
dgn-extern
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81caa56859 | ||
|
|
3e6c4a53a4 | ||
|
|
ba8f91a038 | ||
|
|
2b77751df8 | ||
|
|
70d678c442 |
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,7 +1,7 @@
|
|||||||
## Directions
|
## Directions
|
||||||
|
|
||||||
GitHub Issues are reserved for actionable bug reports and feature requests.
|
GitHub Issues are reserved for actionable bug reports and feature requests.
|
||||||
General questions should be asked at the [InfluxData Community](https://community.influxdata.com) site.
|
General questions should be sent to the [InfluxDB mailing list](https://groups.google.com/forum/#!forum/influxdb).
|
||||||
|
|
||||||
Before opening an issue, search for similar bug reports or feature requests on GitHub Issues.
|
Before opening an issue, search for similar bug reports or feature requests on GitHub Issues.
|
||||||
If no similar issue can be found, fill out either the "Bug Report" or the "Feature Request" section below.
|
If no similar issue can be found, fill out either the "Bug Report" or the "Feature Request" section below.
|
||||||
|
|||||||
195
CHANGELOG.md
195
CHANGELOG.md
@@ -1,187 +1,4 @@
|
|||||||
## v1.3.5 [2017-07-26]
|
## v1.2 [unreleased]
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#3049](https://github.com/influxdata/telegraf/issues/3049): Fix prometheus output cannot be reloaded.
|
|
||||||
- [#3037](https://github.com/influxdata/telegraf/issues/3037): Fix filestat reporting exists when cannot list directory.
|
|
||||||
- [#2386](https://github.com/influxdata/telegraf/issues/2386): Fix ntpq parse issue when using dns_lookup.
|
|
||||||
- [#2554](https://github.com/influxdata/telegraf/issues/2554): Fix panic when agent.interval = "0s".
|
|
||||||
|
|
||||||
## v1.3.4 [2017-07-12]
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#3001](https://github.com/influxdata/telegraf/issues/3001): Fix handling of escape characters within fields.
|
|
||||||
- [#2988](https://github.com/influxdata/telegraf/issues/2988): Fix chrony plugin does not track system time offset.
|
|
||||||
- [#3004](https://github.com/influxdata/telegraf/issues/3004): Do not allow metrics with trailing slashes.
|
|
||||||
- [#3011](https://github.com/influxdata/telegraf/issues/3011): Prevent Write from being called concurrently.
|
|
||||||
|
|
||||||
## v1.3.3 [2017-06-28]
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2915](https://github.com/influxdata/telegraf/issues/2915): Allow dos line endings in tail and logparser.
|
|
||||||
- [#2937](https://github.com/influxdata/telegraf/issues/2937): Remove label value sanitization in prometheus output.
|
|
||||||
- [#2948](https://github.com/influxdata/telegraf/issues/2948): Fix bug parsing default timestamps with modified precision.
|
|
||||||
- [#2954](https://github.com/influxdata/telegraf/issues/2954): Fix panic in elasticsearch input if cannot determine master.
|
|
||||||
|
|
||||||
## v1.3.2 [2017-06-14]
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2862](https://github.com/influxdata/telegraf/issues/2862): Fix InfluxDB UDP metric splitting.
|
|
||||||
- [#2888](https://github.com/influxdata/telegraf/issues/2888): Fix mongodb/leofs urls without scheme.
|
|
||||||
- [#2822](https://github.com/influxdata/telegraf/issues/2822): Fix inconsistent label dimensions in prometheus output.
|
|
||||||
|
|
||||||
## v1.3.1 [2017-05-31]
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2749](https://github.com/influxdata/telegraf/pull/2749): Fixed sqlserver input to work with case sensitive server collation.
|
|
||||||
- [#2782](https://github.com/influxdata/telegraf/pull/2782): Reuse transports in input plugins
|
|
||||||
- [#2815](https://github.com/influxdata/telegraf/issues/2815): Inputs processes fails with "no such process".
|
|
||||||
- [#2851](https://github.com/influxdata/telegraf/pull/2851): Fix InfluxDB output database quoting.
|
|
||||||
- [#2856](https://github.com/influxdata/telegraf/issues/2856): Fix net input on older Linux kernels.
|
|
||||||
- [#2848](https://github.com/influxdata/telegraf/pull/2848): Fix panic in mongo input.
|
|
||||||
- [#2869](https://github.com/influxdata/telegraf/pull/2869): Fix length calculation of split metric buffer.
|
|
||||||
|
|
||||||
## v1.3 [2017-05-15]
|
|
||||||
|
|
||||||
### Release Notes
|
|
||||||
|
|
||||||
- Users of the windows `ping` plugin will need to drop or migrate their
|
|
||||||
measurements in order to continue using the plugin. The reason for this is that
|
|
||||||
the windows plugin was outputting a different type than the linux plugin. This
|
|
||||||
made it impossible to use the `ping` plugin for both windows and linux
|
|
||||||
machines.
|
|
||||||
|
|
||||||
- Ceph: the `ceph_pgmap_state` metric content has been modified to use a unique field `count`, with each state expressed as a `state` tag.
|
|
||||||
|
|
||||||
Telegraf < 1.3:
|
|
||||||
|
|
||||||
```
|
|
||||||
# field_name value
|
|
||||||
active+clean 123
|
|
||||||
active+clean+scrubbing 3
|
|
||||||
```
|
|
||||||
|
|
||||||
Telegraf >= 1.3:
|
|
||||||
|
|
||||||
```
|
|
||||||
# field_name value tag
|
|
||||||
count 123 state=active+clean
|
|
||||||
count 3 state=active+clean+scrubbing
|
|
||||||
```
|
|
||||||
|
|
||||||
- The [Riemann output plugin](./plugins/outputs/riemann) has been rewritten
|
|
||||||
and the previous riemann plugin is _incompatible_ with the new one. The reasons
|
|
||||||
for this are outlined in issue [#1878](https://github.com/influxdata/telegraf/issues/1878).
|
|
||||||
The previous riemann output will still be available using
|
|
||||||
`outputs.riemann_legacy` if needed, but that will eventually be deprecated.
|
|
||||||
It is highly recommended that all users migrate to the new riemann output plugin.
|
|
||||||
|
|
||||||
- Generic [socket_listener](./plugins/inputs/socket_listener) and
|
|
||||||
[socket_writer](./plugins/outputs/socket_writer) plugins have been implemented
|
|
||||||
for receiving and sending UDP, TCP, unix, & unix-datagram data. These plugins
|
|
||||||
will replace udp_listener and tcp_listener, which are still available but will
|
|
||||||
be deprecated eventually.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- [#2721](https://github.com/influxdata/telegraf/pull/2721): Added SASL options for kafka output plugin.
|
|
||||||
- [#2723](https://github.com/influxdata/telegraf/pull/2723): Added SSL configuration for input haproxy.
|
|
||||||
- [#2494](https://github.com/influxdata/telegraf/pull/2494): Add interrupts input plugin.
|
|
||||||
- [#2094](https://github.com/influxdata/telegraf/pull/2094): Add generic socket listener & writer.
|
|
||||||
- [#2204](https://github.com/influxdata/telegraf/pull/2204): Extend http_response to support searching for a substring in response. Return 1 if found, else 0.
|
|
||||||
- [#2137](https://github.com/influxdata/telegraf/pull/2137): Added userstats to mysql input plugin.
|
|
||||||
- [#2179](https://github.com/influxdata/telegraf/pull/2179): Added more InnoDB metric to MySQL plugin.
|
|
||||||
- [#2229](https://github.com/influxdata/telegraf/pull/2229): `ceph_pgmap_state` metric now uses a single field `count`, with PG state published as `state` tag.
|
|
||||||
- [#2251](https://github.com/influxdata/telegraf/pull/2251): InfluxDB output: use own client for improved through-put and less allocations.
|
|
||||||
- [#2330](https://github.com/influxdata/telegraf/pull/2330): Keep -config-directory when running as Windows service.
|
|
||||||
- [#1900](https://github.com/influxdata/telegraf/pull/1900): Riemann plugin rewrite.
|
|
||||||
- [#1453](https://github.com/influxdata/telegraf/pull/1453): diskio: add support for name templates and udev tags.
|
|
||||||
- [#2277](https://github.com/influxdata/telegraf/pull/2277): add integer metrics for Consul check health state.
|
|
||||||
- [#2201](https://github.com/influxdata/telegraf/pull/2201): Add lock option to the IPtables input plugin.
|
|
||||||
- [#2244](https://github.com/influxdata/telegraf/pull/2244): Support ipmi_sensor plugin querying local ipmi sensors.
|
|
||||||
- [#2339](https://github.com/influxdata/telegraf/pull/2339): Increment gather_errors for all errors emitted by inputs.
|
|
||||||
- [#2071](https://github.com/influxdata/telegraf/issues/2071): Use official docker SDK.
|
|
||||||
- [#1678](https://github.com/influxdata/telegraf/pull/1678): Add AMQP consumer input plugin
|
|
||||||
- [#2512](https://github.com/influxdata/telegraf/pull/2512): Added pprof tool.
|
|
||||||
- [#2501](https://github.com/influxdata/telegraf/pull/2501): Support DEAD(X) state in system input plugin.
|
|
||||||
- [#2522](https://github.com/influxdata/telegraf/pull/2522): Add support for mongodb client certificates.
|
|
||||||
- [#1948](https://github.com/influxdata/telegraf/pull/1948): Support adding SNMP table indexes as tags.
|
|
||||||
- [#2332](https://github.com/influxdata/telegraf/pull/2332): Add Elasticsearch 5.x output
|
|
||||||
- [#2587](https://github.com/influxdata/telegraf/pull/2587): Add json timestamp units configurability
|
|
||||||
- [#2597](https://github.com/influxdata/telegraf/issues/2597): Add support for Linux sysctl-fs metrics.
|
|
||||||
- [#2425](https://github.com/influxdata/telegraf/pull/2425): Support to include/exclude docker container labels as tags
|
|
||||||
- [#1667](https://github.com/influxdata/telegraf/pull/1667): dmcache input plugin
|
|
||||||
- [#2637](https://github.com/influxdata/telegraf/issues/2637): Add support for precision in http_listener
|
|
||||||
- [#2636](https://github.com/influxdata/telegraf/pull/2636): Add `message_len_max` option to `kafka_consumer` input
|
|
||||||
- [#1100](https://github.com/influxdata/telegraf/issues/1100): Add collectd parser
|
|
||||||
- [#1820](https://github.com/influxdata/telegraf/issues/1820): easier plugin testing without outputs
|
|
||||||
- [#2493](https://github.com/influxdata/telegraf/pull/2493): Check signature in the GitHub webhook plugin
|
|
||||||
- [#2038](https://github.com/influxdata/telegraf/issues/2038): Add papertrail support to webhooks
|
|
||||||
- [#2253](https://github.com/influxdata/telegraf/pull/2253): Change jolokia plugin to use bulk requests.
|
|
||||||
- [#2575](https://github.com/influxdata/telegraf/issues/2575) Add diskio input for Darwin
|
|
||||||
- [#2705](https://github.com/influxdata/telegraf/pull/2705): Kinesis output: add use_random_partitionkey option
|
|
||||||
- [#2635](https://github.com/influxdata/telegraf/issues/2635): add tcp keep-alive to socket_listener & socket_writer
|
|
||||||
- [#2031](https://github.com/influxdata/telegraf/pull/2031): Add Kapacitor input plugin
|
|
||||||
- [#2732](https://github.com/influxdata/telegraf/pull/2732): Use go 1.8.1
|
|
||||||
- [#2712](https://github.com/influxdata/telegraf/issues/2712): Documentation for rabbitmq input plugin
|
|
||||||
- [#2141](https://github.com/influxdata/telegraf/pull/2141): Logparser handles newly-created files.
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2633](https://github.com/influxdata/telegraf/pull/2633): ipmi_sensor: allow @ symbol in password
|
|
||||||
- [#2077](https://github.com/influxdata/telegraf/issues/2077): SQL Server Input - Arithmetic overflow error converting numeric to data type int.
|
|
||||||
- [#2262](https://github.com/influxdata/telegraf/issues/2262): Flush jitter can inhibit metric collection.
|
|
||||||
- [#2318](https://github.com/influxdata/telegraf/issues/2318): haproxy input - Add missing fields.
|
|
||||||
- [#2287](https://github.com/influxdata/telegraf/issues/2287): Kubernetes input: Handle null startTime for stopped pods.
|
|
||||||
- [#2356](https://github.com/influxdata/telegraf/issues/2356): cpu input panic when /proc/stat is empty.
|
|
||||||
- [#2341](https://github.com/influxdata/telegraf/issues/2341): telegraf swallowing panics in --test mode.
|
|
||||||
- [#2358](https://github.com/influxdata/telegraf/pull/2358): Create pidfile with 644 permissions & defer file deletion.
|
|
||||||
- [#2360](https://github.com/influxdata/telegraf/pull/2360): Fixed install/remove of telegraf on non-systemd Debian/Ubuntu systems
|
|
||||||
- [#2282](https://github.com/influxdata/telegraf/issues/2282): Reloading telegraf freezes prometheus output.
|
|
||||||
- [#2390](https://github.com/influxdata/telegraf/issues/2390): Empty tag value causes error on InfluxDB output.
|
|
||||||
- [#2380](https://github.com/influxdata/telegraf/issues/2380): buffer_size field value is negative number from "internal" plugin.
|
|
||||||
- [#2414](https://github.com/influxdata/telegraf/issues/2414): Missing error handling in the MySQL plugin leads to segmentation violation.
|
|
||||||
- [#2462](https://github.com/influxdata/telegraf/pull/2462): Fix type conflict in windows ping plugin.
|
|
||||||
- [#2178](https://github.com/influxdata/telegraf/issues/2178): logparser: regexp with lookahead.
|
|
||||||
- [#2466](https://github.com/influxdata/telegraf/issues/2466): Telegraf can crash in LoadDirectory on 0600 files.
|
|
||||||
- [#2215](https://github.com/influxdata/telegraf/issues/2215): Iptables input: document better that rules without a comment are ignored.
|
|
||||||
- [#2483](https://github.com/influxdata/telegraf/pull/2483): Fix win_perf_counters capping values at 100.
|
|
||||||
- [#2498](https://github.com/influxdata/telegraf/pull/2498): Exporting Ipmi.Path to be set by config.
|
|
||||||
- [#2500](https://github.com/influxdata/telegraf/pull/2500): Remove warning if parse empty content
|
|
||||||
- [#2520](https://github.com/influxdata/telegraf/pull/2520): Update default value for Cloudwatch rate limit
|
|
||||||
- [#2513](https://github.com/influxdata/telegraf/issues/2513): create /etc/telegraf/telegraf.d directory in tarball.
|
|
||||||
- [#2541](https://github.com/influxdata/telegraf/issues/2541): Return error on unsupported serializer data format.
|
|
||||||
- [#1827](https://github.com/influxdata/telegraf/issues/1827): Fix Windows Performance Counters multi instance identifier
|
|
||||||
- [#2576](https://github.com/influxdata/telegraf/pull/2576): Add write timeout to Riemann output
|
|
||||||
- [#2596](https://github.com/influxdata/telegraf/pull/2596): fix timestamp parsing on prometheus plugin
|
|
||||||
- [#2610](https://github.com/influxdata/telegraf/pull/2610): Fix deadlock when output cannot write
|
|
||||||
- [#2410](https://github.com/influxdata/telegraf/issues/2410): Fix connection leak in postgresql.
|
|
||||||
- [#2628](https://github.com/influxdata/telegraf/issues/2628): Set default measurement name for snmp input.
|
|
||||||
- [#2649](https://github.com/influxdata/telegraf/pull/2649): Improve performance of diskio with many disks
|
|
||||||
- [#2671](https://github.com/influxdata/telegraf/issues/2671): The internal input plugin uses the wrong units for `heap_objects`
|
|
||||||
- [#2684](https://github.com/influxdata/telegraf/pull/2684): Fix ipmi_sensor config is shared between all plugin instances
|
|
||||||
- [#2450](https://github.com/influxdata/telegraf/issues/2450): Network statistics not collected when system has alias interfaces
|
|
||||||
- [#1911](https://github.com/influxdata/telegraf/issues/1911): Sysstat plugin needs LANG=C or similar locale
|
|
||||||
- [#2528](https://github.com/influxdata/telegraf/issues/2528): File output closes standard streams on reload.
|
|
||||||
- [#2603](https://github.com/influxdata/telegraf/issues/2603): AMQP output disconnect blocks all outputs
|
|
||||||
- [#2706](https://github.com/influxdata/telegraf/issues/2706): Improve documentation for redis input plugin
|
|
||||||
|
|
||||||
## v1.2.1 [2017-02-01]
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- [#2317](https://github.com/influxdata/telegraf/issues/2317): Fix segfault on nil metrics with influxdb output.
|
|
||||||
- [#2324](https://github.com/influxdata/telegraf/issues/2324): Fix negative number handling.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- [#2348](https://github.com/influxdata/telegraf/pull/2348): Go version 1.7.4 -> 1.7.5
|
|
||||||
|
|
||||||
## v1.2 [2017-01-00]
|
|
||||||
|
|
||||||
### Release Notes
|
### Release Notes
|
||||||
|
|
||||||
@@ -227,7 +44,6 @@ plugins, not just statsd.
|
|||||||
- [#1942](https://github.com/influxdata/telegraf/pull/1942): Change Amazon Kinesis output plugin to use the built-in serializer plugins.
|
- [#1942](https://github.com/influxdata/telegraf/pull/1942): Change Amazon Kinesis output plugin to use the built-in serializer plugins.
|
||||||
- [#1980](https://github.com/influxdata/telegraf/issues/1980): Hide username/password from elasticsearch error log messages.
|
- [#1980](https://github.com/influxdata/telegraf/issues/1980): Hide username/password from elasticsearch error log messages.
|
||||||
- [#2097](https://github.com/influxdata/telegraf/issues/2097): Configurable HTTP timeouts in Jolokia plugin
|
- [#2097](https://github.com/influxdata/telegraf/issues/2097): Configurable HTTP timeouts in Jolokia plugin
|
||||||
- [#2255](https://github.com/influxdata/telegraf/pull/2255): Allow changing jolokia attribute delimiter
|
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
|
|
||||||
@@ -237,7 +53,7 @@ plugins, not just statsd.
|
|||||||
- [#1775](https://github.com/influxdata/telegraf/issues/1775): Cache & expire metrics for delivery to prometheus.
|
- [#1775](https://github.com/influxdata/telegraf/issues/1775): Cache & expire metrics for delivery to prometheus.
|
||||||
- [#2146](https://github.com/influxdata/telegraf/issues/2146): Fix potential panic in aggregator plugin metric maker.
|
- [#2146](https://github.com/influxdata/telegraf/issues/2146): Fix potential panic in aggregator plugin metric maker.
|
||||||
- [#1843](https://github.com/influxdata/telegraf/pull/1843) & [#1668](https://github.com/influxdata/telegraf/issues/1668): Add optional ability to define PID as a tag.
|
- [#1843](https://github.com/influxdata/telegraf/pull/1843) & [#1668](https://github.com/influxdata/telegraf/issues/1668): Add optional ability to define PID as a tag.
|
||||||
- [#1730](https://github.com/influxdata/telegraf/issues/1730) & [#2261](https://github.com/influxdata/telegraf/pull/2261): Fix win_perf_counters not gathering non-English counters.
|
- [#1730](https://github.com/influxdata/telegraf/issues/1730): Fix win_perf_counters not gathering non-English counters.
|
||||||
- [#2061](https://github.com/influxdata/telegraf/issues/2061): Fix panic when file stat info cannot be collected due to permissions or other issue(s).
|
- [#2061](https://github.com/influxdata/telegraf/issues/2061): Fix panic when file stat info cannot be collected due to permissions or other issue(s).
|
||||||
- [#2045](https://github.com/influxdata/telegraf/issues/2045): Graylog output should set short_message field.
|
- [#2045](https://github.com/influxdata/telegraf/issues/2045): Graylog output should set short_message field.
|
||||||
- [#1904](https://github.com/influxdata/telegraf/issues/1904): Hddtemp always put the value in the field temperature.
|
- [#1904](https://github.com/influxdata/telegraf/issues/1904): Hddtemp always put the value in the field temperature.
|
||||||
@@ -250,10 +66,6 @@ plugins, not just statsd.
|
|||||||
- [#1973](https://github.com/influxdata/telegraf/issues/1973): Partial fix: logparser CLF pattern with IPv6 addresses.
|
- [#1973](https://github.com/influxdata/telegraf/issues/1973): Partial fix: logparser CLF pattern with IPv6 addresses.
|
||||||
- [#1975](https://github.com/influxdata/telegraf/issues/1975) & [#2102](https://github.com/influxdata/telegraf/issues/2102): Fix thread-safety when using multiple instances of the statsd input plugin.
|
- [#1975](https://github.com/influxdata/telegraf/issues/1975) & [#2102](https://github.com/influxdata/telegraf/issues/2102): Fix thread-safety when using multiple instances of the statsd input plugin.
|
||||||
- [#2027](https://github.com/influxdata/telegraf/issues/2027): docker input: interface conversion panic fix.
|
- [#2027](https://github.com/influxdata/telegraf/issues/2027): docker input: interface conversion panic fix.
|
||||||
- [#1814](https://github.com/influxdata/telegraf/issues/1814): snmp: ensure proper context is present on error messages.
|
|
||||||
- [#2299](https://github.com/influxdata/telegraf/issues/2299): opentsdb: add tcp:// prefix if no scheme provided.
|
|
||||||
- [#2297](https://github.com/influxdata/telegraf/issues/2297): influx parser: parse line-protocol without newlines.
|
|
||||||
- [#2245](https://github.com/influxdata/telegraf/issues/2245): influxdb output: fix field type conflict blocking output buffer.
|
|
||||||
|
|
||||||
## v1.1.2 [2016-12-12]
|
## v1.1.2 [2016-12-12]
|
||||||
|
|
||||||
@@ -404,11 +216,8 @@ which can be installed via
|
|||||||
evaluated at every flush interval, rather than once at startup. This makes it
|
evaluated at every flush interval, rather than once at startup. This makes it
|
||||||
consistent with the behavior of `collection_jitter`.
|
consistent with the behavior of `collection_jitter`.
|
||||||
|
|
||||||
- postgresql plugins now handle oid and name typed columns seamlessly, previously they were ignored/skipped.
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- [#1617](https://github.com/influxdata/telegraf/pull/1617): postgresql_extensible now handles name and oid types correctly.
|
|
||||||
- [#1413](https://github.com/influxdata/telegraf/issues/1413): Separate container_version from container_image tag.
|
- [#1413](https://github.com/influxdata/telegraf/issues/1413): Separate container_version from container_image tag.
|
||||||
- [#1525](https://github.com/influxdata/telegraf/pull/1525): Support setting per-device and total metrics for Docker network and blockio.
|
- [#1525](https://github.com/influxdata/telegraf/pull/1525): Support setting per-device and total metrics for Docker network and blockio.
|
||||||
- [#1466](https://github.com/influxdata/telegraf/pull/1466): MongoDB input plugin: adding per DB stats from db.stats()
|
- [#1466](https://github.com/influxdata/telegraf/pull/1466): MongoDB input plugin: adding per DB stats from db.stats()
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ You should also add the following to your SampleConfig() return:
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "influx"
|
data_format = "influx"
|
||||||
@@ -254,7 +254,7 @@ You should also add the following to your SampleConfig() return:
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
## Data format to output.
|
## Data format to output.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||||
data_format = "influx"
|
data_format = "influx"
|
||||||
|
|||||||
114
Godeps
114
Godeps
@@ -1,65 +1,65 @@
|
|||||||
collectd.org 2ce144541b8903101fb8f1483cc0497a68798122
|
github.com/Shopify/sarama 8aadb476e66ca998f2f6bb3c993e9a2daa3666b9
|
||||||
github.com/Shopify/sarama 574d3147eee384229bf96a5d12c207fe7b5234f3
|
github.com/Sirupsen/logrus 219c8cb75c258c552e999735be6df753ffc7afdc
|
||||||
github.com/Sirupsen/logrus 61e43dc76f7ee59a82bdf3d71033dc12bea4c77d
|
github.com/aerospike/aerospike-client-go 7f3a312c3b2a60ac083ec6da296091c52c795c63
|
||||||
github.com/aerospike/aerospike-client-go 95e1ad7791bdbca44707fedbb29be42024900d9c
|
github.com/amir/raidman 53c1b967405155bfc8758557863bf2e14f814687
|
||||||
github.com/amir/raidman c74861fe6a7bb8ede0a010ce4485bdbb4fc4c985
|
github.com/aws/aws-sdk-go 13a12060f716145019378a10e2806c174356b857
|
||||||
github.com/aws/aws-sdk-go 7524cb911daddd6e5c9195def8e59ae892bef8d9
|
github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
|
||||||
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
github.com/cenkalti/backoff 4dc77674aceaabba2c7e3da25d4c823edfb73f99
|
||||||
github.com/cenkalti/backoff b02f2bbce11d7ea6b97f282ef1771b0fe2f65ef3
|
github.com/couchbase/go-couchbase cb664315a324d87d19c879d9cc67fda6be8c2ac1
|
||||||
github.com/couchbase/go-couchbase bfe555a140d53dc1adf390f1a1d4b0fd4ceadb28
|
github.com/couchbase/gomemcached a5ea6356f648fec6ab89add00edd09151455b4b2
|
||||||
github.com/couchbase/gomemcached 4a25d2f4e1dea9ea7dd76dfd943407abf9b07d29
|
|
||||||
github.com/couchbase/goutils 5823a0cbaaa9008406021dc5daf80125ea30bba6
|
github.com/couchbase/goutils 5823a0cbaaa9008406021dc5daf80125ea30bba6
|
||||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
github.com/dancannon/gorethink e7cac92ea2bc52638791a021f212145acfedb1fc
|
||||||
github.com/docker/docker b89aff1afa1f61993ab2ba18fd62d9375a195f5d
|
github.com/davecgh/go-spew 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||||
|
github.com/docker/engine-api 8924d6900370b4c7e7984be5adc61f50a80d7537
|
||||||
|
github.com/docker/go-connections f549a9393d05688dff0992ef3efd8bbe6c628aeb
|
||||||
|
github.com/docker/go-units 5d2041e26a699eaca682e2ea41c8f891e1060444
|
||||||
github.com/eapache/go-resiliency b86b1ec0dd4209a588dc1285cdd471e73525c0b3
|
github.com/eapache/go-resiliency b86b1ec0dd4209a588dc1285cdd471e73525c0b3
|
||||||
github.com/eapache/go-xerial-snappy bb955e01b9346ac19dc29eb16586c90ded99a98c
|
github.com/eapache/queue ded5959c0d4e360646dc9e9908cff48666781367
|
||||||
github.com/eapache/queue 44cc805cf13205b55f69e14bcb69867d1ae92f98
|
github.com/eclipse/paho.mqtt.golang 0f7a459f04f13a41b7ed752d47944528d4bf9a86
|
||||||
github.com/eclipse/paho.mqtt.golang d4f545eb108a2d19f9b1a735689dbfb719bc21fb
|
github.com/go-sql-driver/mysql 1fca743146605a172a266e1654e01e5cd5669bee
|
||||||
github.com/go-sql-driver/mysql 2e00b5cd70399450106cec6431c2e2ce3cae5034
|
github.com/gobwas/glob 49571a1557cd20e6a2410adc6421f85b66c730b5
|
||||||
github.com/gobwas/glob bea32b9cd2d6f55753d94a28e959b13f0244797a
|
github.com/golang/protobuf 552c7b9542c194800fd493123b3798ef0a832032
|
||||||
github.com/golang/protobuf 8ee79997227bf9b34611aee7946ae64735e6fd93
|
github.com/golang/snappy d9eb7a3d35ec988b8585d4a0068e462c27d28380
|
||||||
github.com/golang/snappy 7db9049039a047d955fe8c19b83c8ff5abd765c7
|
github.com/gorilla/context 1ea25387ff6f684839d82767c1733ff4d4d15d0a
|
||||||
github.com/gorilla/mux 392c28fe23e1c45ddba891b0320b3b5df220beea
|
github.com/gorilla/mux c9e326e2bdec29039a3761c07bece13133863e1e
|
||||||
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
|
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
|
||||||
github.com/hashicorp/consul 63d2fc68239b996096a1c55a0d4b400ea4c2583f
|
github.com/hashicorp/consul 5aa90455ce78d4d41578bafc86305e6e6b28d7d2
|
||||||
github.com/influxdata/tail a395bf99fe07c233f41fba0735fa2b13b58588ea
|
github.com/hpcloud/tail b2940955ab8b26e19d43a43c4da0475dd81bdb56
|
||||||
github.com/influxdata/toml 5d1d907f22ead1cd47adde17ceec5bda9cacaf8f
|
github.com/influxdata/config b79f6829346b8d6e78ba73544b1e1038f1f1c9da
|
||||||
|
github.com/influxdata/influxdb fc57c0f7c635df3873f3d64f0ed2100ddc94d5ae
|
||||||
|
github.com/influxdata/toml af4df43894b16e3fd2b788d01bd27ad0776ef2d0
|
||||||
github.com/influxdata/wlog 7c63b0a71ef8300adc255344d275e10e5c3a71ec
|
github.com/influxdata/wlog 7c63b0a71ef8300adc255344d275e10e5c3a71ec
|
||||||
github.com/jackc/pgx b84338d7d62598f75859b2b146d830b22f1b9ec8
|
github.com/kardianos/osext 29ae4ffbc9a6fe9fb2bc5029050ce6996ea1d3bc
|
||||||
github.com/kardianos/osext c2c54e542fb797ad986b31721e1baedf214ca413
|
github.com/kardianos/service 5e335590050d6d00f3aa270217d288dda1c94d0a
|
||||||
github.com/kardianos/service 6d3a0ee7d3425d9d835debc51a0ca1ffa28f4893
|
|
||||||
github.com/kballard/go-shellquote d8ec1a69a250a17bb0e419c386eac1f3711dc142
|
github.com/kballard/go-shellquote d8ec1a69a250a17bb0e419c386eac1f3711dc142
|
||||||
github.com/klauspost/crc32 cb6bfca970f6908083f26f39a79009d608efd5cd
|
github.com/klauspost/crc32 19b0b332c9e4516a6370a0456e6182c3b5036720
|
||||||
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c
|
github.com/lib/pq e182dc4027e2ded4b19396d638610f2653295f36
|
||||||
github.com/miekg/dns 99f84ae56e75126dd77e5de4fae2ea034a468ca1
|
github.com/matttproud/golang_protobuf_extensions d0c3fe89de86839aecf2e0579c40ba3bb336a453
|
||||||
|
github.com/miekg/dns cce6c130cdb92c752850880fd285bea1d64439dd
|
||||||
|
github.com/mreiferson/go-snappystream 028eae7ab5c4c9e2d1cb4c4ca1e53259bbe7e504
|
||||||
github.com/naoina/go-stringutil 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b
|
github.com/naoina/go-stringutil 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b
|
||||||
github.com/nats-io/go-nats ea9585611a4ab58a205b9b125ebd74c389a6b898
|
github.com/nats-io/nats ea8b4fd12ebb823073c0004b9f09ac8748f4f165
|
||||||
github.com/nats-io/nats ea9585611a4ab58a205b9b125ebd74c389a6b898
|
github.com/nats-io/nuid a5152d67cf63cbfb5d992a395458722a45194715
|
||||||
github.com/nats-io/nuid 289cccf02c178dc782430d534e3c1f5b72af807f
|
github.com/nsqio/go-nsq 0b80d6f05e15ca1930e0c5e1d540ed627e299980
|
||||||
github.com/nsqio/go-nsq a53d495e81424aaf7a7665a9d32a97715c40e953
|
github.com/opencontainers/runc 89ab7f2ccc1e45ddf6485eaa802c35dcf321dfc8
|
||||||
github.com/pierrec/lz4 5c9560bfa9ace2bf86080bf40d46b34ae44604df
|
github.com/prometheus/client_golang 18acf9993a863f4c4b40612e19cdd243e7c86831
|
||||||
github.com/pierrec/xxHash 5a004441f897722c627870a981d02b29924215fa
|
|
||||||
github.com/prometheus/client_golang c317fb74746eac4fc65fe3909195f4cf67c5562a
|
|
||||||
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
||||||
github.com/prometheus/common dd2f054febf4a6c00f2343686efb775948a8bff4
|
github.com/prometheus/common e8eabff8812b05acf522b45fdcd725a785188e37
|
||||||
github.com/prometheus/procfs 1878d9fbb537119d24b21ca07effd591627cd160
|
github.com/prometheus/procfs 406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8
|
||||||
github.com/rcrowley/go-metrics 1f30fe9094a513ce4c700b9a54458bbb0c96996c
|
github.com/samuel/go-zookeeper 218e9c81c0dd8b3b18172b2bbfad92cc7d6db55f
|
||||||
github.com/samuel/go-zookeeper 1d7be4effb13d2d908342d349d71a284a7542693
|
github.com/shirou/gopsutil 1516eb9ddc5e61ba58874047a98f8b44b5e585e8
|
||||||
github.com/satori/go.uuid 5bf94b69c6b68ee1b541973bb8e1144db23a194b
|
github.com/soniah/gosnmp 3fe3beb30fa9700988893c56a63b1df8e1b68c26
|
||||||
github.com/shirou/gopsutil 9a4a9167ad3b4355dbf1c2c7a0f5f0d3fb1e9ab9
|
github.com/streadway/amqp b4f3ceab0337f013208d31348b578d83c0064744
|
||||||
github.com/soniah/gosnmp 5ad50dc75ab389f8a1c9f8a67d3a1cd85f67ed15
|
github.com/stretchr/testify 1f4a1643a57e798696635ea4c126e9127adb7d3c
|
||||||
github.com/streadway/amqp 63795daa9a446c920826655f26ba31c81c860fd6
|
github.com/vjeantet/grok 83bfdfdfd1a8146795b28e547a8e3c8b28a466c2
|
||||||
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
|
github.com/wvanbergen/kafka 46f9a1cf3f670edec492029fadded9c2d9e18866
|
||||||
github.com/vjeantet/grok d73e972b60935c7fec0b4ffbc904ed39ecaf7efe
|
github.com/wvanbergen/kazoo-go 0f768712ae6f76454f987c3356177e138df258f8
|
||||||
github.com/wvanbergen/kafka bc265fedb9ff5b5c5d3c0fdcef4a819b3523d3ee
|
github.com/yuin/gopher-lua bf3808abd44b1e55143a2d7f08571aaa80db1808
|
||||||
github.com/wvanbergen/kazoo-go 968957352185472eacb69215fa3dbfcfdbac1096
|
|
||||||
github.com/yuin/gopher-lua 66c871e454fcf10251c61bf8eff02d0978cae75a
|
|
||||||
github.com/zensqlmonitor/go-mssqldb ffe5510c6fa5e15e6d983210ab501c815b56b363
|
github.com/zensqlmonitor/go-mssqldb ffe5510c6fa5e15e6d983210ab501c815b56b363
|
||||||
golang.org/x/crypto dc137beb6cce2043eb6b5f223ab8bf51c32459f4
|
golang.org/x/crypto c197bcf24cde29d3f73c7b4ac6fd41f4384e8af6
|
||||||
golang.org/x/net f2499483f923065a842d38eb4c7f1927e6fc6e6d
|
golang.org/x/net 6acef71eb69611914f7a30939ea9f6e194c78172
|
||||||
golang.org/x/text 506f9d5c962f284575e88337e7d9296d27e729d3
|
golang.org/x/text a71fd10341b064c10f4a81ceac72bcf70f26ea34
|
||||||
gopkg.in/dancannon/gorethink.v1 edc7a6a68e2d8015f5ffe1b2560eed989f8a45be
|
gopkg.in/dancannon/gorethink.v1 7d1af5be49cb5ecc7b177bf387d232050299d6ef
|
||||||
gopkg.in/fatih/pool.v2 6e328e67893eb46323ad06f0e92cb9536babbabc
|
gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715
|
||||||
gopkg.in/mgo.v2 3f83fa5005286a7fe593b055f0d7771a7dce4655
|
gopkg.in/mgo.v2 d90005c5262a3463800497ea5a89aed5fe22c886
|
||||||
gopkg.in/olivere/elastic.v5 ee3ebceab960cf68ab9a89ee6d78c031ef5b4a4e
|
gopkg.in/yaml.v2 a83829b6f1293c91addabc89d0571c246397bbf4
|
||||||
gopkg.in/yaml.v2 4c78c975fe7c825c6d1466c42be594d1d6f3aba6
|
|
||||||
|
|||||||
10
Makefile
10
Makefile
@@ -51,7 +51,6 @@ docker-run:
|
|||||||
-e ADVERTISED_PORT=9092 \
|
-e ADVERTISED_PORT=9092 \
|
||||||
-p "2181:2181" -p "9092:9092" \
|
-p "2181:2181" -p "9092:9092" \
|
||||||
-d spotify/kafka
|
-d spotify/kafka
|
||||||
docker run --name elasticsearch -p "9200:9200" -p "9300:9300" -d elasticsearch:5
|
|
||||||
docker run --name mysql -p "3306:3306" -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -d mysql
|
docker run --name mysql -p "3306:3306" -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -d mysql
|
||||||
docker run --name memcached -p "11211:11211" -d memcached
|
docker run --name memcached -p "11211:11211" -d memcached
|
||||||
docker run --name postgres -p "5432:5432" -d postgres
|
docker run --name postgres -p "5432:5432" -d postgres
|
||||||
@@ -59,7 +58,7 @@ docker-run:
|
|||||||
docker run --name redis -p "6379:6379" -d redis
|
docker run --name redis -p "6379:6379" -d redis
|
||||||
docker run --name nsq -p "4150:4150" -d nsqio/nsq /nsqd
|
docker run --name nsq -p "4150:4150" -d nsqio/nsq /nsqd
|
||||||
docker run --name mqtt -p "1883:1883" -d ncarlier/mqtt
|
docker run --name mqtt -p "1883:1883" -d ncarlier/mqtt
|
||||||
docker run --name riemann -p "5555:5555" -d stealthly/docker-riemann
|
docker run --name riemann -p "5555:5555" -d blalor/riemann
|
||||||
docker run --name nats -p "4222:4222" -d nats
|
docker run --name nats -p "4222:4222" -d nats
|
||||||
|
|
||||||
# Run docker containers necessary for CircleCI unit tests
|
# Run docker containers necessary for CircleCI unit tests
|
||||||
@@ -70,16 +69,15 @@ docker-run-circle:
|
|||||||
-e ADVERTISED_PORT=9092 \
|
-e ADVERTISED_PORT=9092 \
|
||||||
-p "2181:2181" -p "9092:9092" \
|
-p "2181:2181" -p "9092:9092" \
|
||||||
-d spotify/kafka
|
-d spotify/kafka
|
||||||
docker run --name elasticsearch -p "9200:9200" -p "9300:9300" -d elasticsearch:5
|
|
||||||
docker run --name nsq -p "4150:4150" -d nsqio/nsq /nsqd
|
docker run --name nsq -p "4150:4150" -d nsqio/nsq /nsqd
|
||||||
docker run --name mqtt -p "1883:1883" -d ncarlier/mqtt
|
docker run --name mqtt -p "1883:1883" -d ncarlier/mqtt
|
||||||
docker run --name riemann -p "5555:5555" -d stealthly/docker-riemann
|
docker run --name riemann -p "5555:5555" -d blalor/riemann
|
||||||
docker run --name nats -p "4222:4222" -d nats
|
docker run --name nats -p "4222:4222" -d nats
|
||||||
|
|
||||||
# Kill all docker containers, ignore errors
|
# Kill all docker containers, ignore errors
|
||||||
docker-kill:
|
docker-kill:
|
||||||
-docker kill nsq aerospike redis rabbitmq postgres memcached mysql kafka mqtt riemann nats elasticsearch
|
-docker kill nsq aerospike redis rabbitmq postgres memcached mysql kafka mqtt riemann nats
|
||||||
-docker rm nsq aerospike redis rabbitmq postgres memcached mysql kafka mqtt riemann nats elasticsearch
|
-docker rm nsq aerospike redis rabbitmq postgres memcached mysql kafka mqtt riemann nats
|
||||||
|
|
||||||
# Run full unit tests using docker containers (includes setup and teardown)
|
# Run full unit tests using docker containers (includes setup and teardown)
|
||||||
test: vet docker-kill docker-run
|
test: vet docker-kill docker-run
|
||||||
|
|||||||
94
README.md
94
README.md
@@ -25,25 +25,65 @@ new plugins.
|
|||||||
|
|
||||||
## Installation:
|
## Installation:
|
||||||
|
|
||||||
You can either download the binaries directly from the
|
### Linux deb and rpm Packages:
|
||||||
[downloads](https://www.influxdata.com/downloads) page.
|
|
||||||
|
|
||||||
A few alternate installs are available here as well:
|
Latest:
|
||||||
|
* https://dl.influxdata.com/telegraf/releases/telegraf_1.1.1_amd64.deb
|
||||||
|
* https://dl.influxdata.com/telegraf/releases/telegraf-1.1.1.x86_64.rpm
|
||||||
|
|
||||||
|
Latest (arm):
|
||||||
|
* https://dl.influxdata.com/telegraf/releases/telegraf_1.1.1_armhf.deb
|
||||||
|
* https://dl.influxdata.com/telegraf/releases/telegraf-1.1.1.armhf.rpm
|
||||||
|
|
||||||
|
##### Package Instructions:
|
||||||
|
|
||||||
|
* Telegraf binary is installed in `/usr/bin/telegraf`
|
||||||
|
* Telegraf daemon configuration file is in `/etc/telegraf/telegraf.conf`
|
||||||
|
* On sysv systems, the telegraf daemon can be controlled via
|
||||||
|
`service telegraf [action]`
|
||||||
|
* On systemd systems (such as Ubuntu 15+), the telegraf daemon can be
|
||||||
|
controlled via `systemctl [action] telegraf`
|
||||||
|
|
||||||
|
### yum/apt Repositories:
|
||||||
|
|
||||||
|
There is a yum/apt repo available for the whole InfluxData stack, see
|
||||||
|
[here](https://docs.influxdata.com/influxdb/latest/introduction/installation/#installation)
|
||||||
|
for instructions on setting up the repo. Once it is configured, you will be able
|
||||||
|
to use this repo to install & update telegraf.
|
||||||
|
|
||||||
|
### Linux tarballs:
|
||||||
|
|
||||||
|
Latest:
|
||||||
|
* https://dl.influxdata.com/telegraf/releases/telegraf-1.1.1_linux_amd64.tar.gz
|
||||||
|
* https://dl.influxdata.com/telegraf/releases/telegraf-1.1.1_linux_i386.tar.gz
|
||||||
|
* https://dl.influxdata.com/telegraf/releases/telegraf-1.1.1_linux_armhf.tar.gz
|
||||||
|
|
||||||
### FreeBSD tarball:
|
### FreeBSD tarball:
|
||||||
|
|
||||||
Latest:
|
Latest:
|
||||||
* https://dl.influxdata.com/telegraf/releases/telegraf-VERSION_freebsd_amd64.tar.gz
|
* https://dl.influxdata.com/telegraf/releases/telegraf-1.1.1_freebsd_amd64.tar.gz
|
||||||
|
|
||||||
### Ansible Role:
|
### Ansible Role:
|
||||||
|
|
||||||
Ansible role: https://github.com/rossmcdonald/telegraf
|
Ansible role: https://github.com/rossmcdonald/telegraf
|
||||||
|
|
||||||
|
### OSX via Homebrew:
|
||||||
|
|
||||||
|
```
|
||||||
|
brew update
|
||||||
|
brew install telegraf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows Binaries (EXPERIMENTAL)
|
||||||
|
|
||||||
|
Latest:
|
||||||
|
* https://dl.influxdata.com/telegraf/releases/telegraf-1.1.1_windows_amd64.zip
|
||||||
|
|
||||||
### From Source:
|
### From Source:
|
||||||
|
|
||||||
Telegraf manages dependencies via [gdm](https://github.com/sparrc/gdm),
|
Telegraf manages dependencies via [gdm](https://github.com/sparrc/gdm),
|
||||||
which gets installed via the Makefile
|
which gets installed via the Makefile
|
||||||
if you don't have it already. You also must build with golang version 1.8+.
|
if you don't have it already. You also must build with golang version 1.5+.
|
||||||
|
|
||||||
1. [Install Go](https://golang.org/doc/install)
|
1. [Install Go](https://golang.org/doc/install)
|
||||||
2. [Setup your GOPATH](https://golang.org/doc/code.html#GOPATH)
|
2. [Setup your GOPATH](https://golang.org/doc/code.html#GOPATH)
|
||||||
@@ -59,31 +99,31 @@ See usage with:
|
|||||||
telegraf --help
|
telegraf --help
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Generate a telegraf config file:
|
### Generate a telegraf config file:
|
||||||
|
|
||||||
```
|
```
|
||||||
telegraf config > telegraf.conf
|
telegraf config > telegraf.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Generate config with only cpu input & influxdb output plugins defined
|
### Generate config with only cpu input & influxdb output plugins defined
|
||||||
|
|
||||||
```
|
```
|
||||||
telegraf --input-filter cpu --output-filter influxdb config
|
telegraf --input-filter cpu --output-filter influxdb config
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Run a single telegraf collection, outputing metrics to stdout
|
### Run a single telegraf collection, outputing metrics to stdout
|
||||||
|
|
||||||
```
|
```
|
||||||
telegraf --config telegraf.conf -test
|
telegraf --config telegraf.conf -test
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Run telegraf with all plugins defined in config file
|
### Run telegraf with all plugins defined in config file
|
||||||
|
|
||||||
```
|
```
|
||||||
telegraf --config telegraf.conf
|
telegraf --config telegraf.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Run telegraf, enabling the cpu & memory input, and influxdb output plugins
|
### Run telegraf, enabling the cpu & memory input, and influxdb output plugins
|
||||||
|
|
||||||
```
|
```
|
||||||
telegraf --config telegraf.conf -input-filter cpu:mem -output-filter influxdb
|
telegraf --config telegraf.conf -input-filter cpu:mem -output-filter influxdb
|
||||||
@@ -97,21 +137,18 @@ configuration options.
|
|||||||
|
|
||||||
## Input Plugins
|
## Input Plugins
|
||||||
|
|
||||||
* [aerospike](./plugins/inputs/aerospike)
|
|
||||||
* [amqp_consumer](./plugins/inputs/amqp_consumer) (rabbitmq)
|
|
||||||
* [apache](./plugins/inputs/apache)
|
|
||||||
* [aws cloudwatch](./plugins/inputs/cloudwatch)
|
* [aws cloudwatch](./plugins/inputs/cloudwatch)
|
||||||
|
* [aerospike](./plugins/inputs/aerospike)
|
||||||
|
* [apache](./plugins/inputs/apache)
|
||||||
* [bcache](./plugins/inputs/bcache)
|
* [bcache](./plugins/inputs/bcache)
|
||||||
* [cassandra](./plugins/inputs/cassandra)
|
* [cassandra](./plugins/inputs/cassandra)
|
||||||
* [ceph](./plugins/inputs/ceph)
|
* [ceph](./plugins/inputs/ceph)
|
||||||
* [cgroup](./plugins/inputs/cgroup)
|
|
||||||
* [chrony](./plugins/inputs/chrony)
|
* [chrony](./plugins/inputs/chrony)
|
||||||
* [consul](./plugins/inputs/consul)
|
* [consul](./plugins/inputs/consul)
|
||||||
* [conntrack](./plugins/inputs/conntrack)
|
* [conntrack](./plugins/inputs/conntrack)
|
||||||
* [couchbase](./plugins/inputs/couchbase)
|
* [couchbase](./plugins/inputs/couchbase)
|
||||||
* [couchdb](./plugins/inputs/couchdb)
|
* [couchdb](./plugins/inputs/couchdb)
|
||||||
* [disque](./plugins/inputs/disque)
|
* [disque](./plugins/inputs/disque)
|
||||||
* [dmcache](./plugins/inputs/dmcache)
|
|
||||||
* [dns query time](./plugins/inputs/dns_query)
|
* [dns query time](./plugins/inputs/dns_query)
|
||||||
* [docker](./plugins/inputs/docker)
|
* [docker](./plugins/inputs/docker)
|
||||||
* [dovecot](./plugins/inputs/dovecot)
|
* [dovecot](./plugins/inputs/dovecot)
|
||||||
@@ -124,12 +161,9 @@ configuration options.
|
|||||||
* [httpjson](./plugins/inputs/httpjson) (generic JSON-emitting http service plugin)
|
* [httpjson](./plugins/inputs/httpjson) (generic JSON-emitting http service plugin)
|
||||||
* [internal](./plugins/inputs/internal)
|
* [internal](./plugins/inputs/internal)
|
||||||
* [influxdb](./plugins/inputs/influxdb)
|
* [influxdb](./plugins/inputs/influxdb)
|
||||||
* [interrupts](./plugins/inputs/interrupts)
|
|
||||||
* [ipmi_sensor](./plugins/inputs/ipmi_sensor)
|
* [ipmi_sensor](./plugins/inputs/ipmi_sensor)
|
||||||
* [iptables](./plugins/inputs/iptables)
|
* [iptables](./plugins/inputs/iptables)
|
||||||
* [jolokia](./plugins/inputs/jolokia)
|
* [jolokia](./plugins/inputs/jolokia)
|
||||||
* [kapacitor](./plugins/inputs/kapacitor)
|
|
||||||
* [kubernetes](./plugins/inputs/kubernetes)
|
|
||||||
* [leofs](./plugins/inputs/leofs)
|
* [leofs](./plugins/inputs/leofs)
|
||||||
* [lustre2](./plugins/inputs/lustre2)
|
* [lustre2](./plugins/inputs/lustre2)
|
||||||
* [mailchimp](./plugins/inputs/mailchimp)
|
* [mailchimp](./plugins/inputs/mailchimp)
|
||||||
@@ -177,7 +211,6 @@ configuration options.
|
|||||||
* processes
|
* processes
|
||||||
* kernel (/proc/stat)
|
* kernel (/proc/stat)
|
||||||
* kernel (/proc/vmstat)
|
* kernel (/proc/vmstat)
|
||||||
* linux_sysctl_fs (/proc/sys/fs)
|
|
||||||
|
|
||||||
Telegraf can also collect metrics via the following service plugins:
|
Telegraf can also collect metrics via the following service plugins:
|
||||||
|
|
||||||
@@ -188,26 +221,14 @@ Telegraf can also collect metrics via the following service plugins:
|
|||||||
* [nsq_consumer](./plugins/inputs/nsq_consumer)
|
* [nsq_consumer](./plugins/inputs/nsq_consumer)
|
||||||
* [logparser](./plugins/inputs/logparser)
|
* [logparser](./plugins/inputs/logparser)
|
||||||
* [statsd](./plugins/inputs/statsd)
|
* [statsd](./plugins/inputs/statsd)
|
||||||
* [socket_listener](./plugins/inputs/socket_listener)
|
|
||||||
* [tail](./plugins/inputs/tail)
|
* [tail](./plugins/inputs/tail)
|
||||||
* [tcp_listener](./plugins/inputs/socket_listener)
|
* [tcp_listener](./plugins/inputs/tcp_listener)
|
||||||
* [udp_listener](./plugins/inputs/socket_listener)
|
* [udp_listener](./plugins/inputs/udp_listener)
|
||||||
* [webhooks](./plugins/inputs/webhooks)
|
* [webhooks](./plugins/inputs/webhooks)
|
||||||
* [filestack](./plugins/inputs/webhooks/filestack)
|
* [filestack](./plugins/inputs/webhooks/filestack)
|
||||||
* [github](./plugins/inputs/webhooks/github)
|
* [github](./plugins/inputs/webhooks/github)
|
||||||
* [mandrill](./plugins/inputs/webhooks/mandrill)
|
* [mandrill](./plugins/inputs/webhooks/mandrill)
|
||||||
* [rollbar](./plugins/inputs/webhooks/rollbar)
|
* [rollbar](./plugins/inputs/webhooks/rollbar)
|
||||||
* [papertrail](./plugins/inputs/webhooks/papertrail)
|
|
||||||
|
|
||||||
Telegraf is able to parse the following input data formats into metrics, these
|
|
||||||
formats may be used with input plugins supporting the `data_format` option:
|
|
||||||
|
|
||||||
* [InfluxDB Line Protocol](./docs/DATA_FORMATS_INPUT.md#influx)
|
|
||||||
* [JSON](./docs/DATA_FORMATS_INPUT.md#json)
|
|
||||||
* [Graphite](./docs/DATA_FORMATS_INPUT.md#graphite)
|
|
||||||
* [Value](./docs/DATA_FORMATS_INPUT.md#value)
|
|
||||||
* [Nagios](./docs/DATA_FORMATS_INPUT.md#nagios)
|
|
||||||
* [Collectd](./docs/DATA_FORMATS_INPUT.md#collectd)
|
|
||||||
|
|
||||||
## Processor Plugins
|
## Processor Plugins
|
||||||
|
|
||||||
@@ -221,12 +242,11 @@ formats may be used with input plugins supporting the `data_format` option:
|
|||||||
|
|
||||||
* [influxdb](./plugins/outputs/influxdb)
|
* [influxdb](./plugins/outputs/influxdb)
|
||||||
* [amon](./plugins/outputs/amon)
|
* [amon](./plugins/outputs/amon)
|
||||||
* [amqp](./plugins/outputs/amqp) (rabbitmq)
|
* [amqp](./plugins/outputs/amqp)
|
||||||
* [aws kinesis](./plugins/outputs/kinesis)
|
* [aws kinesis](./plugins/outputs/kinesis)
|
||||||
* [aws cloudwatch](./plugins/outputs/cloudwatch)
|
* [aws cloudwatch](./plugins/outputs/cloudwatch)
|
||||||
* [datadog](./plugins/outputs/datadog)
|
* [datadog](./plugins/outputs/datadog)
|
||||||
* [discard](./plugins/outputs/discard)
|
* [discard](./plugins/outputs/discard)
|
||||||
* [elasticsearch](./plugins/outputs/elasticsearch)
|
|
||||||
* [file](./plugins/outputs/file)
|
* [file](./plugins/outputs/file)
|
||||||
* [graphite](./plugins/outputs/graphite)
|
* [graphite](./plugins/outputs/graphite)
|
||||||
* [graylog](./plugins/outputs/graylog)
|
* [graylog](./plugins/outputs/graylog)
|
||||||
@@ -239,10 +259,6 @@ formats may be used with input plugins supporting the `data_format` option:
|
|||||||
* [opentsdb](./plugins/outputs/opentsdb)
|
* [opentsdb](./plugins/outputs/opentsdb)
|
||||||
* [prometheus](./plugins/outputs/prometheus_client)
|
* [prometheus](./plugins/outputs/prometheus_client)
|
||||||
* [riemann](./plugins/outputs/riemann)
|
* [riemann](./plugins/outputs/riemann)
|
||||||
* [riemann_legacy](./plugins/outputs/riemann_legacy)
|
|
||||||
* [socket_writer](./plugins/outputs/socket_writer)
|
|
||||||
* [tcp](./plugins/outputs/socket_writer)
|
|
||||||
* [udp](./plugins/outputs/socket_writer)
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/selfstat"
|
"github.com/influxdata/telegraf/selfstat"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,14 +18,14 @@ type MetricMaker interface {
|
|||||||
measurement string,
|
measurement string,
|
||||||
fields map[string]interface{},
|
fields map[string]interface{},
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
mType telegraf.ValueType,
|
mType plugins.ValueType,
|
||||||
t time.Time,
|
t time.Time,
|
||||||
) telegraf.Metric
|
) plugins.Metric
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccumulator(
|
func NewAccumulator(
|
||||||
maker MetricMaker,
|
maker MetricMaker,
|
||||||
metrics chan telegraf.Metric,
|
metrics chan plugins.Metric,
|
||||||
) *accumulator {
|
) *accumulator {
|
||||||
acc := accumulator{
|
acc := accumulator{
|
||||||
maker: maker,
|
maker: maker,
|
||||||
@@ -36,7 +36,7 @@ func NewAccumulator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
type accumulator struct {
|
type accumulator struct {
|
||||||
metrics chan telegraf.Metric
|
metrics chan plugins.Metric
|
||||||
|
|
||||||
maker MetricMaker
|
maker MetricMaker
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ func (ac *accumulator) AddFields(
|
|||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
t ...time.Time,
|
t ...time.Time,
|
||||||
) {
|
) {
|
||||||
if m := ac.maker.MakeMetric(measurement, fields, tags, telegraf.Untyped, ac.getTime(t)); m != nil {
|
if m := ac.maker.MakeMetric(measurement, fields, tags, plugins.Untyped, ac.getTime(t)); m != nil {
|
||||||
ac.metrics <- m
|
ac.metrics <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ func (ac *accumulator) AddGauge(
|
|||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
t ...time.Time,
|
t ...time.Time,
|
||||||
) {
|
) {
|
||||||
if m := ac.maker.MakeMetric(measurement, fields, tags, telegraf.Gauge, ac.getTime(t)); m != nil {
|
if m := ac.maker.MakeMetric(measurement, fields, tags, plugins.Gauge, ac.getTime(t)); m != nil {
|
||||||
ac.metrics <- m
|
ac.metrics <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ func (ac *accumulator) AddCounter(
|
|||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
t ...time.Time,
|
t ...time.Time,
|
||||||
) {
|
) {
|
||||||
if m := ac.maker.MakeMetric(measurement, fields, tags, telegraf.Counter, ac.getTime(t)); m != nil {
|
if m := ac.maker.MakeMetric(measurement, fields, tags, plugins.Counter, ac.getTime(t)); m != nil {
|
||||||
ac.metrics <- m
|
ac.metrics <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/metric"
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
func TestAdd(t *testing.T) {
|
func TestAdd(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ func TestAdd(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddFields(t *testing.T) {
|
func TestAddFields(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ func TestAccAddError(t *testing.T) {
|
|||||||
log.SetOutput(errBuf)
|
log.SetOutput(errBuf)
|
||||||
defer log.SetOutput(os.Stderr)
|
defer log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ func TestAccAddError(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddNoIntervalWithPrecision(t *testing.T) {
|
func TestAddNoIntervalWithPrecision(t *testing.T) {
|
||||||
now := time.Date(2006, time.February, 10, 12, 0, 0, 82912748, time.UTC)
|
now := time.Date(2006, time.February, 10, 12, 0, 0, 82912748, time.UTC)
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
a.SetPrecision(0, time.Second)
|
a.SetPrecision(0, time.Second)
|
||||||
@@ -132,7 +132,7 @@ func TestAddNoIntervalWithPrecision(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddDisablePrecision(t *testing.T) {
|
func TestAddDisablePrecision(t *testing.T) {
|
||||||
now := time.Date(2006, time.February, 10, 12, 0, 0, 82912748, time.UTC)
|
now := time.Date(2006, time.February, 10, 12, 0, 0, 82912748, time.UTC)
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ func TestAddDisablePrecision(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddNoPrecisionWithInterval(t *testing.T) {
|
func TestAddNoPrecisionWithInterval(t *testing.T) {
|
||||||
now := time.Date(2006, time.February, 10, 12, 0, 0, 82912748, time.UTC)
|
now := time.Date(2006, time.February, 10, 12, 0, 0, 82912748, time.UTC)
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ func TestAddNoPrecisionWithInterval(t *testing.T) {
|
|||||||
|
|
||||||
func TestDifferentPrecisions(t *testing.T) {
|
func TestDifferentPrecisions(t *testing.T) {
|
||||||
now := time.Date(2006, time.February, 10, 12, 0, 0, 82912748, time.UTC)
|
now := time.Date(2006, time.February, 10, 12, 0, 0, 82912748, time.UTC)
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ func TestDifferentPrecisions(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddGauge(t *testing.T) {
|
func TestAddGauge(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
|
|
||||||
@@ -260,24 +260,24 @@ func TestAddGauge(t *testing.T) {
|
|||||||
testm := <-metrics
|
testm := <-metrics
|
||||||
actual := testm.String()
|
actual := testm.String()
|
||||||
assert.Contains(t, actual, "acctest value=101")
|
assert.Contains(t, actual, "acctest value=101")
|
||||||
assert.Equal(t, testm.Type(), telegraf.Gauge)
|
assert.Equal(t, testm.Type(), plugins.Gauge)
|
||||||
|
|
||||||
testm = <-metrics
|
testm = <-metrics
|
||||||
actual = testm.String()
|
actual = testm.String()
|
||||||
assert.Contains(t, actual, "acctest,acc=test value=101")
|
assert.Contains(t, actual, "acctest,acc=test value=101")
|
||||||
assert.Equal(t, testm.Type(), telegraf.Gauge)
|
assert.Equal(t, testm.Type(), plugins.Gauge)
|
||||||
|
|
||||||
testm = <-metrics
|
testm = <-metrics
|
||||||
actual = testm.String()
|
actual = testm.String()
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
fmt.Sprintf("acctest,acc=test value=101 %d\n", now.UnixNano()),
|
fmt.Sprintf("acctest,acc=test value=101 %d\n", now.UnixNano()),
|
||||||
actual)
|
actual)
|
||||||
assert.Equal(t, testm.Type(), telegraf.Gauge)
|
assert.Equal(t, testm.Type(), plugins.Gauge)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddCounter(t *testing.T) {
|
func TestAddCounter(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
metrics := make(chan telegraf.Metric, 10)
|
metrics := make(chan plugins.Metric, 10)
|
||||||
defer close(metrics)
|
defer close(metrics)
|
||||||
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
a := NewAccumulator(&TestMetricMaker{}, metrics)
|
||||||
|
|
||||||
@@ -294,19 +294,19 @@ func TestAddCounter(t *testing.T) {
|
|||||||
testm := <-metrics
|
testm := <-metrics
|
||||||
actual := testm.String()
|
actual := testm.String()
|
||||||
assert.Contains(t, actual, "acctest value=101")
|
assert.Contains(t, actual, "acctest value=101")
|
||||||
assert.Equal(t, testm.Type(), telegraf.Counter)
|
assert.Equal(t, testm.Type(), plugins.Counter)
|
||||||
|
|
||||||
testm = <-metrics
|
testm = <-metrics
|
||||||
actual = testm.String()
|
actual = testm.String()
|
||||||
assert.Contains(t, actual, "acctest,acc=test value=101")
|
assert.Contains(t, actual, "acctest,acc=test value=101")
|
||||||
assert.Equal(t, testm.Type(), telegraf.Counter)
|
assert.Equal(t, testm.Type(), plugins.Counter)
|
||||||
|
|
||||||
testm = <-metrics
|
testm = <-metrics
|
||||||
actual = testm.String()
|
actual = testm.String()
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
fmt.Sprintf("acctest,acc=test value=101 %d\n", now.UnixNano()),
|
fmt.Sprintf("acctest,acc=test value=101 %d\n", now.UnixNano()),
|
||||||
actual)
|
actual)
|
||||||
assert.Equal(t, testm.Type(), telegraf.Counter)
|
assert.Equal(t, testm.Type(), plugins.Counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestMetricMaker struct {
|
type TestMetricMaker struct {
|
||||||
@@ -319,20 +319,20 @@ func (tm *TestMetricMaker) MakeMetric(
|
|||||||
measurement string,
|
measurement string,
|
||||||
fields map[string]interface{},
|
fields map[string]interface{},
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
mType telegraf.ValueType,
|
mType plugins.ValueType,
|
||||||
t time.Time,
|
t time.Time,
|
||||||
) telegraf.Metric {
|
) plugins.Metric {
|
||||||
switch mType {
|
switch mType {
|
||||||
case telegraf.Untyped:
|
case plugins.Untyped:
|
||||||
if m, err := metric.New(measurement, tags, fields, t); err == nil {
|
if m, err := metric.New(measurement, tags, fields, t); err == nil {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
case telegraf.Counter:
|
case plugins.Counter:
|
||||||
if m, err := metric.New(measurement, tags, fields, t, telegraf.Counter); err == nil {
|
if m, err := metric.New(measurement, tags, fields, t, plugins.Counter); err == nil {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
case telegraf.Gauge:
|
case plugins.Gauge:
|
||||||
if m, err := metric.New(measurement, tags, fields, t, telegraf.Gauge); err == nil {
|
if m, err := metric.New(measurement, tags, fields, t, plugins.Gauge); err == nil {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/internal/config"
|
"github.com/influxdata/telegraf/internal/config"
|
||||||
"github.com/influxdata/telegraf/internal/models"
|
"github.com/influxdata/telegraf/internal/models"
|
||||||
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/selfstat"
|
"github.com/influxdata/telegraf/selfstat"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ func NewAgent(config *config.Config) (*Agent, error) {
|
|||||||
func (a *Agent) Connect() error {
|
func (a *Agent) Connect() error {
|
||||||
for _, o := range a.Config.Outputs {
|
for _, o := range a.Config.Outputs {
|
||||||
switch ot := o.Output.(type) {
|
switch ot := o.Output.(type) {
|
||||||
case telegraf.ServiceOutput:
|
case plugins.ServiceOutput:
|
||||||
if err := ot.Start(); err != nil {
|
if err := ot.Start(); err != nil {
|
||||||
log.Printf("E! Service for output %s failed to start, exiting\n%s\n",
|
log.Printf("E! Service for output %s failed to start, exiting\n%s\n",
|
||||||
o.Name, err.Error())
|
o.Name, err.Error())
|
||||||
@@ -76,7 +76,7 @@ func (a *Agent) Close() error {
|
|||||||
for _, o := range a.Config.Outputs {
|
for _, o := range a.Config.Outputs {
|
||||||
err = o.Output.Close()
|
err = o.Output.Close()
|
||||||
switch ot := o.Output.(type) {
|
switch ot := o.Output.(type) {
|
||||||
case telegraf.ServiceOutput:
|
case plugins.ServiceOutput:
|
||||||
ot.Stop()
|
ot.Stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,7 @@ func (a *Agent) gatherer(
|
|||||||
shutdown chan struct{},
|
shutdown chan struct{},
|
||||||
input *models.RunningInput,
|
input *models.RunningInput,
|
||||||
interval time.Duration,
|
interval time.Duration,
|
||||||
metricC chan telegraf.Metric,
|
metricC chan plugins.Metric,
|
||||||
) {
|
) {
|
||||||
defer panicRecover(input)
|
defer panicRecover(input)
|
||||||
|
|
||||||
@@ -157,13 +157,13 @@ func gatherWithTimeout(
|
|||||||
select {
|
select {
|
||||||
case err := <-done:
|
case err := <-done:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(err)
|
log.Printf("E! ERROR in input [%s]: %s", input.Name(), err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
err := fmt.Errorf("took longer to collect than collection interval (%s)",
|
log.Printf("E! ERROR: input [%s] took longer to collect than "+
|
||||||
timeout)
|
"collection interval (%s)",
|
||||||
acc.AddError(err)
|
input.Name(), timeout)
|
||||||
continue
|
continue
|
||||||
case <-shutdown:
|
case <-shutdown:
|
||||||
return
|
return
|
||||||
@@ -176,7 +176,7 @@ func gatherWithTimeout(
|
|||||||
func (a *Agent) Test() error {
|
func (a *Agent) Test() error {
|
||||||
shutdown := make(chan struct{})
|
shutdown := make(chan struct{})
|
||||||
defer close(shutdown)
|
defer close(shutdown)
|
||||||
metricC := make(chan telegraf.Metric)
|
metricC := make(chan plugins.Metric)
|
||||||
|
|
||||||
// dummy receiver for the point channel
|
// dummy receiver for the point channel
|
||||||
go func() {
|
go func() {
|
||||||
@@ -191,12 +191,6 @@ func (a *Agent) Test() error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
for _, input := range a.Config.Inputs {
|
for _, input := range a.Config.Inputs {
|
||||||
if _, ok := input.Input.(telegraf.ServiceInput); ok {
|
|
||||||
fmt.Printf("\nWARNING: skipping plugin [[%s]]: service inputs not supported in --test mode\n",
|
|
||||||
input.Name())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := NewAccumulator(input, metricC)
|
acc := NewAccumulator(input, metricC)
|
||||||
acc.SetPrecision(a.Config.Agent.Precision.Duration,
|
acc.SetPrecision(a.Config.Agent.Precision.Duration,
|
||||||
a.Config.Agent.Interval.Duration)
|
a.Config.Agent.Interval.Duration)
|
||||||
@@ -215,7 +209,7 @@ func (a *Agent) Test() error {
|
|||||||
// Special instructions for some inputs. cpu, for example, needs to be
|
// Special instructions for some inputs. cpu, for example, needs to be
|
||||||
// run twice in order to return cpu usage percentages.
|
// run twice in order to return cpu usage percentages.
|
||||||
switch input.Name() {
|
switch input.Name() {
|
||||||
case "inputs.cpu", "inputs.mongodb", "inputs.procstat":
|
case "cpu", "mongodb", "procstat":
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
fmt.Printf("* Plugin: %s, Collection 2\n", input.Name())
|
fmt.Printf("* Plugin: %s, Collection 2\n", input.Name())
|
||||||
if err := input.Input.Gather(acc); err != nil {
|
if err := input.Input.Gather(acc); err != nil {
|
||||||
@@ -247,14 +241,14 @@ func (a *Agent) flush() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// flusher monitors the metrics input channel and flushes on the minimum interval
|
// flusher monitors the metrics input channel and flushes on the minimum interval
|
||||||
func (a *Agent) flusher(shutdown chan struct{}, metricC chan telegraf.Metric) error {
|
func (a *Agent) flusher(shutdown chan struct{}, metricC chan plugins.Metric) error {
|
||||||
// Inelegant, but this sleep is to allow the Gather threads to run, so that
|
// Inelegant, but this sleep is to allow the Gather threads to run, so that
|
||||||
// the flusher will flush after metrics are collected.
|
// the flusher will flush after metrics are collected.
|
||||||
time.Sleep(time.Millisecond * 300)
|
time.Sleep(time.Millisecond * 300)
|
||||||
|
|
||||||
// create an output metric channel and a gorouting that continously passes
|
// create an output metric channel and a gorouting that continously passes
|
||||||
// each metric onto the output plugins & aggregators.
|
// each metric onto the output plugins & aggregators.
|
||||||
outMetricC := make(chan telegraf.Metric, 100)
|
outMetricC := make(chan plugins.Metric, 100)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -292,7 +286,6 @@ func (a *Agent) flusher(shutdown chan struct{}, metricC chan telegraf.Metric) er
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
ticker := time.NewTicker(a.Config.Agent.FlushInterval.Duration)
|
ticker := time.NewTicker(a.Config.Agent.FlushInterval.Duration)
|
||||||
semaphore := make(chan struct{}, 1)
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-shutdown:
|
case <-shutdown:
|
||||||
@@ -302,22 +295,12 @@ func (a *Agent) flusher(shutdown chan struct{}, metricC chan telegraf.Metric) er
|
|||||||
a.flush()
|
a.flush()
|
||||||
return nil
|
return nil
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case semaphore <- struct{}{}:
|
|
||||||
internal.RandomSleep(a.Config.Agent.FlushJitter.Duration, shutdown)
|
internal.RandomSleep(a.Config.Agent.FlushJitter.Duration, shutdown)
|
||||||
a.flush()
|
a.flush()
|
||||||
<-semaphore
|
|
||||||
default:
|
|
||||||
// skipping this flush because one is already happening
|
|
||||||
log.Println("W! Skipping a scheduled flush because there is" +
|
|
||||||
" already a flush ongoing.")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
case metric := <-metricC:
|
case metric := <-metricC:
|
||||||
// NOTE potential bottleneck here as we put each metric through the
|
// NOTE potential bottleneck here as we put each metric through the
|
||||||
// processors serially.
|
// processors serially.
|
||||||
mS := []telegraf.Metric{metric}
|
mS := []plugins.Metric{metric}
|
||||||
for _, processor := range a.Config.Processors {
|
for _, processor := range a.Config.Processors {
|
||||||
mS = processor.Apply(mS...)
|
mS = processor.Apply(mS...)
|
||||||
}
|
}
|
||||||
@@ -338,13 +321,13 @@ func (a *Agent) Run(shutdown chan struct{}) error {
|
|||||||
a.Config.Agent.Hostname, a.Config.Agent.FlushInterval.Duration)
|
a.Config.Agent.Hostname, a.Config.Agent.FlushInterval.Duration)
|
||||||
|
|
||||||
// channel shared between all input threads for accumulating metrics
|
// channel shared between all input threads for accumulating metrics
|
||||||
metricC := make(chan telegraf.Metric, 100)
|
metricC := make(chan plugins.Metric, 100)
|
||||||
|
|
||||||
// Start all ServicePlugins
|
// Start all ServicePlugins
|
||||||
for _, input := range a.Config.Inputs {
|
for _, input := range a.Config.Inputs {
|
||||||
input.SetDefaultTags(a.Config.Tags)
|
input.SetDefaultTags(a.Config.Tags)
|
||||||
switch p := input.Input.(type) {
|
switch p := input.Input.(type) {
|
||||||
case telegraf.ServiceInput:
|
case plugins.ServiceInput:
|
||||||
acc := NewAccumulator(input, metricC)
|
acc := NewAccumulator(input, metricC)
|
||||||
// Service input plugins should set their own precision of their
|
// Service input plugins should set their own precision of their
|
||||||
// metrics.
|
// metrics.
|
||||||
@@ -398,6 +381,5 @@ func (a *Agent) Run(shutdown chan struct{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
a.Close()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
12
circle.yml
12
circle.yml
@@ -1,11 +1,13 @@
|
|||||||
machine:
|
machine:
|
||||||
go:
|
|
||||||
version: 1.8.1
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
- memcached
|
post:
|
||||||
- redis
|
- sudo service zookeeper stop
|
||||||
- rabbitmq-server
|
- go version
|
||||||
|
- go version | grep 1.7.4 || sudo rm -rf /usr/local/go
|
||||||
|
- wget https://storage.googleapis.com/golang/go1.8beta1.linux-amd64.tar.gz
|
||||||
|
- sudo tar -C /usr/local -xzf go1.8beta1.linux-amd64.tar.gz
|
||||||
|
- go version
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
override:
|
override:
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
_ "net/http/pprof" // Comment this line to disable pprof endpoint.
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"plugin"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -15,19 +16,20 @@ import (
|
|||||||
"github.com/influxdata/telegraf/agent"
|
"github.com/influxdata/telegraf/agent"
|
||||||
"github.com/influxdata/telegraf/internal/config"
|
"github.com/influxdata/telegraf/internal/config"
|
||||||
"github.com/influxdata/telegraf/logger"
|
"github.com/influxdata/telegraf/logger"
|
||||||
|
"github.com/influxdata/telegraf/plugins"
|
||||||
|
"github.com/influxdata/telegraf/plugins/aggregators"
|
||||||
_ "github.com/influxdata/telegraf/plugins/aggregators/all"
|
_ "github.com/influxdata/telegraf/plugins/aggregators/all"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/all"
|
_ "github.com/influxdata/telegraf/plugins/inputs/all"
|
||||||
"github.com/influxdata/telegraf/plugins/outputs"
|
"github.com/influxdata/telegraf/plugins/outputs"
|
||||||
_ "github.com/influxdata/telegraf/plugins/outputs/all"
|
_ "github.com/influxdata/telegraf/plugins/outputs/all"
|
||||||
|
"github.com/influxdata/telegraf/plugins/processors"
|
||||||
_ "github.com/influxdata/telegraf/plugins/processors/all"
|
_ "github.com/influxdata/telegraf/plugins/processors/all"
|
||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fDebug = flag.Bool("debug", false,
|
var fDebug = flag.Bool("debug", false,
|
||||||
"turn on debug logging")
|
"turn on debug logging")
|
||||||
var pprofAddr = flag.String("pprof-addr", "",
|
|
||||||
"pprof address to listen on, not activate pprof if empty")
|
|
||||||
var fQuiet = flag.Bool("quiet", false,
|
var fQuiet = flag.Bool("quiet", false,
|
||||||
"run in quiet mode")
|
"run in quiet mode")
|
||||||
var fTest = flag.Bool("test", false, "gather metrics, print them out, and exit")
|
var fTest = flag.Bool("test", false, "gather metrics, print them out, and exit")
|
||||||
@@ -54,6 +56,8 @@ var fUsage = flag.String("usage", "",
|
|||||||
"print usage for a plugin, ie, 'telegraf -usage mysql'")
|
"print usage for a plugin, ie, 'telegraf -usage mysql'")
|
||||||
var fService = flag.String("service", "",
|
var fService = flag.String("service", "",
|
||||||
"operate on the service")
|
"operate on the service")
|
||||||
|
var fPlugins = flag.String("plugins", "",
|
||||||
|
"path to directory containing external plugins")
|
||||||
|
|
||||||
// Telegraf version, populated linker.
|
// Telegraf version, populated linker.
|
||||||
// ie, -ldflags "-X main.version=`git describe --always --tags`"
|
// ie, -ldflags "-X main.version=`git describe --always --tags`"
|
||||||
@@ -91,7 +95,6 @@ The commands & flags are:
|
|||||||
--output-filter filter the output 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'
|
--usage print usage for a plugin, ie, 'telegraf --usage mysql'
|
||||||
--debug print metrics as they're generated to stdout
|
--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
|
--quiet run in quiet mode
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
@@ -110,196 +113,47 @@ Examples:
|
|||||||
|
|
||||||
# run telegraf, enabling the cpu & memory input, and influxdb output plugins
|
# run telegraf, enabling the cpu & memory input, and influxdb output plugins
|
||||||
telegraf --config telegraf.conf --input-filter cpu:mem --output-filter influxdb
|
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{}
|
var stop chan struct{}
|
||||||
|
|
||||||
func reloadLoop(
|
var srvc service.Service
|
||||||
stop chan struct{},
|
|
||||||
inputFilters []string,
|
type program struct{}
|
||||||
outputFilters []string,
|
|
||||||
aggregatorFilters []string,
|
func reloadLoop(stop chan struct{}, s service.Service) {
|
||||||
processorFilters []string,
|
defer func() {
|
||||||
) {
|
if service.Interactive() {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}()
|
||||||
reload := make(chan bool, 1)
|
reload := make(chan bool, 1)
|
||||||
reload <- true
|
reload <- true
|
||||||
for <-reload {
|
for <-reload {
|
||||||
reload <- false
|
reload <- false
|
||||||
|
|
||||||
// If no other options are specified, load the config file and run.
|
|
||||||
c := config.NewConfig()
|
|
||||||
c.OutputFilters = outputFilters
|
|
||||||
c.InputFilters = inputFilters
|
|
||||||
err := c.LoadConfig(*fConfig)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("E! " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if *fConfigDirectory != "" {
|
|
||||||
err = c.LoadDirectory(*fConfigDirectory)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("E! " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !*fTest && len(c.Outputs) == 0 {
|
|
||||||
log.Fatalf("E! Error: no outputs found, did you provide a valid config file?")
|
|
||||||
}
|
|
||||||
if len(c.Inputs) == 0 {
|
|
||||||
log.Fatalf("E! Error: no inputs found, did you provide a valid config file?")
|
|
||||||
}
|
|
||||||
|
|
||||||
if int64(c.Agent.Interval.Duration) <= 0 {
|
|
||||||
log.Fatalf("E! Agent interval must be positive, found %s",
|
|
||||||
c.Agent.Interval.Duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
if int64(c.Agent.FlushInterval.Duration) <= 0 {
|
|
||||||
log.Fatalf("E! Agent flush_interval must be positive; found %s",
|
|
||||||
c.Agent.Interval.Duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
ag, err := agent.NewAgent(c)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("E! " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup logging
|
|
||||||
logger.SetupLogging(
|
|
||||||
ag.Config.Agent.Debug || *fDebug,
|
|
||||||
ag.Config.Agent.Quiet || *fQuiet,
|
|
||||||
ag.Config.Agent.Logfile,
|
|
||||||
)
|
|
||||||
|
|
||||||
if *fTest {
|
|
||||||
err = ag.Test()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("E! " + err.Error())
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ag.Connect()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("E! " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown := make(chan struct{})
|
|
||||||
signals := make(chan os.Signal)
|
|
||||||
signal.Notify(signals, os.Interrupt, syscall.SIGHUP)
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case sig := <-signals:
|
|
||||||
if sig == os.Interrupt {
|
|
||||||
close(shutdown)
|
|
||||||
}
|
|
||||||
if sig == syscall.SIGHUP {
|
|
||||||
log.Printf("I! Reloading Telegraf config\n")
|
|
||||||
<-reload
|
|
||||||
reload <- true
|
|
||||||
close(shutdown)
|
|
||||||
}
|
|
||||||
case <-stop:
|
|
||||||
close(shutdown)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Printf("I! Starting Telegraf (version %s)\n", version)
|
|
||||||
log.Printf("I! Loaded outputs: %s", strings.Join(c.OutputNames(), " "))
|
|
||||||
log.Printf("I! Loaded inputs: %s", strings.Join(c.InputNames(), " "))
|
|
||||||
log.Printf("I! Tags enabled: %s", c.ListTags())
|
|
||||||
|
|
||||||
if *fPidfile != "" {
|
|
||||||
f, err := os.OpenFile(*fPidfile, os.O_CREATE|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("E! Unable to create pidfile: %s", err)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(f, "%d\n", os.Getpid())
|
|
||||||
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
err := os.Remove(*fPidfile)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("E! Unable to remove pidfile: %s", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ag.Run(shutdown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func usageExit(rc int) {
|
|
||||||
fmt.Println(usage)
|
|
||||||
os.Exit(rc)
|
|
||||||
}
|
|
||||||
|
|
||||||
type program struct {
|
|
||||||
inputFilters []string
|
|
||||||
outputFilters []string
|
|
||||||
aggregatorFilters []string
|
|
||||||
processorFilters []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *program) Start(s service.Service) error {
|
|
||||||
go p.run()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (p *program) run() {
|
|
||||||
stop = make(chan struct{})
|
|
||||||
reloadLoop(
|
|
||||||
stop,
|
|
||||||
p.inputFilters,
|
|
||||||
p.outputFilters,
|
|
||||||
p.aggregatorFilters,
|
|
||||||
p.processorFilters,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
func (p *program) Stop(s service.Service) error {
|
|
||||||
close(stop)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Usage = func() { usageExit(0) }
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
|
|
||||||
inputFilters, outputFilters := []string{}, []string{}
|
var inputFilters []string
|
||||||
if *fInputFilters != "" {
|
if *fInputFilters != "" {
|
||||||
inputFilters = strings.Split(":"+strings.TrimSpace(*fInputFilters)+":", ":")
|
inputFilter := strings.TrimSpace(*fInputFilters)
|
||||||
|
inputFilters = strings.Split(":"+inputFilter+":", ":")
|
||||||
}
|
}
|
||||||
|
var outputFilters []string
|
||||||
if *fOutputFilters != "" {
|
if *fOutputFilters != "" {
|
||||||
outputFilters = strings.Split(":"+strings.TrimSpace(*fOutputFilters)+":", ":")
|
outputFilter := strings.TrimSpace(*fOutputFilters)
|
||||||
|
outputFilters = strings.Split(":"+outputFilter+":", ":")
|
||||||
}
|
}
|
||||||
|
var aggregatorFilters []string
|
||||||
aggregatorFilters, processorFilters := []string{}, []string{}
|
|
||||||
if *fAggregatorFilters != "" {
|
if *fAggregatorFilters != "" {
|
||||||
aggregatorFilters = strings.Split(":"+strings.TrimSpace(*fAggregatorFilters)+":", ":")
|
aggregatorFilter := strings.TrimSpace(*fAggregatorFilters)
|
||||||
|
aggregatorFilters = strings.Split(":"+aggregatorFilter+":", ":")
|
||||||
}
|
}
|
||||||
|
var processorFilters []string
|
||||||
if *fProcessorFilters != "" {
|
if *fProcessorFilters != "" {
|
||||||
processorFilters = strings.Split(":"+strings.TrimSpace(*fProcessorFilters)+":", ":")
|
processorFilter := strings.TrimSpace(*fProcessorFilters)
|
||||||
}
|
processorFilters = strings.Split(":"+processorFilter+":", ":")
|
||||||
|
|
||||||
if *pprofAddr != "" {
|
|
||||||
go func() {
|
|
||||||
pprofHostPort := *pprofAddr
|
|
||||||
parts := strings.Split(pprofHostPort, ":")
|
|
||||||
if len(parts) == 2 && parts[0] == "" {
|
|
||||||
pprofHostPort = fmt.Sprintf("localhost:%s", parts[1])
|
|
||||||
}
|
|
||||||
pprofHostPort = "http://" + pprofHostPort + "/debug/pprof"
|
|
||||||
|
|
||||||
log.Printf("I! Starting pprof HTTP server at: %s", pprofHostPort)
|
|
||||||
|
|
||||||
if err := http.ListenAndServe(*pprofAddr, nil); err != nil {
|
|
||||||
log.Fatal("E! " + err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
@@ -344,14 +198,207 @@ func main() {
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
case *fUsage != "":
|
case *fUsage != "":
|
||||||
err := config.PrintInputConfig(*fUsage)
|
if err := config.PrintInputConfig(*fUsage); err != nil {
|
||||||
err2 := config.PrintOutputConfig(*fUsage)
|
if err2 := config.PrintOutputConfig(*fUsage); err2 != nil {
|
||||||
if err != nil && err2 != nil {
|
|
||||||
log.Fatalf("E! %s and %s", err, err2)
|
log.Fatalf("E! %s and %s", err, err2)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no other options are specified, load the config file and run.
|
||||||
|
c := config.NewConfig()
|
||||||
|
c.OutputFilters = outputFilters
|
||||||
|
c.InputFilters = inputFilters
|
||||||
|
err := c.LoadConfig(*fConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("E! " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if *fConfigDirectory != "" {
|
||||||
|
err = c.LoadDirectory(*fConfigDirectory)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("E! " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(c.Outputs) == 0 {
|
||||||
|
log.Fatalf("E! Error: no outputs found, did you provide a valid config file?")
|
||||||
|
}
|
||||||
|
if len(c.Inputs) == 0 {
|
||||||
|
log.Fatalf("E! Error: no inputs found, did you provide a valid config file?")
|
||||||
|
}
|
||||||
|
|
||||||
|
ag, err := agent.NewAgent(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("E! " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup logging
|
||||||
|
logger.SetupLogging(
|
||||||
|
ag.Config.Agent.Debug || *fDebug,
|
||||||
|
ag.Config.Agent.Quiet || *fQuiet,
|
||||||
|
ag.Config.Agent.Logfile,
|
||||||
|
)
|
||||||
|
|
||||||
|
if *fTest {
|
||||||
|
err = ag.Test()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("E! " + err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ag.Connect()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("E! " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown := make(chan struct{})
|
||||||
|
signals := make(chan os.Signal)
|
||||||
|
signal.Notify(signals, os.Interrupt, syscall.SIGHUP)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case sig := <-signals:
|
||||||
|
if sig == os.Interrupt {
|
||||||
|
close(shutdown)
|
||||||
|
}
|
||||||
|
if sig == syscall.SIGHUP {
|
||||||
|
log.Printf("I! Reloading Telegraf config\n")
|
||||||
|
<-reload
|
||||||
|
reload <- true
|
||||||
|
close(shutdown)
|
||||||
|
}
|
||||||
|
case <-stop:
|
||||||
|
close(shutdown)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Printf("I! Starting Telegraf (version %s)\n", version)
|
||||||
|
log.Printf("I! Loaded outputs: %s", strings.Join(c.OutputNames(), " "))
|
||||||
|
log.Printf("I! Loaded inputs: %s", strings.Join(c.InputNames(), " "))
|
||||||
|
log.Printf("I! Tags enabled: %s", c.ListTags())
|
||||||
|
|
||||||
|
if *fPidfile != "" {
|
||||||
|
f, err := os.Create(*fPidfile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("E! Unable to create pidfile: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "%d\n", os.Getpid())
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
ag.Run(shutdown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func usageExit(rc int) {
|
||||||
|
fmt.Println(usage)
|
||||||
|
os.Exit(rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) Start(s service.Service) error {
|
||||||
|
srvc = s
|
||||||
|
go p.run()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (p *program) run() {
|
||||||
|
stop = make(chan struct{})
|
||||||
|
reloadLoop(stop, srvc)
|
||||||
|
}
|
||||||
|
func (p *program) Stop(s service.Service) error {
|
||||||
|
close(stop)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadExternalPlugins loads external plugins from shared libraries (.so, .dll, etc.)
|
||||||
|
// in the specified directory.
|
||||||
|
func loadExternalPlugins(dir string) error {
|
||||||
|
return filepath.Walk(dir, func(pth string, info os.FileInfo, err error) error {
|
||||||
|
// Stop if there was an error.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore directories.
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore files that aren't shared libraries.
|
||||||
|
ext := strings.ToLower(path.Ext(pth))
|
||||||
|
if ext != ".so" && ext != ".dll" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load plugin.
|
||||||
|
p, err := plugin.Open(pth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register plugin.
|
||||||
|
if err := registerPlugin(dir, pth, p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerPlugin registers an external plugin with telegraf.
|
||||||
|
func registerPlugin(pluginsDir, filePath string, p *plugin.Plugin) error {
|
||||||
|
// Clean the file path and make sure it's relative to the root plugins directory.
|
||||||
|
// This is done because plugin names are namespaced using the directory
|
||||||
|
// structure. E.g., if the root plugin directory, passed in the pluginsDir
|
||||||
|
// argument, is '/home/jdoe/bin/telegraf/plugins' and we're registering plugin
|
||||||
|
// '/home/jdoe/bin/telegraf/plugins/input/mysql.so'
|
||||||
|
pluginsDir = filepath.Clean(pluginsDir)
|
||||||
|
parentDir, _ := filepath.Split(pluginsDir)
|
||||||
|
var err error
|
||||||
|
if filePath, err = filepath.Rel(parentDir, filePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Strip the file extension and save it.
|
||||||
|
ext := path.Ext(filePath)
|
||||||
|
filePath = strings.TrimSuffix(filePath, ext)
|
||||||
|
// Convert path separators to "." to generate a plugin name namespaced by directory names.
|
||||||
|
name := strings.Replace(filePath, string(os.PathSeparator), ".", -1)
|
||||||
|
|
||||||
|
if create, err := p.Lookup("NewInput"); err == nil {
|
||||||
|
inputs.Add(name, inputs.Creator(create.(func() plugins.Input)))
|
||||||
|
} else if create, err := p.Lookup("NewOutput"); err == nil {
|
||||||
|
outputs.Add(name, outputs.Creator(create.(func() plugins.Output)))
|
||||||
|
} else if create, err := p.Lookup("NewProcessor"); err == nil {
|
||||||
|
processors.Add(name, processors.Creator(create.(func() plugins.Processor)))
|
||||||
|
} else if create, err := p.Lookup("NewAggregator"); err == nil {
|
||||||
|
aggregators.Add(name, aggregators.Creator(create.(func() plugins.Aggregator)))
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("not a telegraf plugin: %s%s", filePath, ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("I! Registered: %s (from %s%s)\n", name, filePath, ext)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = func() { usageExit(0) }
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Load external plugins, if requested.
|
||||||
|
if *fPlugins != "" {
|
||||||
|
pluginsDir, err := filepath.Abs(*fPlugins)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("E! " + err.Error())
|
||||||
|
}
|
||||||
|
log.Printf("I! Loading external plugins from: %s\n", pluginsDir)
|
||||||
|
if err := loadExternalPlugins(*fPlugins); err != nil {
|
||||||
|
log.Fatal("E! " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
svcConfig := &service.Config{
|
svcConfig := &service.Config{
|
||||||
Name: "telegraf",
|
Name: "telegraf",
|
||||||
@@ -361,12 +408,7 @@ func main() {
|
|||||||
Arguments: []string{"-config", "C:\\Program Files\\Telegraf\\telegraf.conf"},
|
Arguments: []string{"-config", "C:\\Program Files\\Telegraf\\telegraf.conf"},
|
||||||
}
|
}
|
||||||
|
|
||||||
prg := &program{
|
prg := &program{}
|
||||||
inputFilters: inputFilters,
|
|
||||||
outputFilters: outputFilters,
|
|
||||||
aggregatorFilters: aggregatorFilters,
|
|
||||||
processorFilters: processorFilters,
|
|
||||||
}
|
|
||||||
s, err := service.New(prg, svcConfig)
|
s, err := service.New(prg, svcConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("E! " + err.Error())
|
log.Fatal("E! " + err.Error())
|
||||||
@@ -377,14 +419,10 @@ func main() {
|
|||||||
if *fConfig != "" {
|
if *fConfig != "" {
|
||||||
(*svcConfig).Arguments = []string{"-config", *fConfig}
|
(*svcConfig).Arguments = []string{"-config", *fConfig}
|
||||||
}
|
}
|
||||||
if *fConfigDirectory != "" {
|
|
||||||
(*svcConfig).Arguments = append((*svcConfig).Arguments, "-config-directory", *fConfigDirectory)
|
|
||||||
}
|
|
||||||
err := service.Control(s, *fService)
|
err := service.Control(s, *fService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("E! " + err.Error())
|
log.Fatal("E! " + err.Error())
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
|
||||||
} else {
|
} else {
|
||||||
err = s.Run()
|
err = s.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -393,12 +431,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stop = make(chan struct{})
|
stop = make(chan struct{})
|
||||||
reloadLoop(
|
reloadLoop(stop, nil)
|
||||||
stop,
|
|
||||||
inputFilters,
|
|
||||||
outputFilters,
|
|
||||||
aggregatorFilters,
|
|
||||||
processorFilters,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,16 +24,6 @@ Environment variables can be used anywhere in the config file, simply prepend
|
|||||||
them with $. For strings the variable must be within quotes (ie, "$STR_VAR"),
|
them with $. For strings the variable must be within quotes (ie, "$STR_VAR"),
|
||||||
for numbers and booleans they should be plain (ie, $INT_VAR, $BOOL_VAR)
|
for numbers and booleans they should be plain (ie, $INT_VAR, $BOOL_VAR)
|
||||||
|
|
||||||
## Configuration file locations
|
|
||||||
|
|
||||||
The location of the configuration file can be set via the `--config` command
|
|
||||||
line flag. Telegraf will also pick up all files matching the pattern `*.conf` if
|
|
||||||
the `-config-directory` command line flag is used.
|
|
||||||
|
|
||||||
On most systems, the default locations are `/etc/telegraf/telegraf.conf` for
|
|
||||||
the main configuration file and `/etc/telegraf/telegraf.d` for the directory of
|
|
||||||
configuration files.
|
|
||||||
|
|
||||||
# Global Tags
|
# Global Tags
|
||||||
|
|
||||||
Global tags can be specified in the `[global_tags]` section of the config file
|
Global tags can be specified in the `[global_tags]` section of the config file
|
||||||
@@ -70,7 +60,7 @@ ie, a jitter of 5s and flush_interval 10s means flushes will happen every 10-15s
|
|||||||
as the collection interval, with the maximum being 1s. Precision will NOT
|
as the collection interval, with the maximum being 1s. Precision will NOT
|
||||||
be used for service inputs, such as logparser and statsd. Valid values are
|
be used for service inputs, such as logparser and statsd. Valid values are
|
||||||
"ns", "us" (or "µs"), "ms", "s".
|
"ns", "us" (or "µs"), "ms", "s".
|
||||||
* **logfile**: Specify the log file name. The empty string means to log to stderr.
|
* **logfile**: Specify the log file name. The empty string means to log to stdout.
|
||||||
* **debug**: Run telegraf in debug mode.
|
* **debug**: Run telegraf in debug mode.
|
||||||
* **quiet**: Run telegraf in quiet mode (error messages only).
|
* **quiet**: Run telegraf in quiet mode (error messages only).
|
||||||
* **hostname**: Override default hostname, if empty use os.Hostname().
|
* **hostname**: Override default hostname, if empty use os.Hostname().
|
||||||
@@ -124,40 +114,31 @@ is not specified then processor execution order will be random.
|
|||||||
Filters can be configured per input, output, processor, or aggregator,
|
Filters can be configured per input, output, processor, or aggregator,
|
||||||
see below for examples.
|
see below for examples.
|
||||||
|
|
||||||
* **namepass**:
|
* **namepass**: An array of strings that is used to filter metrics generated by the
|
||||||
An array of glob pattern strings. Only points whose measurement name matches
|
current input. Each string in the array is tested as a glob match against
|
||||||
a pattern in this list are emitted.
|
measurement names and if it matches, the field is emitted.
|
||||||
* **namedrop**:
|
* **namedrop**: The inverse of pass, if a measurement name matches, it is not emitted.
|
||||||
The inverse of `namepass`. If a match is found the point is discarded. This
|
* **fieldpass**: An array of strings that is used to filter metrics generated by the
|
||||||
is tested on points after they have passed the `namepass` test.
|
current input. Each string in the array is tested as a glob match against field names
|
||||||
* **fieldpass**:
|
and if it matches, the field is emitted. fieldpass is not available for outputs.
|
||||||
An array of glob pattern strings. Only fields whose field key matches a
|
* **fielddrop**: The inverse of pass, if a field name matches, it is not emitted.
|
||||||
pattern in this list are emitted. Not available for outputs.
|
fielddrop is not available for outputs.
|
||||||
* **fielddrop**:
|
* **tagpass**: tag names and arrays of strings that are used to filter
|
||||||
The inverse of `fieldpass`. Fields with a field key matching one of the
|
measurements by the current input. Each string in the array is tested as a glob
|
||||||
patterns will be discarded from the point. Not available for outputs.
|
match against the tag name, and if it matches the measurement is emitted.
|
||||||
* **tagpass**:
|
* **tagdrop**: The inverse of tagpass. If a tag matches, the measurement is not
|
||||||
A table mapping tag keys to arrays of glob pattern strings. Only points
|
emitted. This is tested on measurements that have passed the tagpass test.
|
||||||
that contain a tag key in the table and a tag value matching one of its
|
* **tagexclude**: tagexclude can be used to exclude a tag from measurement(s).
|
||||||
patterns is emitted.
|
As opposed to tagdrop, which will drop an entire measurement based on it's
|
||||||
* **tagdrop**:
|
tags, tagexclude simply strips the given tag keys from the measurement. This
|
||||||
The inverse of `tagpass`. If a match is found the point is discarded. This
|
can be used on inputs & outputs, but it is _recommended_ to be used on inputs,
|
||||||
is tested on points after they have passed the `tagpass` test.
|
as it is more efficient to filter out tags at the ingestion point.
|
||||||
* **taginclude**:
|
* **taginclude**: taginclude is the inverse of tagexclude. It will only include
|
||||||
An array of glob pattern strings. Only tags with a tag key matching one of
|
the tag keys in the final measurement.
|
||||||
the patterns are emitted. In contrast to `tagpass`, which will pass an entire
|
|
||||||
point based on its tag, `taginclude` removes all non matching tags from the
|
|
||||||
point. This filter can be used on both inputs & outputs, but it is
|
|
||||||
_recommended_ to be used on inputs, as it is more efficient to filter out tags
|
|
||||||
at the ingestion point.
|
|
||||||
* **tagexclude**:
|
|
||||||
The inverse of `taginclude`. Tags with a tag key matching one of the patterns
|
|
||||||
will be discarded from the point.
|
|
||||||
|
|
||||||
**NOTE** Due to the way TOML is parsed, `tagpass` and `tagdrop` parameters
|
**NOTE** `tagpass` and `tagdrop` parameters must be defined at the _end_ of
|
||||||
must be defined at the _end_ of the plugin definition, otherwise subsequent
|
the plugin definition, otherwise subsequent plugin config options will be
|
||||||
plugin config options will be interpreted as part of the tagpass/tagdrop
|
interpreted as part of the tagpass/tagdrop map.
|
||||||
tables.
|
|
||||||
|
|
||||||
#### Input Configuration Examples
|
#### Input Configuration Examples
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ Telegraf is able to parse the following input data formats into metrics:
|
|||||||
1. [Graphite](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md#graphite)
|
1. [Graphite](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md#graphite)
|
||||||
1. [Value](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md#value), ie: 45 or "booyah"
|
1. [Value](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md#value), ie: 45 or "booyah"
|
||||||
1. [Nagios](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md#nagios) (exec input only)
|
1. [Nagios](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md#nagios) (exec input only)
|
||||||
1. [Collectd](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md#collectd)
|
|
||||||
|
|
||||||
Telegraf metrics, like InfluxDB
|
Telegraf metrics, like InfluxDB
|
||||||
[points](https://docs.influxdata.com/influxdb/v0.10/write_protocols/line/),
|
[points](https://docs.influxdata.com/influxdb/v0.10/write_protocols/line/),
|
||||||
@@ -41,7 +40,7 @@ example, in the exec plugin:
|
|||||||
name_suffix = "_mycollector"
|
name_suffix = "_mycollector"
|
||||||
|
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "json"
|
data_format = "json"
|
||||||
@@ -68,7 +67,7 @@ metrics are parsed directly into Telegraf metrics.
|
|||||||
name_suffix = "_mycollector"
|
name_suffix = "_mycollector"
|
||||||
|
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "influx"
|
data_format = "influx"
|
||||||
@@ -118,7 +117,7 @@ For example, if you had this configuration:
|
|||||||
name_suffix = "_mycollector"
|
name_suffix = "_mycollector"
|
||||||
|
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "json"
|
data_format = "json"
|
||||||
@@ -162,7 +161,7 @@ For example, if the following configuration:
|
|||||||
name_suffix = "_mycollector"
|
name_suffix = "_mycollector"
|
||||||
|
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "json"
|
data_format = "json"
|
||||||
@@ -233,7 +232,7 @@ name of the plugin.
|
|||||||
name_override = "entropy_available"
|
name_override = "entropy_available"
|
||||||
|
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "value"
|
data_format = "value"
|
||||||
@@ -391,7 +390,7 @@ There are many more options available,
|
|||||||
name_suffix = "_mycollector"
|
name_suffix = "_mycollector"
|
||||||
|
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "graphite"
|
data_format = "graphite"
|
||||||
@@ -428,54 +427,14 @@ Note: Nagios Input Data Formats is only supported in `exec` input plugin.
|
|||||||
```toml
|
```toml
|
||||||
[[inputs.exec]]
|
[[inputs.exec]]
|
||||||
## Commands array
|
## Commands array
|
||||||
commands = ["/usr/lib/nagios/plugins/check_load -w 5,6,7 -c 7,8,9"]
|
commands = ["/usr/lib/nagios/plugins/check_load", "-w 5,6,7 -c 7,8,9"]
|
||||||
|
|
||||||
## measurement name suffix (for separating different commands)
|
## measurement name suffix (for separating different commands)
|
||||||
name_suffix = "_mycollector"
|
name_suffix = "_mycollector"
|
||||||
|
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "nagios"
|
data_format = "nagios"
|
||||||
```
|
```
|
||||||
|
|
||||||
# Collectd:
|
|
||||||
|
|
||||||
The collectd format parses the collectd binary network protocol. Tags are
|
|
||||||
created for host, instance, type, and type instance. All collectd values are
|
|
||||||
added as float64 fields.
|
|
||||||
|
|
||||||
For more information about the binary network protocol see
|
|
||||||
[here](https://collectd.org/wiki/index.php/Binary_protocol).
|
|
||||||
|
|
||||||
You can control the cryptographic settings with parser options. Create an
|
|
||||||
authentication file and set `collectd_auth_file` to the path of the file, then
|
|
||||||
set the desired security level in `collectd_security_level`.
|
|
||||||
|
|
||||||
Additional information including client setup can be found
|
|
||||||
[here](https://collectd.org/wiki/index.php/Networking_introduction#Cryptographic_setup).
|
|
||||||
|
|
||||||
You can also change the path to the typesdb or add additional typesdb using
|
|
||||||
`collectd_typesdb`.
|
|
||||||
|
|
||||||
#### Collectd Configuration:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[inputs.socket_listener]]
|
|
||||||
service_address = "udp://127.0.0.1:25826"
|
|
||||||
name_prefix = "collectd_"
|
|
||||||
|
|
||||||
## Data format to consume.
|
|
||||||
## Each data format has its own unique set of configuration options, read
|
|
||||||
## more about them here:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
|
||||||
data_format = "collectd"
|
|
||||||
|
|
||||||
## Authentication file for cryptographic security levels
|
|
||||||
collectd_auth_file = "/etc/collectd/auth_file"
|
|
||||||
## One of none (default), sign, or encrypt
|
|
||||||
collectd_security_level = "encrypt"
|
|
||||||
## Path of to TypesDB specifications
|
|
||||||
collectd_typesdb = ["/usr/share/collectd/types.db"]
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ config option, for example, in the `file` output plugin:
|
|||||||
files = ["stdout"]
|
files = ["stdout"]
|
||||||
|
|
||||||
## Data format to output.
|
## Data format to output.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||||
data_format = "influx"
|
data_format = "influx"
|
||||||
@@ -60,7 +60,7 @@ metrics are serialized directly into InfluxDB line-protocol.
|
|||||||
files = ["stdout", "/tmp/metrics.out"]
|
files = ["stdout", "/tmp/metrics.out"]
|
||||||
|
|
||||||
## Data format to output.
|
## Data format to output.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||||
data_format = "influx"
|
data_format = "influx"
|
||||||
@@ -104,7 +104,7 @@ tars.cpu-total.us-east-1.cpu.usage_idle 98.09 1455320690
|
|||||||
files = ["stdout", "/tmp/metrics.out"]
|
files = ["stdout", "/tmp/metrics.out"]
|
||||||
|
|
||||||
## Data format to output.
|
## Data format to output.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||||
data_format = "graphite"
|
data_format = "graphite"
|
||||||
@@ -143,18 +143,8 @@ The JSON data format serialized Telegraf metrics in json format. The format is:
|
|||||||
files = ["stdout", "/tmp/metrics.out"]
|
files = ["stdout", "/tmp/metrics.out"]
|
||||||
|
|
||||||
## Data format to output.
|
## Data format to output.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||||
data_format = "json"
|
data_format = "json"
|
||||||
json_timestamp_units = "1ns"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, the timestamp that is output in JSON data format serialized Telegraf
|
|
||||||
metrics is in seconds. The precision of this timestamp can be adjusted for any output
|
|
||||||
by adding the optional `json_timestamp_units` parameter to the configuration for
|
|
||||||
that output. This parameter can be used to set the timestamp units to nanoseconds (`ns`),
|
|
||||||
microseconds (`us` or `µs`), milliseconds (`ms`), or seconds (`s`). Note that this
|
|
||||||
parameter will be truncated to the nearest power of 10 that, so if the `json_timestamp_units`
|
|
||||||
are set to `15ms` the timestamps for the JSON format serialized Telegraf metrics will be
|
|
||||||
output in hundredths of a second (`10ms`).
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# List
|
# List
|
||||||
- collectd.org [MIT LICENSE](https://github.com/collectd/go-collectd/blob/master/LICENSE)
|
|
||||||
- github.com/Shopify/sarama [MIT LICENSE](https://github.com/Shopify/sarama/blob/master/MIT-LICENSE)
|
- github.com/Shopify/sarama [MIT LICENSE](https://github.com/Shopify/sarama/blob/master/MIT-LICENSE)
|
||||||
- github.com/Sirupsen/logrus [MIT LICENSE](https://github.com/Sirupsen/logrus/blob/master/LICENSE)
|
- github.com/Sirupsen/logrus [MIT LICENSE](https://github.com/Sirupsen/logrus/blob/master/LICENSE)
|
||||||
- github.com/armon/go-metrics [MIT LICENSE](https://github.com/armon/go-metrics/blob/master/LICENSE)
|
- github.com/armon/go-metrics [MIT LICENSE](https://github.com/armon/go-metrics/blob/master/LICENSE)
|
||||||
@@ -31,3 +30,4 @@
|
|||||||
- gopkg.in/dancannon/gorethink.v1 [APACHE LICENSE](https://github.com/dancannon/gorethink/blob/v1.1.2/LICENSE)
|
- gopkg.in/dancannon/gorethink.v1 [APACHE LICENSE](https://github.com/dancannon/gorethink/blob/v1.1.2/LICENSE)
|
||||||
- gopkg.in/mgo.v2 [BSD LICENSE](https://github.com/go-mgo/mgo/blob/v2/LICENSE)
|
- gopkg.in/mgo.v2 [BSD LICENSE](https://github.com/go-mgo/mgo/blob/v2/LICENSE)
|
||||||
- golang.org/x/crypto/ [BSD LICENSE](https://github.com/golang/crypto/blob/master/LICENSE)
|
- golang.org/x/crypto/ [BSD LICENSE](https://github.com/golang/crypto/blob/master/LICENSE)
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
# Telegraf profiling
|
|
||||||
|
|
||||||
Telegraf uses the standard package `net/http/pprof`. This package serves via its HTTP server runtime profiling data in the format expected by the pprof visualization tool.
|
|
||||||
|
|
||||||
By default, the profiling is turned off.
|
|
||||||
|
|
||||||
To enable profiling you need to specify address to config parameter `pprof-addr`, for example:
|
|
||||||
|
|
||||||
```
|
|
||||||
telegraf --config telegraf.conf --pprof-addr localhost:6060
|
|
||||||
```
|
|
||||||
|
|
||||||
There are several paths to get different profiling information:
|
|
||||||
|
|
||||||
To look at the heap profile:
|
|
||||||
|
|
||||||
`go tool pprof http://localhost:6060/debug/pprof/heap`
|
|
||||||
|
|
||||||
or to look at a 30-second CPU profile:
|
|
||||||
|
|
||||||
`go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30`
|
|
||||||
|
|
||||||
To view all available profiles, open `http://localhost:6060/debug/pprof/` in your browser.
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -105,11 +105,10 @@
|
|||||||
"% Privileged Time",
|
"% Privileged Time",
|
||||||
"% User Time",
|
"% User Time",
|
||||||
"% Processor Time",
|
"% Processor Time",
|
||||||
"% DPC Time",
|
|
||||||
]
|
]
|
||||||
Measurement = "win_cpu"
|
Measurement = "win_cpu"
|
||||||
# Set to true to include _Total instance when querying for all (*).
|
# Set to true to include _Total instance when querying for all (*).
|
||||||
IncludeTotal=true
|
#IncludeTotal=false
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
[[inputs.win_perf_counters.object]]
|
||||||
# Disk times and queues
|
# Disk times and queues
|
||||||
@@ -117,54 +116,21 @@
|
|||||||
Instances = ["*"]
|
Instances = ["*"]
|
||||||
Counters = [
|
Counters = [
|
||||||
"% Idle Time",
|
"% Idle Time",
|
||||||
"% Disk Time",
|
"% Disk Time","% Disk Read Time",
|
||||||
"% Disk Read Time",
|
|
||||||
"% Disk Write Time",
|
"% Disk Write Time",
|
||||||
|
"% User Time",
|
||||||
"Current Disk Queue Length",
|
"Current Disk Queue Length",
|
||||||
"% Free Space",
|
|
||||||
"Free Megabytes",
|
|
||||||
]
|
]
|
||||||
Measurement = "win_disk"
|
Measurement = "win_disk"
|
||||||
# Set to true to include _Total instance when querying for all (*).
|
# Set to true to include _Total instance when querying for all (*).
|
||||||
#IncludeTotal=false
|
#IncludeTotal=false
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
|
||||||
ObjectName = "PhysicalDisk"
|
|
||||||
Instances = ["*"]
|
|
||||||
Counters = [
|
|
||||||
"Disk Read Bytes/sec",
|
|
||||||
"Disk Write Bytes/sec",
|
|
||||||
"Current Disk Queue Length",
|
|
||||||
"Disk Reads/sec",
|
|
||||||
"Disk Writes/sec",
|
|
||||||
"% Disk Time",
|
|
||||||
"% Disk Read Time",
|
|
||||||
"% Disk Write Time",
|
|
||||||
]
|
|
||||||
Measurement = "win_diskio"
|
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
|
||||||
ObjectName = "Network Interface"
|
|
||||||
Instances = ["*"]
|
|
||||||
Counters = [
|
|
||||||
"Bytes Received/sec",
|
|
||||||
"Bytes Sent/sec",
|
|
||||||
"Packets Received/sec",
|
|
||||||
"Packets Sent/sec",
|
|
||||||
"Packets Received Discarded",
|
|
||||||
"Packets Outbound Discarded",
|
|
||||||
"Packets Received Errors",
|
|
||||||
"Packets Outbound Errors",
|
|
||||||
]
|
|
||||||
Measurement = "win_net"
|
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
[[inputs.win_perf_counters.object]]
|
||||||
ObjectName = "System"
|
ObjectName = "System"
|
||||||
Counters = [
|
Counters = [
|
||||||
"Context Switches/sec",
|
"Context Switches/sec",
|
||||||
"System Calls/sec",
|
"System Calls/sec",
|
||||||
"Processor Queue Length",
|
"Processor Queue Length",
|
||||||
"System Up Time",
|
|
||||||
]
|
]
|
||||||
Instances = ["------"]
|
Instances = ["------"]
|
||||||
Measurement = "win_system"
|
Measurement = "win_system"
|
||||||
@@ -184,10 +150,6 @@
|
|||||||
"Transition Faults/sec",
|
"Transition Faults/sec",
|
||||||
"Pool Nonpaged Bytes",
|
"Pool Nonpaged Bytes",
|
||||||
"Pool Paged Bytes",
|
"Pool Paged Bytes",
|
||||||
"Standby Cache Reserve Bytes",
|
|
||||||
"Standby Cache Normal Priority Bytes",
|
|
||||||
"Standby Cache Core Bytes",
|
|
||||||
|
|
||||||
]
|
]
|
||||||
# Use 6 x - to remove the Instance bit from the query.
|
# Use 6 x - to remove the Instance bit from the query.
|
||||||
Instances = ["------"]
|
Instances = ["------"]
|
||||||
@@ -195,31 +157,6 @@
|
|||||||
# Set to true to include _Total instance when querying for all (*).
|
# Set to true to include _Total instance when querying for all (*).
|
||||||
#IncludeTotal=false
|
#IncludeTotal=false
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
|
||||||
# Example query where the Instance portion must be removed to get data back,
|
|
||||||
# such as from the Paging File object.
|
|
||||||
ObjectName = "Paging File"
|
|
||||||
Counters = [
|
|
||||||
"% Usage",
|
|
||||||
]
|
|
||||||
Instances = ["_Total"]
|
|
||||||
Measurement = "win_swap"
|
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
|
||||||
ObjectName = "Network Interface"
|
|
||||||
Instances = ["*"]
|
|
||||||
Counters = [
|
|
||||||
"Bytes Sent/sec",
|
|
||||||
"Bytes Received/sec",
|
|
||||||
"Packets Sent/sec",
|
|
||||||
"Packets Received/sec",
|
|
||||||
"Packets Received Discarded",
|
|
||||||
"Packets Received Errors",
|
|
||||||
"Packets Outbound Discarded",
|
|
||||||
"Packets Outbound Errors",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Windows system plugins using WMI (disabled by default, using
|
# Windows system plugins using WMI (disabled by default, using
|
||||||
# win_perf_counters over WMI is recommended)
|
# win_perf_counters over WMI is recommended)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package buffer
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/selfstat"
|
"github.com/influxdata/telegraf/selfstat"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ var (
|
|||||||
|
|
||||||
// Buffer is an object for storing metrics in a circular buffer.
|
// Buffer is an object for storing metrics in a circular buffer.
|
||||||
type Buffer struct {
|
type Buffer struct {
|
||||||
buf chan telegraf.Metric
|
buf chan plugins.Metric
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ type Buffer struct {
|
|||||||
// called when the buffer is full, then the oldest metric(s) will be dropped.
|
// called when the buffer is full, then the oldest metric(s) will be dropped.
|
||||||
func NewBuffer(size int) *Buffer {
|
func NewBuffer(size int) *Buffer {
|
||||||
return &Buffer{
|
return &Buffer{
|
||||||
buf: make(chan telegraf.Metric, size),
|
buf: make(chan plugins.Metric, size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,17 +39,15 @@ func (b *Buffer) Len() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add adds metrics to the buffer.
|
// Add adds metrics to the buffer.
|
||||||
func (b *Buffer) Add(metrics ...telegraf.Metric) {
|
func (b *Buffer) Add(metrics ...plugins.Metric) {
|
||||||
for i, _ := range metrics {
|
for i, _ := range metrics {
|
||||||
MetricsWritten.Incr(1)
|
MetricsWritten.Incr(1)
|
||||||
select {
|
select {
|
||||||
case b.buf <- metrics[i]:
|
case b.buf <- metrics[i]:
|
||||||
default:
|
default:
|
||||||
b.mu.Lock()
|
|
||||||
MetricsDropped.Incr(1)
|
MetricsDropped.Incr(1)
|
||||||
<-b.buf
|
<-b.buf
|
||||||
b.buf <- metrics[i]
|
b.buf <- metrics[i]
|
||||||
b.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,10 +55,10 @@ func (b *Buffer) Add(metrics ...telegraf.Metric) {
|
|||||||
// Batch returns a batch of metrics of size batchSize.
|
// Batch returns a batch of metrics of size batchSize.
|
||||||
// the batch will be of maximum length batchSize. It can be less than batchSize,
|
// the batch will be of maximum length batchSize. It can be less than batchSize,
|
||||||
// if the length of Buffer is less than batchSize.
|
// if the length of Buffer is less than batchSize.
|
||||||
func (b *Buffer) Batch(batchSize int) []telegraf.Metric {
|
func (b *Buffer) Batch(batchSize int) []plugins.Metric {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
n := min(len(b.buf), batchSize)
|
n := min(len(b.buf), batchSize)
|
||||||
out := make([]telegraf.Metric, n)
|
out := make([]plugins.Metric, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
out[i] = <-b.buf
|
out[i] = <-b.buf
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ package buffer
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var metricList = []telegraf.Metric{
|
var metricList = []plugins.Metric{
|
||||||
testutil.TestMetric(2, "mymetric1"),
|
testutil.TestMetric(2, "mymetric1"),
|
||||||
testutil.TestMetric(1, "mymetric2"),
|
testutil.TestMetric(1, "mymetric2"),
|
||||||
testutil.TestMetric(11, "mymetric3"),
|
testutil.TestMetric(11, "mymetric3"),
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -16,9 +15,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/internal/models"
|
"github.com/influxdata/telegraf/internal/models"
|
||||||
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/aggregators"
|
"github.com/influxdata/telegraf/plugins/aggregators"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
"github.com/influxdata/telegraf/plugins/outputs"
|
"github.com/influxdata/telegraf/plugins/outputs"
|
||||||
@@ -26,6 +25,7 @@ import (
|
|||||||
"github.com/influxdata/telegraf/plugins/processors"
|
"github.com/influxdata/telegraf/plugins/processors"
|
||||||
"github.com/influxdata/telegraf/plugins/serializers"
|
"github.com/influxdata/telegraf/plugins/serializers"
|
||||||
|
|
||||||
|
"github.com/influxdata/config"
|
||||||
"github.com/influxdata/toml"
|
"github.com/influxdata/toml"
|
||||||
"github.com/influxdata/toml/ast"
|
"github.com/influxdata/toml/ast"
|
||||||
)
|
)
|
||||||
@@ -85,8 +85,8 @@ type AgentConfig struct {
|
|||||||
// ie, if Interval=10s then always collect on :00, :10, :20, etc.
|
// ie, if Interval=10s then always collect on :00, :10, :20, etc.
|
||||||
RoundInterval bool
|
RoundInterval bool
|
||||||
|
|
||||||
// By default or when set to "0s", precision will be set to the same
|
// By default, precision will be set to the same timestamp order as the
|
||||||
// timestamp order as the collection interval, with the maximum being 1s.
|
// collection interval, with the maximum being 1s.
|
||||||
// ie, when interval = "10s", precision will be "1s"
|
// ie, when interval = "10s", precision will be "1s"
|
||||||
// when interval = "250ms", precision will be "1ms"
|
// when interval = "250ms", precision will be "1ms"
|
||||||
// Precision will NOT be used for service inputs. It is up to each individual
|
// Precision will NOT be used for service inputs. It is up to each individual
|
||||||
@@ -230,13 +230,10 @@ var header = `# Telegraf Configuration
|
|||||||
## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s
|
## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s
|
||||||
flush_jitter = "0s"
|
flush_jitter = "0s"
|
||||||
|
|
||||||
## By default or when set to "0s", precision will be set to the same
|
## By default, precision will be set to the same timestamp order as the
|
||||||
## timestamp order as the collection interval, with the maximum being 1s.
|
## collection interval, with the maximum being 1s.
|
||||||
## ie, when interval = "10s", precision will be "1s"
|
## Precision will NOT be used for service inputs, such as logparser and statsd.
|
||||||
## when interval = "250ms", precision will be "1ms"
|
## Valid values are "ns", "us" (or "µs"), "ms", "s".
|
||||||
## Precision will NOT be used for service inputs. It is up to each individual
|
|
||||||
## service input to set the timestamp at the appropriate precision.
|
|
||||||
## Valid time units are "ns", "us" (or "µs"), "ms", "s".
|
|
||||||
precision = ""
|
precision = ""
|
||||||
|
|
||||||
## Logging configuration:
|
## Logging configuration:
|
||||||
@@ -402,7 +399,7 @@ func printFilteredInputs(inputFilters []string, commented bool) {
|
|||||||
sort.Strings(pnames)
|
sort.Strings(pnames)
|
||||||
|
|
||||||
// cache service inputs to print them at the end
|
// cache service inputs to print them at the end
|
||||||
servInputs := make(map[string]telegraf.ServiceInput)
|
servInputs := make(map[string]plugins.ServiceInput)
|
||||||
// for alphabetical looping:
|
// for alphabetical looping:
|
||||||
servInputNames := []string{}
|
servInputNames := []string{}
|
||||||
|
|
||||||
@@ -412,7 +409,7 @@ func printFilteredInputs(inputFilters []string, commented bool) {
|
|||||||
input := creator()
|
input := creator()
|
||||||
|
|
||||||
switch p := input.(type) {
|
switch p := input.(type) {
|
||||||
case telegraf.ServiceInput:
|
case plugins.ServiceInput:
|
||||||
servInputs[pname] = p
|
servInputs[pname] = p
|
||||||
servInputNames = append(servInputNames, pname)
|
servInputNames = append(servInputNames, pname)
|
||||||
continue
|
continue
|
||||||
@@ -509,10 +506,6 @@ func PrintOutputConfig(name string) error {
|
|||||||
|
|
||||||
func (c *Config) LoadDirectory(path string) error {
|
func (c *Config) LoadDirectory(path string) error {
|
||||||
walkfn := func(thispath string, info os.FileInfo, _ error) error {
|
walkfn := func(thispath string, info os.FileInfo, _ error) error {
|
||||||
if info == nil {
|
|
||||||
log.Printf("W! Telegraf is not permitted to read %s", thispath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -573,7 +566,7 @@ func (c *Config) LoadConfig(path string) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%s: invalid configuration", path)
|
return fmt.Errorf("%s: invalid configuration", path)
|
||||||
}
|
}
|
||||||
if err = toml.UnmarshalTable(subTable, c.Tags); err != nil {
|
if err = config.UnmarshalTable(subTable, c.Tags); err != nil {
|
||||||
log.Printf("E! Could not parse [global_tags] config\n")
|
log.Printf("E! Could not parse [global_tags] config\n")
|
||||||
return fmt.Errorf("Error parsing %s, %s", path, err)
|
return fmt.Errorf("Error parsing %s, %s", path, err)
|
||||||
}
|
}
|
||||||
@@ -586,7 +579,7 @@ func (c *Config) LoadConfig(path string) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%s: invalid configuration", path)
|
return fmt.Errorf("%s: invalid configuration", path)
|
||||||
}
|
}
|
||||||
if err = toml.UnmarshalTable(subTable, c.Agent); err != nil {
|
if err = config.UnmarshalTable(subTable, c.Agent); err != nil {
|
||||||
log.Printf("E! Could not parse [agent] config\n")
|
log.Printf("E! Could not parse [agent] config\n")
|
||||||
return fmt.Errorf("Error parsing %s, %s", path, err)
|
return fmt.Errorf("Error parsing %s, %s", path, err)
|
||||||
}
|
}
|
||||||
@@ -723,7 +716,7 @@ func (c *Config) addAggregator(name string, table *ast.Table) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := toml.UnmarshalTable(table, aggregator); err != nil {
|
if err := config.UnmarshalTable(table, aggregator); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,7 +736,7 @@ func (c *Config) addProcessor(name string, table *ast.Table) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := toml.UnmarshalTable(table, processor); err != nil {
|
if err := config.UnmarshalTable(table, processor); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,7 +776,7 @@ func (c *Config) addOutput(name string, table *ast.Table) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := toml.UnmarshalTable(table, output); err != nil {
|
if err := config.UnmarshalTable(table, output); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,7 +817,7 @@ func (c *Config) addInput(name string, table *ast.Table) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := toml.UnmarshalTable(table, input); err != nil {
|
if err := config.UnmarshalTable(table, input); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -916,7 +909,7 @@ func buildAggregator(name string, tbl *ast.Table) (*models.AggregatorConfig, err
|
|||||||
conf.Tags = make(map[string]string)
|
conf.Tags = make(map[string]string)
|
||||||
if node, ok := tbl.Fields["tags"]; ok {
|
if node, ok := tbl.Fields["tags"]; ok {
|
||||||
if subtbl, ok := node.(*ast.Table); ok {
|
if subtbl, ok := node.(*ast.Table); ok {
|
||||||
if err := toml.UnmarshalTable(subtbl, conf.Tags); err != nil {
|
if err := config.UnmarshalTable(subtbl, conf.Tags); err != nil {
|
||||||
log.Printf("Could not parse tags for input %s\n", name)
|
log.Printf("Could not parse tags for input %s\n", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1153,7 +1146,7 @@ func buildInput(name string, tbl *ast.Table) (*models.InputConfig, error) {
|
|||||||
cp.Tags = make(map[string]string)
|
cp.Tags = make(map[string]string)
|
||||||
if node, ok := tbl.Fields["tags"]; ok {
|
if node, ok := tbl.Fields["tags"]; ok {
|
||||||
if subtbl, ok := node.(*ast.Table); ok {
|
if subtbl, ok := node.(*ast.Table); ok {
|
||||||
if err := toml.UnmarshalTable(subtbl, cp.Tags); err != nil {
|
if err := config.UnmarshalTable(subtbl, cp.Tags); err != nil {
|
||||||
log.Printf("E! Could not parse tags for input %s\n", name)
|
log.Printf("E! Could not parse tags for input %s\n", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1233,34 +1226,6 @@ func buildParser(name string, tbl *ast.Table) (parsers.Parser, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if node, ok := tbl.Fields["collectd_auth_file"]; ok {
|
|
||||||
if kv, ok := node.(*ast.KeyValue); ok {
|
|
||||||
if str, ok := kv.Value.(*ast.String); ok {
|
|
||||||
c.CollectdAuthFile = str.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if node, ok := tbl.Fields["collectd_security_level"]; ok {
|
|
||||||
if kv, ok := node.(*ast.KeyValue); ok {
|
|
||||||
if str, ok := kv.Value.(*ast.String); ok {
|
|
||||||
c.CollectdSecurityLevel = str.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if node, ok := tbl.Fields["collectd_typesdb"]; ok {
|
|
||||||
if kv, ok := node.(*ast.KeyValue); ok {
|
|
||||||
if ary, ok := kv.Value.(*ast.Array); ok {
|
|
||||||
for _, elem := range ary.Value {
|
|
||||||
if str, ok := elem.(*ast.String); ok {
|
|
||||||
c.CollectdTypesDB = append(c.CollectdTypesDB, str.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.MetricName = name
|
c.MetricName = name
|
||||||
|
|
||||||
delete(tbl.Fields, "data_format")
|
delete(tbl.Fields, "data_format")
|
||||||
@@ -1268,9 +1233,6 @@ func buildParser(name string, tbl *ast.Table) (parsers.Parser, error) {
|
|||||||
delete(tbl.Fields, "templates")
|
delete(tbl.Fields, "templates")
|
||||||
delete(tbl.Fields, "tag_keys")
|
delete(tbl.Fields, "tag_keys")
|
||||||
delete(tbl.Fields, "data_type")
|
delete(tbl.Fields, "data_type")
|
||||||
delete(tbl.Fields, "collectd_auth_file")
|
|
||||||
delete(tbl.Fields, "collectd_security_level")
|
|
||||||
delete(tbl.Fields, "collectd_typesdb")
|
|
||||||
|
|
||||||
return parsers.NewParser(c)
|
return parsers.NewParser(c)
|
||||||
}
|
}
|
||||||
@@ -1279,7 +1241,7 @@ func buildParser(name string, tbl *ast.Table) (parsers.Parser, error) {
|
|||||||
// a serializers.Serializer object, and creates it, which can then be added onto
|
// a serializers.Serializer object, and creates it, which can then be added onto
|
||||||
// an Output object.
|
// an Output object.
|
||||||
func buildSerializer(name string, tbl *ast.Table) (serializers.Serializer, error) {
|
func buildSerializer(name string, tbl *ast.Table) (serializers.Serializer, error) {
|
||||||
c := &serializers.Config{TimestampUnits: time.Duration(1 * time.Second)}
|
c := &serializers.Config{}
|
||||||
|
|
||||||
if node, ok := tbl.Fields["data_format"]; ok {
|
if node, ok := tbl.Fields["data_format"]; ok {
|
||||||
if kv, ok := node.(*ast.KeyValue); ok {
|
if kv, ok := node.(*ast.KeyValue); ok {
|
||||||
@@ -1309,26 +1271,9 @@ func buildSerializer(name string, tbl *ast.Table) (serializers.Serializer, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if node, ok := tbl.Fields["json_timestamp_units"]; ok {
|
|
||||||
if kv, ok := node.(*ast.KeyValue); ok {
|
|
||||||
if str, ok := kv.Value.(*ast.String); ok {
|
|
||||||
timestampVal, err := time.ParseDuration(str.Value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Unable to parse json_timestamp_units as a duration, %s", err)
|
|
||||||
}
|
|
||||||
// now that we have a duration, truncate it to the nearest
|
|
||||||
// power of ten (just in case)
|
|
||||||
nearest_exponent := int64(math.Log10(float64(timestampVal.Nanoseconds())))
|
|
||||||
new_nanoseconds := int64(math.Pow(10.0, float64(nearest_exponent)))
|
|
||||||
c.TimestampUnits = time.Duration(new_nanoseconds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(tbl.Fields, "data_format")
|
delete(tbl.Fields, "data_format")
|
||||||
delete(tbl.Fields, "prefix")
|
delete(tbl.Fields, "prefix")
|
||||||
delete(tbl.Fields, "template")
|
delete(tbl.Fields, "template")
|
||||||
delete(tbl.Fields, "json_timestamp_units")
|
|
||||||
return serializers.NewSerializer(c)
|
return serializers.NewSerializer(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
internal/config/testdata/telegraf-agent.toml
vendored
2
internal/config/testdata/telegraf-agent.toml
vendored
@@ -60,7 +60,7 @@
|
|||||||
# Kafka topic for producer messages
|
# Kafka topic for producer messages
|
||||||
topic = "telegraf"
|
topic = "telegraf"
|
||||||
# Telegraf tag to use as a routing key
|
# Telegraf tag to use as a routing key
|
||||||
# ie, if this tag exists, its value will be used as the routing key
|
# ie, if this tag exists, it's value will be used as the routing key
|
||||||
routing_tag = "host"
|
routing_tag = "host"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
37
internal/errchan/errchan.go
Normal file
37
internal/errchan/errchan.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package errchan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrChan struct {
|
||||||
|
C chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns an error channel of max length 'n'
|
||||||
|
// errors can be sent to the ErrChan.C channel, and will be returned when
|
||||||
|
// ErrChan.Error() is called.
|
||||||
|
func New(n int) *ErrChan {
|
||||||
|
return &ErrChan{
|
||||||
|
C: make(chan error, n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error closes the ErrChan.C channel and returns an error if there are any
|
||||||
|
// non-nil errors, otherwise returns nil.
|
||||||
|
func (e *ErrChan) Error() error {
|
||||||
|
close(e.C)
|
||||||
|
|
||||||
|
var out string
|
||||||
|
for err := range e.C {
|
||||||
|
if err != nil {
|
||||||
|
out += "[" + err.Error() + "], "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if out != "" {
|
||||||
|
return fmt.Errorf("Errors encountered: " + strings.TrimRight(out, ", "))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ func (g *GlobPath) Match() map[string]os.FileInfo {
|
|||||||
if !g.hasMeta {
|
if !g.hasMeta {
|
||||||
out := make(map[string]os.FileInfo)
|
out := make(map[string]os.FileInfo)
|
||||||
info, err := os.Stat(g.path)
|
info, err := os.Stat(g.path)
|
||||||
if err == nil {
|
if !os.IsNotExist(err) {
|
||||||
out[g.path] = info
|
out[g.path] = info
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
@@ -55,7 +55,7 @@ func (g *GlobPath) Match() map[string]os.FileInfo {
|
|||||||
files, _ := filepath.Glob(g.path)
|
files, _ := filepath.Glob(g.path)
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
info, err := os.Stat(file)
|
info, err := os.Stat(file)
|
||||||
if err == nil {
|
if !os.IsNotExist(err) {
|
||||||
out[file] = info
|
out[file] = info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package globpath
|
package globpath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -29,7 +28,7 @@ func TestCompileAndMatch(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
matches := g1.Match()
|
matches := g1.Match()
|
||||||
assert.Len(t, matches, 6)
|
assert.Len(t, matches, 3)
|
||||||
matches = g2.Match()
|
matches = g2.Match()
|
||||||
assert.Len(t, matches, 2)
|
assert.Len(t, matches, 2)
|
||||||
matches = g3.Match()
|
matches = g3.Match()
|
||||||
@@ -57,34 +56,7 @@ func TestFindRootDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindNestedTextFile(t *testing.T) {
|
|
||||||
dir := getTestdataDir()
|
|
||||||
// test super asterisk
|
|
||||||
g1, err := Compile(dir + "/**.txt")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
matches := g1.Match()
|
|
||||||
assert.Len(t, matches, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTestdataDir() string {
|
func getTestdataDir() string {
|
||||||
_, filename, _, _ := runtime.Caller(1)
|
_, filename, _, _ := runtime.Caller(1)
|
||||||
return strings.Replace(filename, "globpath_test.go", "testdata", 1)
|
return strings.Replace(filename, "globpath_test.go", "testdata", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMatch_ErrPermission(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
expected map[string]os.FileInfo
|
|
||||||
}{
|
|
||||||
{"/root/foo", map[string]os.FileInfo{}},
|
|
||||||
{"/root/f*", map[string]os.FileInfo{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
glob, err := Compile(test.input)
|
|
||||||
require.NoError(t, err)
|
|
||||||
actual := glob.Match()
|
|
||||||
require.Equal(t, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ package models
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/metric"
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
"github.com/influxdata/telegraf/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
// makemetric is used by both RunningAggregator & RunningInput
|
// makemetric is used by both RunningAggregator & RunningInput
|
||||||
@@ -33,9 +32,9 @@ func makemetric(
|
|||||||
daemonTags map[string]string,
|
daemonTags map[string]string,
|
||||||
filter Filter,
|
filter Filter,
|
||||||
applyFilter bool,
|
applyFilter bool,
|
||||||
mType telegraf.ValueType,
|
mType plugins.ValueType,
|
||||||
t time.Time,
|
t time.Time,
|
||||||
) telegraf.Metric {
|
) plugins.Metric {
|
||||||
if len(fields) == 0 || len(measurement) == 0 {
|
if len(fields) == 0 || len(measurement) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -78,27 +77,7 @@ func makemetric(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range tags {
|
|
||||||
if strings.HasSuffix(k, `\`) {
|
|
||||||
log.Printf("D! Measurement [%s] tag [%s] "+
|
|
||||||
"ends with a backslash, skipping", measurement, k)
|
|
||||||
delete(tags, k)
|
|
||||||
continue
|
|
||||||
} else if strings.HasSuffix(v, `\`) {
|
|
||||||
log.Printf("D! Measurement [%s] tag [%s] has a value "+
|
|
||||||
"ending with a backslash, skipping", measurement, k)
|
|
||||||
delete(tags, k)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range fields {
|
for k, v := range fields {
|
||||||
if strings.HasSuffix(k, `\`) {
|
|
||||||
log.Printf("D! Measurement [%s] field [%s] "+
|
|
||||||
"ends with a backslash, skipping", measurement, k)
|
|
||||||
delete(fields, k)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Validate uint64 and float64 fields
|
// Validate uint64 and float64 fields
|
||||||
// convert all int & uint types to int64
|
// convert all int & uint types to int64
|
||||||
switch val := v.(type) {
|
switch val := v.(type) {
|
||||||
@@ -149,14 +128,6 @@ func makemetric(
|
|||||||
delete(fields, k)
|
delete(fields, k)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case string:
|
|
||||||
if strings.HasSuffix(val, `\`) {
|
|
||||||
log.Printf("D! Measurement [%s] field [%s] has a value "+
|
|
||||||
"ending with a backslash, skipping", measurement, k)
|
|
||||||
delete(fields, k)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields[k] = v
|
|
||||||
default:
|
default:
|
||||||
fields[k] = v
|
fields[k] = v
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,28 +3,28 @@ package models
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/metric"
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
"github.com/influxdata/telegraf/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RunningAggregator struct {
|
type RunningAggregator struct {
|
||||||
a telegraf.Aggregator
|
a plugins.Aggregator
|
||||||
Config *AggregatorConfig
|
Config *AggregatorConfig
|
||||||
|
|
||||||
metrics chan telegraf.Metric
|
metrics chan plugins.Metric
|
||||||
|
|
||||||
periodStart time.Time
|
periodStart time.Time
|
||||||
periodEnd time.Time
|
periodEnd time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRunningAggregator(
|
func NewRunningAggregator(
|
||||||
a telegraf.Aggregator,
|
a plugins.Aggregator,
|
||||||
conf *AggregatorConfig,
|
conf *AggregatorConfig,
|
||||||
) *RunningAggregator {
|
) *RunningAggregator {
|
||||||
return &RunningAggregator{
|
return &RunningAggregator{
|
||||||
a: a,
|
a: a,
|
||||||
Config: conf,
|
Config: conf,
|
||||||
metrics: make(chan telegraf.Metric, 100),
|
metrics: make(chan plugins.Metric, 100),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,9 +52,9 @@ func (r *RunningAggregator) MakeMetric(
|
|||||||
measurement string,
|
measurement string,
|
||||||
fields map[string]interface{},
|
fields map[string]interface{},
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
mType telegraf.ValueType,
|
mType plugins.ValueType,
|
||||||
t time.Time,
|
t time.Time,
|
||||||
) telegraf.Metric {
|
) plugins.Metric {
|
||||||
m := makemetric(
|
m := makemetric(
|
||||||
measurement,
|
measurement,
|
||||||
fields,
|
fields,
|
||||||
@@ -80,7 +80,7 @@ func (r *RunningAggregator) MakeMetric(
|
|||||||
// Add applies the given metric to the aggregator.
|
// Add applies the given metric to the aggregator.
|
||||||
// Before applying to the plugin, it will run any defined filters on the metric.
|
// Before applying to the plugin, it will run any defined filters on the metric.
|
||||||
// Apply returns true if the original metric should be dropped.
|
// Apply returns true if the original metric should be dropped.
|
||||||
func (r *RunningAggregator) Add(in telegraf.Metric) bool {
|
func (r *RunningAggregator) Add(in plugins.Metric) bool {
|
||||||
if r.Config.Filter.IsActive() {
|
if r.Config.Filter.IsActive() {
|
||||||
// check if the aggregator should apply this metric
|
// check if the aggregator should apply this metric
|
||||||
name := in.Name()
|
name := in.Name()
|
||||||
@@ -98,11 +98,11 @@ func (r *RunningAggregator) Add(in telegraf.Metric) bool {
|
|||||||
r.metrics <- in
|
r.metrics <- in
|
||||||
return r.Config.DropOriginal
|
return r.Config.DropOriginal
|
||||||
}
|
}
|
||||||
func (r *RunningAggregator) add(in telegraf.Metric) {
|
func (r *RunningAggregator) add(in plugins.Metric) {
|
||||||
r.a.Add(in)
|
r.a.Add(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RunningAggregator) push(acc telegraf.Accumulator) {
|
func (r *RunningAggregator) push(acc plugins.Accumulator) {
|
||||||
r.a.Push(acc)
|
r.a.Push(acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ func (r *RunningAggregator) reset() {
|
|||||||
// Run runs the running aggregator, listens for incoming metrics, and waits
|
// Run runs the running aggregator, listens for incoming metrics, and waits
|
||||||
// for period ticks to tell it when to push and reset the aggregator.
|
// for period ticks to tell it when to push and reset the aggregator.
|
||||||
func (r *RunningAggregator) Run(
|
func (r *RunningAggregator) Run(
|
||||||
acc telegraf.Accumulator,
|
acc plugins.Accumulator,
|
||||||
shutdown chan struct{},
|
shutdown chan struct{},
|
||||||
) {
|
) {
|
||||||
// The start of the period is truncated to the nearest second.
|
// The start of the period is truncated to the nearest second.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -30,7 +30,7 @@ func TestAdd(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
time.Now().Add(time.Millisecond*150),
|
time.Now().Add(time.Millisecond*150),
|
||||||
)
|
)
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
@@ -62,7 +62,7 @@ func TestAddMetricsOutsideCurrentPeriod(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
time.Now().Add(-time.Hour),
|
time.Now().Add(-time.Hour),
|
||||||
)
|
)
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
@@ -72,7 +72,7 @@ func TestAddMetricsOutsideCurrentPeriod(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
time.Now().Add(time.Hour),
|
time.Now().Add(time.Hour),
|
||||||
)
|
)
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
@@ -82,7 +82,7 @@ func TestAddMetricsOutsideCurrentPeriod(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
time.Now().Add(time.Millisecond*50),
|
time.Now().Add(time.Millisecond*50),
|
||||||
)
|
)
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
@@ -120,7 +120,7 @@ func TestAddAndPushOnePeriod(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
time.Now().Add(time.Millisecond*100),
|
time.Now().Add(time.Millisecond*100),
|
||||||
)
|
)
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
@@ -151,7 +151,7 @@ func TestAddDropOriginal(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
time.Now(),
|
time.Now(),
|
||||||
)
|
)
|
||||||
assert.True(t, ra.Add(m))
|
assert.True(t, ra.Add(m))
|
||||||
@@ -161,7 +161,7 @@ func TestAddDropOriginal(t *testing.T) {
|
|||||||
"foobar",
|
"foobar",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
time.Now(),
|
time.Now(),
|
||||||
)
|
)
|
||||||
assert.False(t, ra.Add(m2))
|
assert.False(t, ra.Add(m2))
|
||||||
@@ -179,7 +179,7 @@ func TestMakeMetricA(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -190,14 +190,14 @@ func TestMakeMetricA(t *testing.T) {
|
|||||||
assert.Equal(
|
assert.Equal(
|
||||||
t,
|
t,
|
||||||
m.Type(),
|
m.Type(),
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
)
|
)
|
||||||
|
|
||||||
m = ra.MakeMetric(
|
m = ra.MakeMetric(
|
||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Counter,
|
plugins.Counter,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -208,14 +208,14 @@ func TestMakeMetricA(t *testing.T) {
|
|||||||
assert.Equal(
|
assert.Equal(
|
||||||
t,
|
t,
|
||||||
m.Type(),
|
m.Type(),
|
||||||
telegraf.Counter,
|
plugins.Counter,
|
||||||
)
|
)
|
||||||
|
|
||||||
m = ra.MakeMetric(
|
m = ra.MakeMetric(
|
||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Gauge,
|
plugins.Gauge,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -226,7 +226,7 @@ func TestMakeMetricA(t *testing.T) {
|
|||||||
assert.Equal(
|
assert.Equal(
|
||||||
t,
|
t,
|
||||||
m.Type(),
|
m.Type(),
|
||||||
telegraf.Gauge,
|
plugins.Gauge,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,14 +240,14 @@ func (t *TestAggregator) Reset() {
|
|||||||
atomic.StoreInt64(&t.sum, 0)
|
atomic.StoreInt64(&t.sum, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestAggregator) Push(acc telegraf.Accumulator) {
|
func (t *TestAggregator) Push(acc plugins.Accumulator) {
|
||||||
acc.AddFields("TestMetric",
|
acc.AddFields("TestMetric",
|
||||||
map[string]interface{}{"sum": t.sum},
|
map[string]interface{}{"sum": t.sum},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestAggregator) Add(in telegraf.Metric) {
|
func (t *TestAggregator) Add(in plugins.Metric) {
|
||||||
for _, v := range in.Fields() {
|
for _, v := range in.Fields() {
|
||||||
if vi, ok := v.(int64); ok {
|
if vi, ok := v.(int64); ok {
|
||||||
atomic.AddInt64(&t.sum, vi)
|
atomic.AddInt64(&t.sum, vi)
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/selfstat"
|
"github.com/influxdata/telegraf/selfstat"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GlobalMetricsGathered = selfstat.Register("agent", "metrics_gathered", map[string]string{})
|
var GlobalMetricsGathered = selfstat.Register("agent", "metrics_gathered", map[string]string{})
|
||||||
|
|
||||||
type RunningInput struct {
|
type RunningInput struct {
|
||||||
Input telegraf.Input
|
Input plugins.Input
|
||||||
Config *InputConfig
|
Config *InputConfig
|
||||||
|
|
||||||
trace bool
|
trace bool
|
||||||
@@ -21,7 +21,7 @@ type RunningInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewRunningInput(
|
func NewRunningInput(
|
||||||
input telegraf.Input,
|
input plugins.Input,
|
||||||
config *InputConfig,
|
config *InputConfig,
|
||||||
) *RunningInput {
|
) *RunningInput {
|
||||||
return &RunningInput{
|
return &RunningInput{
|
||||||
@@ -56,9 +56,9 @@ func (r *RunningInput) MakeMetric(
|
|||||||
measurement string,
|
measurement string,
|
||||||
fields map[string]interface{},
|
fields map[string]interface{},
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
mType telegraf.ValueType,
|
mType plugins.ValueType,
|
||||||
t time.Time,
|
t time.Time,
|
||||||
) telegraf.Metric {
|
) plugins.Metric {
|
||||||
m := makemetric(
|
m := makemetric(
|
||||||
measurement,
|
measurement,
|
||||||
fields,
|
fields,
|
||||||
@@ -75,7 +75,7 @@ func (r *RunningInput) MakeMetric(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if r.trace && m != nil {
|
if r.trace && m != nil {
|
||||||
fmt.Print("> " + m.String())
|
fmt.Println("> " + m.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
r.MetricsGathered.Incr(1)
|
r.MetricsGathered.Incr(1)
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMakeMetricNoFields(t *testing.T) {
|
func TestMakeMetricNoFields(t *testing.T) {
|
||||||
@@ -22,7 +21,7 @@ func TestMakeMetricNoFields(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{},
|
map[string]interface{}{},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Nil(t, m)
|
assert.Nil(t, m)
|
||||||
@@ -42,7 +41,7 @@ func TestMakeMetricNilFields(t *testing.T) {
|
|||||||
"nil": nil,
|
"nil": nil,
|
||||||
},
|
},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -67,7 +66,7 @@ func TestMakeMetric(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -78,14 +77,14 @@ func TestMakeMetric(t *testing.T) {
|
|||||||
assert.Equal(
|
assert.Equal(
|
||||||
t,
|
t,
|
||||||
m.Type(),
|
m.Type(),
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
)
|
)
|
||||||
|
|
||||||
m = ri.MakeMetric(
|
m = ri.MakeMetric(
|
||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Counter,
|
plugins.Counter,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -96,14 +95,14 @@ func TestMakeMetric(t *testing.T) {
|
|||||||
assert.Equal(
|
assert.Equal(
|
||||||
t,
|
t,
|
||||||
m.Type(),
|
m.Type(),
|
||||||
telegraf.Counter,
|
plugins.Counter,
|
||||||
)
|
)
|
||||||
|
|
||||||
m = ri.MakeMetric(
|
m = ri.MakeMetric(
|
||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Gauge,
|
plugins.Gauge,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -114,7 +113,7 @@ func TestMakeMetric(t *testing.T) {
|
|||||||
assert.Equal(
|
assert.Equal(
|
||||||
t,
|
t,
|
||||||
m.Type(),
|
m.Type(),
|
||||||
telegraf.Gauge,
|
plugins.Gauge,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@ func TestMakeMetricWithPluginTags(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
nil,
|
nil,
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -162,7 +161,7 @@ func TestMakeMetricFilteredOut(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
nil,
|
nil,
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Nil(t, m)
|
assert.Nil(t, m)
|
||||||
@@ -184,7 +183,7 @@ func TestMakeMetricWithDaemonTags(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -214,7 +213,7 @@ func TestMakeMetricInfFields(t *testing.T) {
|
|||||||
"ninf": ninf,
|
"ninf": ninf,
|
||||||
},
|
},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -251,7 +250,7 @@ func TestMakeMetricAllFieldTypes(t *testing.T) {
|
|||||||
"m": true,
|
"m": true,
|
||||||
},
|
},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Contains(t, m.String(), "a=10i")
|
assert.Contains(t, m.String(), "a=10i")
|
||||||
@@ -281,7 +280,7 @@ func TestMakeMetricNameOverride(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -302,7 +301,7 @@ func TestMakeMetricNamePrefix(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -323,7 +322,7 @@ func TestMakeMetricNameSuffix(t *testing.T) {
|
|||||||
"RITest",
|
"RITest",
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{"value": int(101)},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
plugins.Untyped,
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
@@ -333,130 +332,8 @@ func TestMakeMetricNameSuffix(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMakeMetric_TrailingSlash(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
measurement string
|
|
||||||
fields map[string]interface{}
|
|
||||||
tags map[string]string
|
|
||||||
expectedNil bool
|
|
||||||
expectedMeasurement string
|
|
||||||
expectedFields map[string]interface{}
|
|
||||||
expectedTags map[string]string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Measurement cannot have trailing slash",
|
|
||||||
measurement: `cpu\`,
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
tags: map[string]string{},
|
|
||||||
expectedNil: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Field key with trailing slash dropped",
|
|
||||||
measurement: `cpu`,
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
`bad\`: `xyzzy`,
|
|
||||||
},
|
|
||||||
tags: map[string]string{},
|
|
||||||
expectedMeasurement: `cpu`,
|
|
||||||
expectedFields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
expectedTags: map[string]string{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Field value with trailing slash dropped",
|
|
||||||
measurement: `cpu`,
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
"bad": `xyzzy\`,
|
|
||||||
},
|
|
||||||
tags: map[string]string{},
|
|
||||||
expectedMeasurement: `cpu`,
|
|
||||||
expectedFields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
expectedTags: map[string]string{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Must have one field after dropped",
|
|
||||||
measurement: `cpu`,
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"bad": `xyzzy\`,
|
|
||||||
},
|
|
||||||
tags: map[string]string{},
|
|
||||||
expectedNil: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Tag key with trailing slash dropped",
|
|
||||||
measurement: `cpu`,
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
tags: map[string]string{
|
|
||||||
`host\`: "localhost",
|
|
||||||
"a": "x",
|
|
||||||
},
|
|
||||||
expectedMeasurement: `cpu`,
|
|
||||||
expectedFields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
expectedTags: map[string]string{
|
|
||||||
"a": "x",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Tag value with trailing slash dropped",
|
|
||||||
measurement: `cpu`,
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
tags: map[string]string{
|
|
||||||
`host`: `localhost\`,
|
|
||||||
"a": "x",
|
|
||||||
},
|
|
||||||
expectedMeasurement: `cpu`,
|
|
||||||
expectedFields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
expectedTags: map[string]string{
|
|
||||||
"a": "x",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ri := NewRunningInput(&testInput{}, &InputConfig{
|
|
||||||
Name: "TestRunningInput",
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
m := ri.MakeMetric(
|
|
||||||
tc.measurement,
|
|
||||||
tc.fields,
|
|
||||||
tc.tags,
|
|
||||||
telegraf.Untyped,
|
|
||||||
now)
|
|
||||||
|
|
||||||
if tc.expectedNil {
|
|
||||||
require.Nil(t, m)
|
|
||||||
} else {
|
|
||||||
require.NotNil(t, m)
|
|
||||||
require.Equal(t, tc.expectedMeasurement, m.Name())
|
|
||||||
require.Equal(t, tc.expectedFields, m.Fields())
|
|
||||||
require.Equal(t, tc.expectedTags, m.Tags())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type testInput struct{}
|
type testInput struct{}
|
||||||
|
|
||||||
func (t *testInput) Description() string { return "" }
|
func (t *testInput) Description() string { return "" }
|
||||||
func (t *testInput) SampleConfig() string { return "" }
|
func (t *testInput) SampleConfig() string { return "" }
|
||||||
func (t *testInput) Gather(acc telegraf.Accumulator) error { return nil }
|
func (t *testInput) Gather(acc plugins.Accumulator) error { return nil }
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal/buffer"
|
"github.com/influxdata/telegraf/internal/buffer"
|
||||||
"github.com/influxdata/telegraf/metric"
|
"github.com/influxdata/telegraf/metric"
|
||||||
"github.com/influxdata/telegraf/selfstat"
|
"github.com/influxdata/telegraf/selfstat"
|
||||||
@@ -22,7 +21,7 @@ const (
|
|||||||
// RunningOutput contains the output configuration
|
// RunningOutput contains the output configuration
|
||||||
type RunningOutput struct {
|
type RunningOutput struct {
|
||||||
Name string
|
Name string
|
||||||
Output telegraf.Output
|
Output plugins.Output
|
||||||
Config *OutputConfig
|
Config *OutputConfig
|
||||||
MetricBufferLimit int
|
MetricBufferLimit int
|
||||||
MetricBatchSize int
|
MetricBatchSize int
|
||||||
@@ -35,14 +34,11 @@ type RunningOutput struct {
|
|||||||
|
|
||||||
metrics *buffer.Buffer
|
metrics *buffer.Buffer
|
||||||
failMetrics *buffer.Buffer
|
failMetrics *buffer.Buffer
|
||||||
|
|
||||||
// Guards against concurrent calls to the Output as described in #3009
|
|
||||||
sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRunningOutput(
|
func NewRunningOutput(
|
||||||
name string,
|
name string,
|
||||||
output telegraf.Output,
|
output plugins.Output,
|
||||||
conf *OutputConfig,
|
conf *OutputConfig,
|
||||||
batchSize int,
|
batchSize int,
|
||||||
bufferLimit int,
|
bufferLimit int,
|
||||||
@@ -93,10 +89,7 @@ func NewRunningOutput(
|
|||||||
|
|
||||||
// AddMetric adds a metric to the output. This function can also write cached
|
// AddMetric adds a metric to the output. This function can also write cached
|
||||||
// points if FlushBufferWhenFull is true.
|
// points if FlushBufferWhenFull is true.
|
||||||
func (ro *RunningOutput) AddMetric(m telegraf.Metric) {
|
func (ro *RunningOutput) AddMetric(m plugins.Metric) {
|
||||||
if m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Filter any tagexclude/taginclude parameters before adding metric
|
// Filter any tagexclude/taginclude parameters before adding metric
|
||||||
if ro.Config.Filter.IsActive() {
|
if ro.Config.Filter.IsActive() {
|
||||||
// In order to filter out tags, we need to create a new metric, since
|
// In order to filter out tags, we need to create a new metric, since
|
||||||
@@ -126,9 +119,9 @@ func (ro *RunningOutput) AddMetric(m telegraf.Metric) {
|
|||||||
// Write writes all cached points to this output.
|
// Write writes all cached points to this output.
|
||||||
func (ro *RunningOutput) Write() error {
|
func (ro *RunningOutput) Write() error {
|
||||||
nFails, nMetrics := ro.failMetrics.Len(), ro.metrics.Len()
|
nFails, nMetrics := ro.failMetrics.Len(), ro.metrics.Len()
|
||||||
ro.BufferSize.Set(int64(nFails + nMetrics))
|
|
||||||
log.Printf("D! Output [%s] buffer fullness: %d / %d metrics. ",
|
log.Printf("D! Output [%s] buffer fullness: %d / %d metrics. ",
|
||||||
ro.Name, nFails+nMetrics, ro.MetricBufferLimit)
|
ro.Name, nFails+nMetrics, ro.MetricBufferLimit)
|
||||||
|
ro.BufferSize.Incr(int64(nFails + nMetrics))
|
||||||
var err error
|
var err error
|
||||||
if !ro.failMetrics.IsEmpty() {
|
if !ro.failMetrics.IsEmpty() {
|
||||||
// how many batches of failed writes we need to write.
|
// how many batches of failed writes we need to write.
|
||||||
@@ -168,13 +161,11 @@ func (ro *RunningOutput) Write() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ro *RunningOutput) write(metrics []telegraf.Metric) error {
|
func (ro *RunningOutput) write(metrics []plugins.Metric) error {
|
||||||
nMetrics := len(metrics)
|
nMetrics := len(metrics)
|
||||||
if nMetrics == 0 {
|
if nMetrics == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ro.Lock()
|
|
||||||
defer ro.Unlock()
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
err := ro.Output.Write(metrics)
|
err := ro.Output.Write(metrics)
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
@@ -182,6 +173,7 @@ func (ro *RunningOutput) write(metrics []telegraf.Metric) error {
|
|||||||
log.Printf("D! Output [%s] wrote batch of %d metrics in %s\n",
|
log.Printf("D! Output [%s] wrote batch of %d metrics in %s\n",
|
||||||
ro.Name, nMetrics, elapsed)
|
ro.Name, nMetrics, elapsed)
|
||||||
ro.MetricsWritten.Incr(int64(nMetrics))
|
ro.MetricsWritten.Incr(int64(nMetrics))
|
||||||
|
ro.BufferSize.Incr(-int64(nMetrics))
|
||||||
ro.WriteTime.Incr(elapsed.Nanoseconds())
|
ro.WriteTime.Incr(elapsed.Nanoseconds())
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var first5 = []telegraf.Metric{
|
var first5 = []plugins.Metric{
|
||||||
testutil.TestMetric(101, "metric1"),
|
testutil.TestMetric(101, "metric1"),
|
||||||
testutil.TestMetric(101, "metric2"),
|
testutil.TestMetric(101, "metric2"),
|
||||||
testutil.TestMetric(101, "metric3"),
|
testutil.TestMetric(101, "metric3"),
|
||||||
@@ -20,7 +20,7 @@ var first5 = []telegraf.Metric{
|
|||||||
testutil.TestMetric(101, "metric5"),
|
testutil.TestMetric(101, "metric5"),
|
||||||
}
|
}
|
||||||
|
|
||||||
var next5 = []telegraf.Metric{
|
var next5 = []plugins.Metric{
|
||||||
testutil.TestMetric(101, "metric6"),
|
testutil.TestMetric(101, "metric6"),
|
||||||
testutil.TestMetric(101, "metric7"),
|
testutil.TestMetric(101, "metric7"),
|
||||||
testutil.TestMetric(101, "metric8"),
|
testutil.TestMetric(101, "metric8"),
|
||||||
@@ -75,23 +75,6 @@ func BenchmarkRunningOutputAddFailWrites(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddingNilMetric(t *testing.T) {
|
|
||||||
conf := &OutputConfig{
|
|
||||||
Filter: Filter{},
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &mockOutput{}
|
|
||||||
ro := NewRunningOutput("test", m, conf, 1000, 10000)
|
|
||||||
|
|
||||||
ro.AddMetric(nil)
|
|
||||||
ro.AddMetric(nil)
|
|
||||||
ro.AddMetric(nil)
|
|
||||||
|
|
||||||
err := ro.Write()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Len(t, m.Metrics(), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that NameDrop filters ger properly applied.
|
// Test that NameDrop filters ger properly applied.
|
||||||
func TestRunningOutput_DropFilter(t *testing.T) {
|
func TestRunningOutput_DropFilter(t *testing.T) {
|
||||||
conf := &OutputConfig{
|
conf := &OutputConfig{
|
||||||
@@ -482,7 +465,7 @@ func TestRunningOutputWriteFailOrder3(t *testing.T) {
|
|||||||
type mockOutput struct {
|
type mockOutput struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
||||||
metrics []telegraf.Metric
|
metrics []plugins.Metric
|
||||||
|
|
||||||
// if true, mock a write failure
|
// if true, mock a write failure
|
||||||
failWrite bool
|
failWrite bool
|
||||||
@@ -504,7 +487,7 @@ func (m *mockOutput) SampleConfig() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockOutput) Write(metrics []telegraf.Metric) error {
|
func (m *mockOutput) Write(metrics []plugins.Metric) error {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
if m.failWrite {
|
if m.failWrite {
|
||||||
@@ -512,7 +495,7 @@ func (m *mockOutput) Write(metrics []telegraf.Metric) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.metrics == nil {
|
if m.metrics == nil {
|
||||||
m.metrics = []telegraf.Metric{}
|
m.metrics = []plugins.Metric{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, metric := range metrics {
|
for _, metric := range metrics {
|
||||||
@@ -521,7 +504,7 @@ func (m *mockOutput) Write(metrics []telegraf.Metric) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockOutput) Metrics() []telegraf.Metric {
|
func (m *mockOutput) Metrics() []plugins.Metric {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
return m.metrics
|
return m.metrics
|
||||||
@@ -548,7 +531,7 @@ func (m *perfOutput) SampleConfig() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *perfOutput) Write(metrics []telegraf.Metric) error {
|
func (m *perfOutput) Write(metrics []plugins.Metric) error {
|
||||||
if m.failWrite {
|
if m.failWrite {
|
||||||
return fmt.Errorf("Failed Write!")
|
return fmt.Errorf("Failed Write!")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RunningProcessor struct {
|
type RunningProcessor struct {
|
||||||
Name string
|
Name string
|
||||||
Processor telegraf.Processor
|
Processor plugins.Processor
|
||||||
Config *ProcessorConfig
|
Config *ProcessorConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,8 +23,8 @@ type ProcessorConfig struct {
|
|||||||
Filter Filter
|
Filter Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rp *RunningProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
func (rp *RunningProcessor) Apply(in ...plugins.Metric) []plugins.Metric {
|
||||||
ret := []telegraf.Metric{}
|
ret := []plugins.Metric{}
|
||||||
|
|
||||||
for _, metric := range in {
|
for _, metric := range in {
|
||||||
if rp.Config.Filter.IsActive() {
|
if rp.Config.Filter.IsActive() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package models
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -19,8 +19,8 @@ func (f *TestProcessor) Description() string { return "" }
|
|||||||
// "foo" to "fuz"
|
// "foo" to "fuz"
|
||||||
// "bar" to "baz"
|
// "bar" to "baz"
|
||||||
// And it also drops measurements named "dropme"
|
// And it also drops measurements named "dropme"
|
||||||
func (f *TestProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
func (f *TestProcessor) Apply(in ...plugins.Metric) []plugins.Metric {
|
||||||
out := make([]telegraf.Metric, 0)
|
out := make([]plugins.Metric, 0)
|
||||||
for _, m := range in {
|
for _, m := range in {
|
||||||
switch m.Name() {
|
switch m.Name() {
|
||||||
case "foo":
|
case "foo":
|
||||||
@@ -46,7 +46,7 @@ func NewTestRunningProcessor() *RunningProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRunningProcessor(t *testing.T) {
|
func TestRunningProcessor(t *testing.T) {
|
||||||
inmetrics := []telegraf.Metric{
|
inmetrics := []plugins.Metric{
|
||||||
testutil.TestMetric(1, "foo"),
|
testutil.TestMetric(1, "foo"),
|
||||||
testutil.TestMetric(1, "bar"),
|
testutil.TestMetric(1, "bar"),
|
||||||
testutil.TestMetric(1, "baz"),
|
testutil.TestMetric(1, "baz"),
|
||||||
@@ -69,7 +69,7 @@ func TestRunningProcessor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRunningProcessor_WithNameDrop(t *testing.T) {
|
func TestRunningProcessor_WithNameDrop(t *testing.T) {
|
||||||
inmetrics := []telegraf.Metric{
|
inmetrics := []plugins.Metric{
|
||||||
testutil.TestMetric(1, "foo"),
|
testutil.TestMetric(1, "foo"),
|
||||||
testutil.TestMetric(1, "bar"),
|
testutil.TestMetric(1, "bar"),
|
||||||
testutil.TestMetric(1, "baz"),
|
testutil.TestMetric(1, "baz"),
|
||||||
@@ -96,7 +96,7 @@ func TestRunningProcessor_WithNameDrop(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRunningProcessor_DroppedMetric(t *testing.T) {
|
func TestRunningProcessor_DroppedMetric(t *testing.T) {
|
||||||
inmetrics := []telegraf.Metric{
|
inmetrics := []plugins.Metric{
|
||||||
testutil.TestMetric(1, "dropme"),
|
testutil.TestMetric(1, "dropme"),
|
||||||
testutil.TestMetric(1, "foo"),
|
testutil.TestMetric(1, "foo"),
|
||||||
testutil.TestMetric(1, "bar"),
|
testutil.TestMetric(1, "bar"),
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/wlog"
|
"github.com/influxdata/wlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var prefixRegex = regexp.MustCompile("^[DIWE]!")
|
|
||||||
|
|
||||||
// newTelegrafWriter returns a logging-wrapped writer.
|
// newTelegrafWriter returns a logging-wrapped writer.
|
||||||
func newTelegrafWriter(w io.Writer) io.Writer {
|
func newTelegrafWriter(w io.Writer) io.Writer {
|
||||||
return &telegrafLog{
|
return &telegrafLog{
|
||||||
@@ -24,13 +21,7 @@ type telegrafLog struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *telegrafLog) Write(b []byte) (n int, err error) {
|
func (t *telegrafLog) Write(b []byte) (n int, err error) {
|
||||||
var line []byte
|
return t.writer.Write(append([]byte(time.Now().UTC().Format(time.RFC3339)+" "), b...))
|
||||||
if !prefixRegex.Match(b) {
|
|
||||||
line = append([]byte(time.Now().UTC().Format(time.RFC3339)+" I! "), b...)
|
|
||||||
} else {
|
|
||||||
line = append([]byte(time.Now().UTC().Format(time.RFC3339)+" "), b...)
|
|
||||||
}
|
|
||||||
return t.writer.Write(line)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupLogging configures the logging output.
|
// SetupLogging configures the logging output.
|
||||||
|
|||||||
@@ -51,19 +51,6 @@ func TestErrorWriteLogToFile(t *testing.T) {
|
|||||||
assert.Equal(t, f[19:], []byte("Z E! TEST\n"))
|
assert.Equal(t, f[19:], []byte("Z E! TEST\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddDefaultLogLevel(t *testing.T) {
|
|
||||||
tmpfile, err := ioutil.TempFile("", "")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
defer func() { os.Remove(tmpfile.Name()) }()
|
|
||||||
|
|
||||||
SetupLogging(true, false, tmpfile.Name())
|
|
||||||
log.Printf("TEST")
|
|
||||||
|
|
||||||
f, err := ioutil.ReadFile(tmpfile.Name())
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, f[19:], []byte("Z I! TEST\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkTelegrafLogWrite(b *testing.B) {
|
func BenchmarkTelegrafLogWrite(b *testing.B) {
|
||||||
var msg = []byte("test")
|
var msg = []byte("test")
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|||||||
130
metric/metric.go
130
metric/metric.go
@@ -6,10 +6,12 @@ import (
|
|||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
|
|
||||||
|
// TODO remove
|
||||||
|
"github.com/influxdata/influxdb/client/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MaxInt = int(^uint(0) >> 1)
|
const MaxInt = int(^uint(0) >> 1)
|
||||||
@@ -19,23 +21,20 @@ func New(
|
|||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
fields map[string]interface{},
|
fields map[string]interface{},
|
||||||
t time.Time,
|
t time.Time,
|
||||||
mType ...telegraf.ValueType,
|
mType ...plugins.ValueType,
|
||||||
) (telegraf.Metric, error) {
|
) (plugins.Metric, error) {
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
return nil, fmt.Errorf("Metric cannot be made without any fields")
|
return nil, fmt.Errorf("Metric cannot be made without any fields")
|
||||||
}
|
}
|
||||||
if len(name) == 0 {
|
if len(name) == 0 {
|
||||||
return nil, fmt.Errorf("Metric cannot be made with an empty name")
|
return nil, fmt.Errorf("Metric cannot be made with an empty name")
|
||||||
}
|
}
|
||||||
if strings.HasSuffix(name, `\`) {
|
|
||||||
return nil, fmt.Errorf("Metric cannot have measurement name ending with a backslash")
|
|
||||||
}
|
|
||||||
|
|
||||||
var thisType telegraf.ValueType
|
var thisType plugins.ValueType
|
||||||
if len(mType) > 0 {
|
if len(mType) > 0 {
|
||||||
thisType = mType[0]
|
thisType = mType[0]
|
||||||
} else {
|
} else {
|
||||||
thisType = telegraf.Untyped
|
thisType = plugins.Untyped
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &metric{
|
m := &metric{
|
||||||
@@ -48,25 +47,13 @@ func New(
|
|||||||
// pre-allocate exact size of the tags slice
|
// pre-allocate exact size of the tags slice
|
||||||
taglen := 0
|
taglen := 0
|
||||||
for k, v := range tags {
|
for k, v := range tags {
|
||||||
if strings.HasSuffix(k, `\`) {
|
// TODO check that length of tag key & value are > 0
|
||||||
return nil, fmt.Errorf("Metric cannot have tag key ending with a backslash")
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(v, `\`) {
|
|
||||||
return nil, fmt.Errorf("Metric cannot have tag value ending with a backslash")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(k) == 0 || len(v) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
taglen += 2 + len(escape(k, "tagkey")) + len(escape(v, "tagval"))
|
taglen += 2 + len(escape(k, "tagkey")) + len(escape(v, "tagval"))
|
||||||
}
|
}
|
||||||
m.tags = make([]byte, taglen)
|
m.tags = make([]byte, taglen)
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
for k, v := range tags {
|
for k, v := range tags {
|
||||||
if len(k) == 0 || len(v) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m.tags[i] = ','
|
m.tags[i] = ','
|
||||||
i++
|
i++
|
||||||
i += copy(m.tags[i:], escape(k, "tagkey"))
|
i += copy(m.tags[i:], escape(k, "tagkey"))
|
||||||
@@ -77,17 +64,7 @@ func New(
|
|||||||
|
|
||||||
// pre-allocate capacity of the fields slice
|
// pre-allocate capacity of the fields slice
|
||||||
fieldlen := 0
|
fieldlen := 0
|
||||||
for k, v := range fields {
|
for k, _ := range fields {
|
||||||
if strings.HasSuffix(k, `\`) {
|
|
||||||
return nil, fmt.Errorf("Metric cannot have field key ending with a backslash")
|
|
||||||
}
|
|
||||||
switch val := v.(type) {
|
|
||||||
case string:
|
|
||||||
if strings.HasSuffix(val, `\`) {
|
|
||||||
return nil, fmt.Errorf("Metric cannot have field value ending with a backslash")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10 bytes is completely arbitrary, but will at least prevent some
|
// 10 bytes is completely arbitrary, but will at least prevent some
|
||||||
// amount of allocations. There's a small possibility this will create
|
// amount of allocations. There's a small possibility this will create
|
||||||
// slightly more allocations for a metric that has many short fields.
|
// slightly more allocations for a metric that has many short fields.
|
||||||
@@ -119,7 +96,7 @@ func indexUnescapedByte(buf []byte, b byte) int {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
keyi += i
|
keyi += i
|
||||||
if buf[keyi-1] != '\\' {
|
if countBackslashes(buf, keyi-1)%2 == 0 {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
keyi++
|
keyi++
|
||||||
@@ -128,13 +105,31 @@ func indexUnescapedByte(buf []byte, b byte) int {
|
|||||||
return keyi
|
return keyi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// countBackslashes counts the number of preceding backslashes starting at
|
||||||
|
// the 'start' index.
|
||||||
|
func countBackslashes(buf []byte, index int) int {
|
||||||
|
var count int
|
||||||
|
for {
|
||||||
|
if index < 0 {
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
if buf[index] == '\\' {
|
||||||
|
count++
|
||||||
|
index--
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
type metric struct {
|
type metric struct {
|
||||||
name []byte
|
name []byte
|
||||||
tags []byte
|
tags []byte
|
||||||
fields []byte
|
fields []byte
|
||||||
t []byte
|
t []byte
|
||||||
|
|
||||||
mType telegraf.ValueType
|
mType plugins.ValueType
|
||||||
aggregate bool
|
aggregate bool
|
||||||
|
|
||||||
// cached values for reuse in "get" functions
|
// cached values for reuse in "get" functions
|
||||||
@@ -142,6 +137,11 @@ type metric struct {
|
|||||||
nsec int64
|
nsec int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *metric) Point() *client.Point {
|
||||||
|
c, _ := client.NewPoint(m.Name(), m.Tags(), m.Fields(), m.Time())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func (m *metric) String() string {
|
func (m *metric) String() string {
|
||||||
return string(m.name) + string(m.tags) + " " + string(m.fields) + " " + string(m.t) + "\n"
|
return string(m.name) + string(m.tags) + " " + string(m.fields) + " " + string(m.t) + "\n"
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ func (m *metric) IsAggregate() bool {
|
|||||||
return m.aggregate
|
return m.aggregate
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metric) Type() telegraf.ValueType {
|
func (m *metric) Type() plugins.ValueType {
|
||||||
return m.mType
|
return m.mType
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,53 +178,11 @@ func (m *metric) Serialize() []byte {
|
|||||||
return tmp
|
return tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metric) SerializeTo(dst []byte) int {
|
func (m *metric) Split(maxSize int) []plugins.Metric {
|
||||||
i := 0
|
if m.Len() < maxSize {
|
||||||
if i >= len(dst) {
|
return []plugins.Metric{m}
|
||||||
return i
|
|
||||||
}
|
}
|
||||||
|
var out []plugins.Metric
|
||||||
i += copy(dst[i:], m.name)
|
|
||||||
if i >= len(dst) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
i += copy(dst[i:], m.tags)
|
|
||||||
if i >= len(dst) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
dst[i] = ' '
|
|
||||||
i++
|
|
||||||
if i >= len(dst) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
i += copy(dst[i:], m.fields)
|
|
||||||
if i >= len(dst) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
dst[i] = ' '
|
|
||||||
i++
|
|
||||||
if i >= len(dst) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
i += copy(dst[i:], m.t)
|
|
||||||
if i >= len(dst) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
dst[i] = '\n'
|
|
||||||
|
|
||||||
return i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *metric) Split(maxSize int) []telegraf.Metric {
|
|
||||||
if m.Len() <= maxSize {
|
|
||||||
return []telegraf.Metric{m}
|
|
||||||
}
|
|
||||||
var out []telegraf.Metric
|
|
||||||
|
|
||||||
// constant number of bytes for each metric (in addition to field bytes)
|
// constant number of bytes for each metric (in addition to field bytes)
|
||||||
constant := len(m.name) + len(m.tags) + len(m.t) + 3
|
constant := len(m.name) + len(m.tags) + len(m.t) + 3
|
||||||
@@ -251,7 +209,7 @@ func (m *metric) Split(maxSize int) []telegraf.Metric {
|
|||||||
|
|
||||||
// if true, then we need to create a metric _not_ including the currently
|
// if true, then we need to create a metric _not_ including the currently
|
||||||
// selected field
|
// selected field
|
||||||
if len(m.fields[i:j])+len(fields)+constant >= maxSize {
|
if len(m.fields[i:j])+len(fields)+constant > maxSize {
|
||||||
// if false, then we'll create a metric including the currently
|
// if false, then we'll create a metric including the currently
|
||||||
// selected field anyways. This means that the given maxSize is too
|
// selected field anyways. This means that the given maxSize is too
|
||||||
// small for a single field to fit.
|
// small for a single field to fit.
|
||||||
@@ -305,7 +263,7 @@ func (m *metric) Fields() map[string]interface{} {
|
|||||||
case '"':
|
case '"':
|
||||||
// string field
|
// string field
|
||||||
fieldMap[unescape(string(m.fields[i:][0:i1]), "fieldkey")] = unescape(string(m.fields[i:][i2+1:i3-1]), "fieldval")
|
fieldMap[unescape(string(m.fields[i:][0:i1]), "fieldkey")] = unescape(string(m.fields[i:][i2+1:i3-1]), "fieldval")
|
||||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
// number field
|
// number field
|
||||||
switch m.fields[i:][i3-1] {
|
switch m.fields[i:][i3-1] {
|
||||||
case 'i':
|
case 'i':
|
||||||
@@ -472,11 +430,11 @@ func (m *metric) RemoveField(key string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metric) Copy() telegraf.Metric {
|
func (m *metric) Copy() plugins.Metric {
|
||||||
return copyWith(m.name, m.tags, m.fields, m.t)
|
return copyWith(m.name, m.tags, m.fields, m.t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyWith(name, tags, fields, t []byte) telegraf.Metric {
|
func copyWith(name, tags, fields, t []byte) plugins.Metric {
|
||||||
out := metric{
|
out := metric{
|
||||||
name: make([]byte, len(name)),
|
name: make([]byte, len(name)),
|
||||||
tags: make([]byte, len(tags)),
|
tags: make([]byte, len(tags)),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
// vars for making sure that the compiler doesnt optimize out the benchmarks:
|
// vars for making sure that the compiler doesnt optimize out the benchmarks:
|
||||||
@@ -17,7 +17,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkNewMetric(b *testing.B) {
|
func BenchmarkNewMetric(b *testing.B) {
|
||||||
var mt telegraf.Metric
|
var mt plugins.Metric
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
mt, _ = New("test_metric",
|
mt, _ = New("test_metric",
|
||||||
map[string]string{
|
map[string]string{
|
||||||
@@ -37,7 +37,7 @@ func BenchmarkNewMetric(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkAddTag(b *testing.B) {
|
func BenchmarkAddTag(b *testing.B) {
|
||||||
var mt telegraf.Metric
|
var mt plugins.Metric
|
||||||
mt = &metric{
|
mt = &metric{
|
||||||
name: []byte("cpu"),
|
name: []byte("cpu"),
|
||||||
tags: []byte(",host=localhost"),
|
tags: []byte(",host=localhost"),
|
||||||
@@ -51,14 +51,14 @@ func BenchmarkAddTag(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkSplit(b *testing.B) {
|
func BenchmarkSplit(b *testing.B) {
|
||||||
var mt telegraf.Metric
|
var mt plugins.Metric
|
||||||
mt = &metric{
|
mt = &metric{
|
||||||
name: []byte("cpu"),
|
name: []byte("cpu"),
|
||||||
tags: []byte(",host=localhost"),
|
tags: []byte(",host=localhost"),
|
||||||
fields: []byte("a=101,b=10i,c=10101,d=101010,e=42"),
|
fields: []byte("a=101,b=10i,c=10101,d=101010,e=42"),
|
||||||
t: []byte("1480614053000000000"),
|
t: []byte("1480614053000000000"),
|
||||||
}
|
}
|
||||||
var metrics []telegraf.Metric
|
var metrics []plugins.Metric
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
metrics = mt.Split(60)
|
metrics = mt.Split(60)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewMetric(t *testing.T) {
|
func TestNewMetric(t *testing.T) {
|
||||||
@@ -27,7 +26,7 @@ func TestNewMetric(t *testing.T) {
|
|||||||
m, err := New("cpu", tags, fields, now)
|
m, err := New("cpu", tags, fields, now)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, telegraf.Untyped, m.Type())
|
assert.Equal(t, plugins.Untyped, m.Type())
|
||||||
assert.Equal(t, tags, m.Tags())
|
assert.Equal(t, tags, m.Tags())
|
||||||
assert.Equal(t, fields, m.Fields())
|
assert.Equal(t, fields, m.Fields())
|
||||||
assert.Equal(t, "cpu", m.Name())
|
assert.Equal(t, "cpu", m.Name())
|
||||||
@@ -255,8 +254,6 @@ func TestNewMetric_Fields(t *testing.T) {
|
|||||||
"bool": true,
|
"bool": true,
|
||||||
"false": false,
|
"false": false,
|
||||||
"string": "test",
|
"string": "test",
|
||||||
"quote_string": `x"y`,
|
|
||||||
"backslash_quote_string": `x\"y`,
|
|
||||||
}
|
}
|
||||||
m, err := New("cpu", tags, fields, now)
|
m, err := New("cpu", tags, fields, now)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -369,7 +366,7 @@ func TestIndexUnescapedByte(t *testing.T) {
|
|||||||
{
|
{
|
||||||
in: []byte(`foo\\bar`),
|
in: []byte(`foo\\bar`),
|
||||||
b: 'b',
|
b: 'b',
|
||||||
expected: -1,
|
expected: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: []byte(`foobar`),
|
in: []byte(`foobar`),
|
||||||
@@ -405,10 +402,10 @@ func TestNewGaugeMetric(t *testing.T) {
|
|||||||
"usage_idle": float64(99),
|
"usage_idle": float64(99),
|
||||||
"usage_busy": float64(1),
|
"usage_busy": float64(1),
|
||||||
}
|
}
|
||||||
m, err := New("cpu", tags, fields, now, telegraf.Gauge)
|
m, err := New("cpu", tags, fields, now, plugins.Gauge)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, telegraf.Gauge, m.Type())
|
assert.Equal(t, plugins.Gauge, m.Type())
|
||||||
assert.Equal(t, tags, m.Tags())
|
assert.Equal(t, tags, m.Tags())
|
||||||
assert.Equal(t, fields, m.Fields())
|
assert.Equal(t, fields, m.Fields())
|
||||||
assert.Equal(t, "cpu", m.Name())
|
assert.Equal(t, "cpu", m.Name())
|
||||||
@@ -427,10 +424,10 @@ func TestNewCounterMetric(t *testing.T) {
|
|||||||
"usage_idle": float64(99),
|
"usage_idle": float64(99),
|
||||||
"usage_busy": float64(1),
|
"usage_busy": float64(1),
|
||||||
}
|
}
|
||||||
m, err := New("cpu", tags, fields, now, telegraf.Counter)
|
m, err := New("cpu", tags, fields, now, plugins.Counter)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, telegraf.Counter, m.Type())
|
assert.Equal(t, plugins.Counter, m.Type())
|
||||||
assert.Equal(t, tags, m.Tags())
|
assert.Equal(t, tags, m.Tags())
|
||||||
assert.Equal(t, fields, m.Fields())
|
assert.Equal(t, fields, m.Fields())
|
||||||
assert.Equal(t, "cpu", m.Name())
|
assert.Equal(t, "cpu", m.Name())
|
||||||
@@ -461,7 +458,7 @@ func TestSplitMetric(t *testing.T) {
|
|||||||
assert.Len(t, split70, 3)
|
assert.Len(t, split70, 3)
|
||||||
|
|
||||||
split60 := m.Split(60)
|
split60 := m.Split(60)
|
||||||
assert.Len(t, split60, 5)
|
assert.Len(t, split60, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test splitting metric into various max lengths
|
// test splitting metric into various max lengths
|
||||||
@@ -581,42 +578,6 @@ func TestSplitMetric_OneField(t *testing.T) {
|
|||||||
assert.Equal(t, "cpu,host=localhost float=100001 1480940990034083306\n", split[0].String())
|
assert.Equal(t, "cpu,host=localhost float=100001 1480940990034083306\n", split[0].String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSplitMetric_ExactSize(t *testing.T) {
|
|
||||||
now := time.Unix(0, 1480940990034083306)
|
|
||||||
tags := map[string]string{
|
|
||||||
"host": "localhost",
|
|
||||||
}
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"float": float64(100001),
|
|
||||||
"int": int64(100001),
|
|
||||||
"bool": true,
|
|
||||||
"false": false,
|
|
||||||
"string": "test",
|
|
||||||
}
|
|
||||||
m, err := New("cpu", tags, fields, now)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
actual := m.Split(m.Len())
|
|
||||||
// check that no copy was made
|
|
||||||
require.Equal(t, &m, &actual[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitMetric_NoRoomForNewline(t *testing.T) {
|
|
||||||
now := time.Unix(0, 1480940990034083306)
|
|
||||||
tags := map[string]string{
|
|
||||||
"host": "localhost",
|
|
||||||
}
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"float": float64(100001),
|
|
||||||
"int": int64(100001),
|
|
||||||
"bool": true,
|
|
||||||
"false": false,
|
|
||||||
}
|
|
||||||
m, err := New("cpu", tags, fields, now)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
actual := m.Split(m.Len() - 1)
|
|
||||||
require.Equal(t, 2, len(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewMetricAggregate(t *testing.T) {
|
func TestNewMetricAggregate(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
@@ -634,6 +595,25 @@ func TestNewMetricAggregate(t *testing.T) {
|
|||||||
assert.True(t, m.IsAggregate())
|
assert.True(t, m.IsAggregate())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewMetricPoint(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
tags := map[string]string{
|
||||||
|
"host": "localhost",
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"usage_idle": float64(99),
|
||||||
|
}
|
||||||
|
m, err := New("cpu", tags, fields, now)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
p := m.Point()
|
||||||
|
|
||||||
|
assert.Equal(t, fields, m.Fields())
|
||||||
|
assert.Equal(t, fields, p.Fields())
|
||||||
|
assert.Equal(t, "cpu", p.Name())
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewMetricString(t *testing.T) {
|
func TestNewMetricString(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
@@ -664,78 +644,3 @@ func TestNewMetricFailNaN(t *testing.T) {
|
|||||||
_, err := New("cpu", tags, fields, now)
|
_, err := New("cpu", tags, fields, now)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyTagValueOrKey(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
tags := map[string]string{
|
|
||||||
"host": "localhost",
|
|
||||||
"emptytag": "",
|
|
||||||
"": "valuewithoutkey",
|
|
||||||
}
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage_idle": float64(99),
|
|
||||||
}
|
|
||||||
m, err := New("cpu", tags, fields, now)
|
|
||||||
|
|
||||||
assert.True(t, m.HasTag("host"))
|
|
||||||
assert.False(t, m.HasTag("emptytag"))
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("cpu,host=localhost usage_idle=99 %d\n", now.UnixNano()),
|
|
||||||
m.String())
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewMetric_TrailingSlash(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
tags map[string]string
|
|
||||||
fields map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: `cpu\`,
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cpu",
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
`value\`: "x",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cpu",
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": `x\`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cpu",
|
|
||||||
tags: map[string]string{
|
|
||||||
`host\`: "localhost",
|
|
||||||
},
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cpu",
|
|
||||||
tags: map[string]string{
|
|
||||||
"host": `localhost\`,
|
|
||||||
},
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"value": int64(42),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
_, err := New(tc.name, tc.tags, tc.fields, now)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -40,26 +39,15 @@ const (
|
|||||||
fieldsState
|
fieldsState
|
||||||
)
|
)
|
||||||
|
|
||||||
func Parse(buf []byte) ([]telegraf.Metric, error) {
|
func Parse(buf []byte) ([]plugins.Metric, error) {
|
||||||
return ParseWithDefaultTimePrecision(buf, time.Now(), "")
|
return ParseWithDefaultTime(buf, time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseWithDefaultTime(buf []byte, t time.Time) ([]telegraf.Metric, error) {
|
func ParseWithDefaultTime(buf []byte, t time.Time) ([]plugins.Metric, error) {
|
||||||
return ParseWithDefaultTimePrecision(buf, t, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseWithDefaultTimePrecision(
|
|
||||||
buf []byte,
|
|
||||||
t time.Time,
|
|
||||||
precision string,
|
|
||||||
) ([]telegraf.Metric, error) {
|
|
||||||
if len(buf) == 0 {
|
|
||||||
return []telegraf.Metric{}, nil
|
|
||||||
}
|
|
||||||
if len(buf) <= 6 {
|
if len(buf) <= 6 {
|
||||||
return []telegraf.Metric{}, makeError("buffer too short", buf, 0)
|
return []plugins.Metric{}, makeError("buffer too short", buf, 0)
|
||||||
}
|
}
|
||||||
metrics := make([]telegraf.Metric, 0, bytes.Count(buf, []byte("\n"))+1)
|
metrics := make([]plugins.Metric, 0, bytes.Count(buf, []byte("\n"))+1)
|
||||||
var errStr string
|
var errStr string
|
||||||
i := 0
|
i := 0
|
||||||
for {
|
for {
|
||||||
@@ -72,7 +60,7 @@ func ParseWithDefaultTimePrecision(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := parseMetric(buf[i:i+j], t, precision)
|
m, err := parseMetric(buf[i:i+j], t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
i += j + 1 // increment i past the previous newline
|
i += j + 1 // increment i past the previous newline
|
||||||
errStr += " " + err.Error()
|
errStr += " " + err.Error()
|
||||||
@@ -89,10 +77,7 @@ func ParseWithDefaultTimePrecision(
|
|||||||
return metrics, nil
|
return metrics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMetric(buf []byte,
|
func parseMetric(buf []byte, defaultTime time.Time) (plugins.Metric, error) {
|
||||||
defaultTime time.Time,
|
|
||||||
precision string,
|
|
||||||
) (telegraf.Metric, error) {
|
|
||||||
var dTime string
|
var dTime string
|
||||||
// scan the first block which is measurement[,tag1=value1,tag2=value=2...]
|
// scan the first block which is measurement[,tag1=value1,tag2=value=2...]
|
||||||
pos, key, err := scanKey(buf, 0)
|
pos, key, err := scanKey(buf, 0)
|
||||||
@@ -126,23 +111,9 @@ func parseMetric(buf []byte,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply precision multiplier
|
|
||||||
var nsec int64
|
|
||||||
multiplier := getPrecisionMultiplier(precision)
|
|
||||||
if len(ts) > 0 && multiplier > 1 {
|
|
||||||
tsint, err := parseIntBytes(ts, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nsec := multiplier * tsint
|
|
||||||
ts = []byte(strconv.FormatInt(nsec, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &metric{
|
m := &metric{
|
||||||
fields: fields,
|
fields: fields,
|
||||||
t: ts,
|
t: ts,
|
||||||
nsec: nsec,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse out the measurement name
|
// parse out the measurement name
|
||||||
@@ -654,21 +625,3 @@ func makeError(reason string, buf []byte, i int) error {
|
|||||||
return fmt.Errorf("metric parsing error, reason: [%s], buffer: [%s], index: [%d]",
|
return fmt.Errorf("metric parsing error, reason: [%s], buffer: [%s], index: [%d]",
|
||||||
reason, buf, i)
|
reason, buf, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPrecisionMultiplier will return a multiplier for the precision specified.
|
|
||||||
func getPrecisionMultiplier(precision string) int64 {
|
|
||||||
d := time.Nanosecond
|
|
||||||
switch precision {
|
|
||||||
case "u":
|
|
||||||
d = time.Microsecond
|
|
||||||
case "ms":
|
|
||||||
d = time.Millisecond
|
|
||||||
case "s":
|
|
||||||
d = time.Second
|
|
||||||
case "m":
|
|
||||||
d = time.Minute
|
|
||||||
case "h":
|
|
||||||
d = time.Hour
|
|
||||||
}
|
|
||||||
return int64(d)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -44,9 +44,6 @@ cpu,host=foo,datacenter=us-east idle=99,busy=1i,b=true,s="string"
|
|||||||
cpu,host=foo,datacenter=us-east idle=99,busy=1i,b=true,s="string"
|
cpu,host=foo,datacenter=us-east idle=99,busy=1i,b=true,s="string"
|
||||||
`
|
`
|
||||||
|
|
||||||
const negMetrics = `weather,host=local temp=-99i,temp_float=-99.4 1465839830100400200
|
|
||||||
`
|
|
||||||
|
|
||||||
// some metrics are invalid
|
// some metrics are invalid
|
||||||
const someInvalid = `cpu,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
const someInvalid = `cpu,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
||||||
cpu,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
cpu,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
||||||
@@ -88,26 +85,6 @@ func TestParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseNegNumbers(t *testing.T) {
|
|
||||||
metrics, err := Parse([]byte(negMetrics))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Len(t, metrics, 1)
|
|
||||||
|
|
||||||
assert.Equal(t,
|
|
||||||
map[string]interface{}{
|
|
||||||
"temp": int64(-99),
|
|
||||||
"temp_float": float64(-99.4),
|
|
||||||
},
|
|
||||||
metrics[0].Fields(),
|
|
||||||
)
|
|
||||||
assert.Equal(t,
|
|
||||||
map[string]string{
|
|
||||||
"host": "local",
|
|
||||||
},
|
|
||||||
metrics[0].Tags(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseErrors(t *testing.T) {
|
func TestParseErrors(t *testing.T) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
metrics, err := Parse([]byte(someInvalid))
|
metrics, err := Parse([]byte(someInvalid))
|
||||||
@@ -364,41 +341,6 @@ func TestParseNegativeTimestamps(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParsePrecision(t *testing.T) {
|
|
||||||
for _, tt := range []struct {
|
|
||||||
line string
|
|
||||||
precision string
|
|
||||||
expected int64
|
|
||||||
}{
|
|
||||||
{"test v=42 1491847420", "s", 1491847420000000000},
|
|
||||||
{"test v=42 1491847420123", "ms", 1491847420123000000},
|
|
||||||
{"test v=42 1491847420123456", "u", 1491847420123456000},
|
|
||||||
{"test v=42 1491847420123456789", "ns", 1491847420123456789},
|
|
||||||
|
|
||||||
{"test v=42 1491847420123456789", "1s", 1491847420123456789},
|
|
||||||
{"test v=42 1491847420123456789", "asdf", 1491847420123456789},
|
|
||||||
} {
|
|
||||||
metrics, err := ParseWithDefaultTimePrecision(
|
|
||||||
[]byte(tt.line+"\n"), time.Now(), tt.precision)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, tt.expected, metrics[0].UnixNano())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePrecisionUnsetTime(t *testing.T) {
|
|
||||||
for _, tt := range []struct {
|
|
||||||
line string
|
|
||||||
precision string
|
|
||||||
}{
|
|
||||||
{"test v=42", "s"},
|
|
||||||
{"test v=42", "ns"},
|
|
||||||
} {
|
|
||||||
_, err := ParseWithDefaultTimePrecision(
|
|
||||||
[]byte(tt.line+"\n"), time.Now(), tt.precision)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseMaxKeyLength(t *testing.T) {
|
func TestParseMaxKeyLength(t *testing.T) {
|
||||||
key := ""
|
key := ""
|
||||||
for {
|
for {
|
||||||
|
|||||||
159
metric/reader.go
159
metric/reader.go
@@ -1,159 +0,0 @@
|
|||||||
package metric
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
)
|
|
||||||
|
|
||||||
type state int
|
|
||||||
|
|
||||||
const (
|
|
||||||
_ state = iota
|
|
||||||
// normal state copies whole metrics into the given buffer until we can't
|
|
||||||
// fit the next metric.
|
|
||||||
normal
|
|
||||||
// split state means that we have a metric that we were able to split, so
|
|
||||||
// that we can fit it into multiple metrics (and calls to Read)
|
|
||||||
split
|
|
||||||
// overflow state means that we have a metric that didn't fit into a single
|
|
||||||
// buffer, and needs to be split across multiple calls to Read.
|
|
||||||
overflow
|
|
||||||
// splitOverflow state means that a split metric didn't fit into a single
|
|
||||||
// buffer, and needs to be split across multiple calls to Read.
|
|
||||||
splitOverflow
|
|
||||||
// done means we're done reading metrics, and now always return (0, io.EOF)
|
|
||||||
done
|
|
||||||
)
|
|
||||||
|
|
||||||
type reader struct {
|
|
||||||
metrics []telegraf.Metric
|
|
||||||
splitMetrics []telegraf.Metric
|
|
||||||
buf []byte
|
|
||||||
state state
|
|
||||||
|
|
||||||
// metric index
|
|
||||||
iM int
|
|
||||||
// split metric index
|
|
||||||
iSM int
|
|
||||||
// buffer index
|
|
||||||
iB int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReader(metrics []telegraf.Metric) io.Reader {
|
|
||||||
return &reader{
|
|
||||||
metrics: metrics,
|
|
||||||
state: normal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) Read(p []byte) (n int, err error) {
|
|
||||||
var i int
|
|
||||||
switch r.state {
|
|
||||||
case done:
|
|
||||||
return 0, io.EOF
|
|
||||||
case normal:
|
|
||||||
for {
|
|
||||||
// this for-loop is the sunny-day scenario, where we are given a
|
|
||||||
// buffer that is large enough to hold at least a single metric.
|
|
||||||
// all of the cases below it are edge-cases.
|
|
||||||
if r.metrics[r.iM].Len() <= len(p[i:]) {
|
|
||||||
i += r.metrics[r.iM].SerializeTo(p[i:])
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.iM++
|
|
||||||
if r.iM == len(r.metrics) {
|
|
||||||
r.state = done
|
|
||||||
return i, io.EOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we haven't written any bytes, check if we can split the current
|
|
||||||
// metric into multiple full metrics at a smaller size.
|
|
||||||
if i == 0 {
|
|
||||||
tmp := r.metrics[r.iM].Split(len(p))
|
|
||||||
if len(tmp) > 1 {
|
|
||||||
r.splitMetrics = tmp
|
|
||||||
r.state = split
|
|
||||||
if r.splitMetrics[0].Len() <= len(p) {
|
|
||||||
i += r.splitMetrics[0].SerializeTo(p)
|
|
||||||
r.iSM = 1
|
|
||||||
} else {
|
|
||||||
// splitting didn't quite work, so we'll drop down and
|
|
||||||
// overflow the metric.
|
|
||||||
r.state = normal
|
|
||||||
r.iSM = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we haven't written any bytes and we're not at the end of the metrics
|
|
||||||
// slice, then it means we have a single metric that is larger than the
|
|
||||||
// provided buffer.
|
|
||||||
if i == 0 {
|
|
||||||
r.buf = r.metrics[r.iM].Serialize()
|
|
||||||
i += copy(p, r.buf[r.iB:])
|
|
||||||
r.iB += i
|
|
||||||
r.state = overflow
|
|
||||||
}
|
|
||||||
|
|
||||||
case split:
|
|
||||||
if r.splitMetrics[r.iSM].Len() <= len(p) {
|
|
||||||
// write the current split metric
|
|
||||||
i += r.splitMetrics[r.iSM].SerializeTo(p)
|
|
||||||
r.iSM++
|
|
||||||
if r.iSM >= len(r.splitMetrics) {
|
|
||||||
// done writing the current split metrics
|
|
||||||
r.iSM = 0
|
|
||||||
r.iM++
|
|
||||||
if r.iM == len(r.metrics) {
|
|
||||||
r.state = done
|
|
||||||
return i, io.EOF
|
|
||||||
}
|
|
||||||
r.state = normal
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This would only happen if we split the metric, and then a
|
|
||||||
// subsequent buffer was smaller than the initial one given,
|
|
||||||
// so that our split metric no longer fits.
|
|
||||||
r.buf = r.splitMetrics[r.iSM].Serialize()
|
|
||||||
i += copy(p, r.buf[r.iB:])
|
|
||||||
r.iB += i
|
|
||||||
r.state = splitOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
case splitOverflow:
|
|
||||||
i = copy(p, r.buf[r.iB:])
|
|
||||||
r.iB += i
|
|
||||||
if r.iB >= len(r.buf) {
|
|
||||||
r.iB = 0
|
|
||||||
r.iSM++
|
|
||||||
if r.iSM == len(r.splitMetrics) {
|
|
||||||
r.iM++
|
|
||||||
if r.iM == len(r.metrics) {
|
|
||||||
r.state = done
|
|
||||||
return i, io.EOF
|
|
||||||
}
|
|
||||||
r.state = normal
|
|
||||||
} else {
|
|
||||||
r.state = split
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case overflow:
|
|
||||||
i = copy(p, r.buf[r.iB:])
|
|
||||||
r.iB += i
|
|
||||||
if r.iB >= len(r.buf) {
|
|
||||||
r.iB = 0
|
|
||||||
r.iM++
|
|
||||||
if r.iM == len(r.metrics) {
|
|
||||||
r.state = done
|
|
||||||
return i, io.EOF
|
|
||||||
}
|
|
||||||
r.state = normal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
@@ -1,635 +0,0 @@
|
|||||||
package metric
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkMetricReader(b *testing.B) {
|
|
||||||
metrics := make([]telegraf.Metric, 10)
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
metrics[i], _ = New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{"value": int64(1)}, time.Now())
|
|
||||||
}
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
r := NewReader(metrics)
|
|
||||||
io.Copy(ioutil.Discard, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMetricReader(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
metrics := make([]telegraf.Metric, 10)
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
metrics[i], _ = New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{"value": int64(1)}, ts)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
|
|
||||||
buf := make([]byte, 35)
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
assert.True(t, err == io.EOF, err.Error())
|
|
||||||
}
|
|
||||||
assert.Equal(t, 33, n)
|
|
||||||
assert.Equal(t, "foo value=1i 1481032190000000000\n", string(buf[0:n]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// reader should now be done, and always return 0, io.EOF
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
assert.True(t, err == io.EOF, err.Error())
|
|
||||||
assert.Equal(t, 0, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMetricReader_OverflowMetric(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{"value": int64(10)}, ts)
|
|
||||||
metrics := []telegraf.Metric{m}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 5)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
exp string
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"foo v",
|
|
||||||
nil,
|
|
||||||
5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alue=",
|
|
||||||
nil,
|
|
||||||
5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"10i 1",
|
|
||||||
nil,
|
|
||||||
5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"48103",
|
|
||||||
nil,
|
|
||||||
5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"21900",
|
|
||||||
nil,
|
|
||||||
5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"00000",
|
|
||||||
nil,
|
|
||||||
5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"000\n",
|
|
||||||
io.EOF,
|
|
||||||
4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
io.EOF,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
assert.Equal(t, test.n, n)
|
|
||||||
assert.Equal(t, test.exp, string(buf[0:n]))
|
|
||||||
assert.Equal(t, test.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regression test for when a metric is the same size as the buffer.
|
|
||||||
//
|
|
||||||
// Previously EOF would not be set until the next call to Read.
|
|
||||||
func TestMetricReader_MetricSizeEqualsBufferSize(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{"a": int64(1)}, ts)
|
|
||||||
metrics := []telegraf.Metric{m1}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, m1.Len())
|
|
||||||
|
|
||||||
for {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
// Should never read 0 bytes unless at EOF, unless input buffer is 0 length
|
|
||||||
if n == 0 {
|
|
||||||
require.Equal(t, io.EOF, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Lines should be terminated with a LF
|
|
||||||
if err == io.EOF {
|
|
||||||
require.Equal(t, uint8('\n'), buf[n-1])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regression test for when a metric requires to be split and one of the
|
|
||||||
// split metrics is exactly the size of the buffer.
|
|
||||||
//
|
|
||||||
// Previously an empty string would be returned on the next Read without error,
|
|
||||||
// and then next Read call would panic.
|
|
||||||
func TestMetricReader_SplitWithExactLengthSplit(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{"a": int64(1), "bb": int64(2)}, ts)
|
|
||||||
metrics := []telegraf.Metric{m1}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 30)
|
|
||||||
|
|
||||||
// foo a=1i,bb=2i 1481032190000000000\n // len 35
|
|
||||||
//
|
|
||||||
// Requires this specific split order:
|
|
||||||
// foo a=1i 1481032190000000000\n // len 29
|
|
||||||
// foo bb=2i 1481032190000000000\n // len 30
|
|
||||||
|
|
||||||
for {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
// Should never read 0 bytes unless at EOF, unless input buffer is 0 length
|
|
||||||
if n == 0 {
|
|
||||||
require.Equal(t, io.EOF, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Lines should be terminated with a LF
|
|
||||||
if err == io.EOF {
|
|
||||||
require.Equal(t, uint8('\n'), buf[n-1])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regresssion test for when a metric requires to be split and one of the
|
|
||||||
// split metrics is larger than the buffer.
|
|
||||||
//
|
|
||||||
// Previously the metric index would be set incorrectly causing a panic.
|
|
||||||
func TestMetricReader_SplitOverflowOversized(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"a": int64(1),
|
|
||||||
"bbb": int64(2),
|
|
||||||
}, ts)
|
|
||||||
metrics := []telegraf.Metric{m1}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 30)
|
|
||||||
|
|
||||||
// foo a=1i,bbb=2i 1481032190000000000\n // len 36
|
|
||||||
//
|
|
||||||
// foo a=1i 1481032190000000000\n // len 29
|
|
||||||
// foo bbb=2i 1481032190000000000\n // len 31
|
|
||||||
|
|
||||||
for {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
// Should never read 0 bytes unless at EOF, unless input buffer is 0 length
|
|
||||||
if n == 0 {
|
|
||||||
require.Equal(t, io.EOF, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Lines should be terminated with a LF
|
|
||||||
if err == io.EOF {
|
|
||||||
require.Equal(t, uint8('\n'), buf[n-1])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regresssion test for when a split metric exactly fits in the buffer.
|
|
||||||
//
|
|
||||||
// Previously the metric would be overflow split when not required.
|
|
||||||
func TestMetricReader_SplitOverflowUneeded(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{"a": int64(1), "b": int64(2)}, ts)
|
|
||||||
metrics := []telegraf.Metric{m1}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 29)
|
|
||||||
|
|
||||||
// foo a=1i,b=2i 1481032190000000000\n // len 34
|
|
||||||
//
|
|
||||||
// foo a=1i 1481032190000000000\n // len 29
|
|
||||||
// foo b=2i 1481032190000000000\n // len 29
|
|
||||||
|
|
||||||
for {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
// Should never read 0 bytes unless at EOF, unless input buffer is 0 length
|
|
||||||
if n == 0 {
|
|
||||||
require.Equal(t, io.EOF, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Lines should be terminated with a LF
|
|
||||||
if err == io.EOF {
|
|
||||||
require.Equal(t, uint8('\n'), buf[n-1])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMetricReader_OverflowMultipleMetrics(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{"value": int64(10)}, ts)
|
|
||||||
metrics := []telegraf.Metric{m, m.Copy()}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 10)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
exp string
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"foo value=",
|
|
||||||
nil,
|
|
||||||
10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"10i 148103",
|
|
||||||
nil,
|
|
||||||
10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"2190000000",
|
|
||||||
nil,
|
|
||||||
10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"000\n",
|
|
||||||
nil,
|
|
||||||
4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"foo value=",
|
|
||||||
nil,
|
|
||||||
10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"10i 148103",
|
|
||||||
nil,
|
|
||||||
10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"2190000000",
|
|
||||||
nil,
|
|
||||||
10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"000\n",
|
|
||||||
io.EOF,
|
|
||||||
4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
io.EOF,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
assert.Equal(t, test.n, n)
|
|
||||||
assert.Equal(t, test.exp, string(buf[0:n]))
|
|
||||||
assert.Equal(t, test.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test splitting a metric
|
|
||||||
func TestMetricReader_SplitMetric(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value1": int64(10),
|
|
||||||
"value2": int64(10),
|
|
||||||
"value3": int64(10),
|
|
||||||
"value4": int64(10),
|
|
||||||
"value5": int64(10),
|
|
||||||
"value6": int64(10),
|
|
||||||
},
|
|
||||||
ts,
|
|
||||||
)
|
|
||||||
metrics := []telegraf.Metric{m1}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 60)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
expRegex string
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`foo value\d=10i,value\d=10i,value\d=10i 1481032190000000000\n`,
|
|
||||||
nil,
|
|
||||||
57,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`foo value\d=10i,value\d=10i,value\d=10i 1481032190000000000\n`,
|
|
||||||
io.EOF,
|
|
||||||
57,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
io.EOF,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
assert.Equal(t, test.n, n)
|
|
||||||
re := regexp.MustCompile(test.expRegex)
|
|
||||||
assert.True(t, re.MatchString(string(buf[0:n])), string(buf[0:n]))
|
|
||||||
assert.Equal(t, test.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test an array with one split metric and one unsplit
|
|
||||||
func TestMetricReader_SplitMetric2(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value1": int64(10),
|
|
||||||
"value2": int64(10),
|
|
||||||
"value3": int64(10),
|
|
||||||
"value4": int64(10),
|
|
||||||
"value5": int64(10),
|
|
||||||
"value6": int64(10),
|
|
||||||
},
|
|
||||||
ts,
|
|
||||||
)
|
|
||||||
m2, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value1": int64(10),
|
|
||||||
},
|
|
||||||
ts,
|
|
||||||
)
|
|
||||||
metrics := []telegraf.Metric{m1, m2}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 60)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
expRegex string
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`foo value\d=10i,value\d=10i,value\d=10i 1481032190000000000\n`,
|
|
||||||
nil,
|
|
||||||
57,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`foo value\d=10i,value\d=10i,value\d=10i 1481032190000000000\n`,
|
|
||||||
nil,
|
|
||||||
57,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`foo value1=10i 1481032190000000000\n`,
|
|
||||||
io.EOF,
|
|
||||||
35,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
io.EOF,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
assert.Equal(t, test.n, n)
|
|
||||||
re := regexp.MustCompile(test.expRegex)
|
|
||||||
assert.True(t, re.MatchString(string(buf[0:n])), string(buf[0:n]))
|
|
||||||
assert.Equal(t, test.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test split that results in metrics that are still too long, which results in
|
|
||||||
// the reader falling back to regular overflow.
|
|
||||||
func TestMetricReader_SplitMetricTooLong(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value1": int64(10),
|
|
||||||
"value2": int64(10),
|
|
||||||
},
|
|
||||||
ts,
|
|
||||||
)
|
|
||||||
metrics := []telegraf.Metric{m1}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 30)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
expRegex string
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`foo value\d=10i,value\d=10i 1481`,
|
|
||||||
nil,
|
|
||||||
30,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`032190000000000\n`,
|
|
||||||
io.EOF,
|
|
||||||
16,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
io.EOF,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
assert.Equal(t, test.n, n)
|
|
||||||
re := regexp.MustCompile(test.expRegex)
|
|
||||||
assert.True(t, re.MatchString(string(buf[0:n])), string(buf[0:n]))
|
|
||||||
assert.Equal(t, test.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test split with a changing buffer size in the middle of subsequent calls
|
|
||||||
// to Read
|
|
||||||
func TestMetricReader_SplitMetricChangingBuffer(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value1": int64(10),
|
|
||||||
"value2": int64(10),
|
|
||||||
"value3": int64(10),
|
|
||||||
},
|
|
||||||
ts,
|
|
||||||
)
|
|
||||||
m2, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value1": int64(10),
|
|
||||||
},
|
|
||||||
ts,
|
|
||||||
)
|
|
||||||
metrics := []telegraf.Metric{m1, m2}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
expRegex string
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
buf []byte
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`foo value\d=10i 1481032190000000000\n`,
|
|
||||||
nil,
|
|
||||||
35,
|
|
||||||
make([]byte, 36),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`foo value\d=10i 148103219000000`,
|
|
||||||
nil,
|
|
||||||
30,
|
|
||||||
make([]byte, 30),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`0000\n`,
|
|
||||||
nil,
|
|
||||||
5,
|
|
||||||
make([]byte, 30),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`foo value\d=10i 1481032190000000000\n`,
|
|
||||||
nil,
|
|
||||||
35,
|
|
||||||
make([]byte, 36),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`foo value1=10i 1481032190000000000\n`,
|
|
||||||
io.EOF,
|
|
||||||
35,
|
|
||||||
make([]byte, 36),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
io.EOF,
|
|
||||||
0,
|
|
||||||
make([]byte, 36),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
n, err := r.Read(test.buf)
|
|
||||||
assert.Equal(t, test.n, n, test.expRegex)
|
|
||||||
re := regexp.MustCompile(test.expRegex)
|
|
||||||
assert.True(t, re.MatchString(string(test.buf[0:n])), string(test.buf[0:n]))
|
|
||||||
assert.Equal(t, test.err, err, test.expRegex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test split with a changing buffer size in the middle of subsequent calls
|
|
||||||
// to Read
|
|
||||||
func TestMetricReader_SplitMetricChangingBuffer2(t *testing.T) {
|
|
||||||
ts := time.Unix(1481032190, 0)
|
|
||||||
m1, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value1": int64(10),
|
|
||||||
"value2": int64(10),
|
|
||||||
},
|
|
||||||
ts,
|
|
||||||
)
|
|
||||||
m2, _ := New("foo", map[string]string{},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value1": int64(10),
|
|
||||||
},
|
|
||||||
ts,
|
|
||||||
)
|
|
||||||
metrics := []telegraf.Metric{m1, m2}
|
|
||||||
|
|
||||||
r := NewReader(metrics)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
expRegex string
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
buf []byte
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`foo value\d=10i 1481032190000000000\n`,
|
|
||||||
nil,
|
|
||||||
35,
|
|
||||||
make([]byte, 36),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`foo value\d=10i 148103219000000`,
|
|
||||||
nil,
|
|
||||||
30,
|
|
||||||
make([]byte, 30),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`0000\n`,
|
|
||||||
nil,
|
|
||||||
5,
|
|
||||||
make([]byte, 30),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`foo value1=10i 1481032190000000000\n`,
|
|
||||||
io.EOF,
|
|
||||||
35,
|
|
||||||
make([]byte, 36),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
io.EOF,
|
|
||||||
0,
|
|
||||||
make([]byte, 36),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
n, err := r.Read(test.buf)
|
|
||||||
assert.Equal(t, test.n, n, test.expRegex)
|
|
||||||
re := regexp.MustCompile(test.expRegex)
|
|
||||||
assert.True(t, re.MatchString(string(test.buf[0:n])), string(test.buf[0:n]))
|
|
||||||
assert.Equal(t, test.err, err, test.expRegex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMetricRoundtrip(t *testing.T) {
|
|
||||||
const lp = `nstat,bu=linux,cls=server,dc=cer,env=production,host=hostname,name=netstat,sr=database IpExtInBcastOctets=12570626154i,IpExtInBcastPkts=95541226i,IpExtInCEPkts=0i,IpExtInCsumErrors=0i,IpExtInECT0Pkts=55674i,IpExtInECT1Pkts=0i,IpExtInMcastOctets=5928296i,IpExtInMcastPkts=174365i,IpExtInNoECTPkts=17965863529i,IpExtInNoRoutes=20i,IpExtInOctets=3334866321815i,IpExtInTruncatedPkts=0i,IpExtOutBcastOctets=0i,IpExtOutBcastPkts=0i,IpExtOutMcastOctets=0i,IpExtOutMcastPkts=0i,IpExtOutOctets=31397892391399i,TcpExtArpFilter=0i,TcpExtBusyPollRxPackets=0i,TcpExtDelayedACKLocked=14094i,TcpExtDelayedACKLost=302083i,TcpExtDelayedACKs=55486507i,TcpExtEmbryonicRsts=11879i,TcpExtIPReversePathFilter=0i,TcpExtListenDrops=1736i,TcpExtListenOverflows=0i,TcpExtLockDroppedIcmps=0i,TcpExtOfoPruned=0i,TcpExtOutOfWindowIcmps=8i,TcpExtPAWSActive=0i,TcpExtPAWSEstab=974i,TcpExtPAWSPassive=0i,TcpExtPruneCalled=0i,TcpExtRcvPruned=0i,TcpExtSyncookiesFailed=12593i,TcpExtSyncookiesRecv=0i,TcpExtSyncookiesSent=0i,TcpExtTCPACKSkippedChallenge=0i,TcpExtTCPACKSkippedFinWait2=0i,TcpExtTCPACKSkippedPAWS=806i,TcpExtTCPACKSkippedSeq=519i,TcpExtTCPACKSkippedSynRecv=0i,TcpExtTCPACKSkippedTimeWait=0i,TcpExtTCPAbortFailed=0i,TcpExtTCPAbortOnClose=22i,TcpExtTCPAbortOnData=36593i,TcpExtTCPAbortOnLinger=0i,TcpExtTCPAbortOnMemory=0i,TcpExtTCPAbortOnTimeout=674i,TcpExtTCPAutoCorking=494253233i,TcpExtTCPBacklogDrop=0i,TcpExtTCPChallengeACK=281i,TcpExtTCPDSACKIgnoredNoUndo=93354i,TcpExtTCPDSACKIgnoredOld=336i,TcpExtTCPDSACKOfoRecv=0i,TcpExtTCPDSACKOfoSent=7i,TcpExtTCPDSACKOldSent=302073i,TcpExtTCPDSACKRecv=215884i,TcpExtTCPDSACKUndo=7633i,TcpExtTCPDeferAcceptDrop=0i,TcpExtTCPDirectCopyFromBacklog=0i,TcpExtTCPDirectCopyFromPrequeue=0i,TcpExtTCPFACKReorder=1320i,TcpExtTCPFastOpenActive=0i,TcpExtTCPFastOpenActiveFail=0i,TcpExtTCPFastOpenCookieReqd=0i,TcpExtTCPFastOpenListenOverflow=0i,TcpExtTCPFastOpenPassive=0i,TcpExtTCPFastOpenPassiveFail=0i,TcpExtTCPFastRetrans=350681i,TcpExtTCPForwardRetrans=142168i,TcpExtTCPFromZeroWindowAdv=4317i,TcpExtTCPFullUndo=29502i,TcpExtTCPHPAcks=10267073000i,TcpExtTCPHPHits=5629837098i,TcpExtTCPHPHitsToUser=0i,TcpExtTCPHystartDelayCwnd=285127i,TcpExtTCPHystartDelayDetect=12318i,TcpExtTCPHystartTrainCwnd=69160570i,TcpExtTCPHystartTrainDetect=3315799i,TcpExtTCPLossFailures=109i,TcpExtTCPLossProbeRecovery=110819i,TcpExtTCPLossProbes=233995i,TcpExtTCPLossUndo=5276i,TcpExtTCPLostRetransmit=397i,TcpExtTCPMD5NotFound=0i,TcpExtTCPMD5Unexpected=0i,TcpExtTCPMemoryPressures=0i,TcpExtTCPMinTTLDrop=0i,TcpExtTCPOFODrop=0i,TcpExtTCPOFOMerge=7i,TcpExtTCPOFOQueue=15196i,TcpExtTCPOrigDataSent=29055119435i,TcpExtTCPPartialUndo=21320i,TcpExtTCPPrequeueDropped=0i,TcpExtTCPPrequeued=0i,TcpExtTCPPureAcks=1236441827i,TcpExtTCPRcvCoalesce=225590473i,TcpExtTCPRcvCollapsed=0i,TcpExtTCPRenoFailures=0i,TcpExtTCPRenoRecovery=0i,TcpExtTCPRenoRecoveryFail=0i,TcpExtTCPRenoReorder=0i,TcpExtTCPReqQFullDoCookies=0i,TcpExtTCPReqQFullDrop=0i,TcpExtTCPRetransFail=41i,TcpExtTCPSACKDiscard=0i,TcpExtTCPSACKReneging=0i,TcpExtTCPSACKReorder=4307i,TcpExtTCPSYNChallenge=244i,TcpExtTCPSackFailures=1698i,TcpExtTCPSackMerged=184668i,TcpExtTCPSackRecovery=97369i,TcpExtTCPSackRecoveryFail=381i,TcpExtTCPSackShiftFallback=2697079i,TcpExtTCPSackShifted=760299i,TcpExtTCPSchedulerFailed=0i,TcpExtTCPSlowStartRetrans=9276i,TcpExtTCPSpuriousRTOs=959i,TcpExtTCPSpuriousRtxHostQueues=2973i,TcpExtTCPSynRetrans=200970i,TcpExtTCPTSReorder=15221i,TcpExtTCPTimeWaitOverflow=0i,TcpExtTCPTimeouts=70127i,TcpExtTCPToZeroWindowAdv=4317i,TcpExtTCPWantZeroWindowAdv=2133i,TcpExtTW=24809813i,TcpExtTWKilled=0i,TcpExtTWRecycled=0i 1496460785000000000
|
|
||||||
nstat,bu=linux,cls=server,dc=cer,env=production,host=hostname,name=snmp,sr=database IcmpInAddrMaskReps=0i,IcmpInAddrMasks=90i,IcmpInCsumErrors=0i,IcmpInDestUnreachs=284401i,IcmpInEchoReps=9i,IcmpInEchos=1761912i,IcmpInErrors=407i,IcmpInMsgs=2047767i,IcmpInParmProbs=0i,IcmpInRedirects=0i,IcmpInSrcQuenchs=0i,IcmpInTimeExcds=46i,IcmpInTimestampReps=0i,IcmpInTimestamps=1309i,IcmpMsgInType0=9i,IcmpMsgInType11=46i,IcmpMsgInType13=1309i,IcmpMsgInType17=90i,IcmpMsgInType3=284401i,IcmpMsgInType8=1761912i,IcmpMsgOutType0=1761912i,IcmpMsgOutType14=1248i,IcmpMsgOutType3=108709i,IcmpMsgOutType8=9i,IcmpOutAddrMaskReps=0i,IcmpOutAddrMasks=0i,IcmpOutDestUnreachs=108709i,IcmpOutEchoReps=1761912i,IcmpOutEchos=9i,IcmpOutErrors=0i,IcmpOutMsgs=1871878i,IcmpOutParmProbs=0i,IcmpOutRedirects=0i,IcmpOutSrcQuenchs=0i,IcmpOutTimeExcds=0i,IcmpOutTimestampReps=1248i,IcmpOutTimestamps=0i,IpDefaultTTL=64i,IpForwDatagrams=0i,IpForwarding=2i,IpFragCreates=0i,IpFragFails=0i,IpFragOKs=0i,IpInAddrErrors=0i,IpInDelivers=17658795773i,IpInDiscards=0i,IpInHdrErrors=0i,IpInReceives=17659269339i,IpInUnknownProtos=0i,IpOutDiscards=236976i,IpOutNoRoutes=1009i,IpOutRequests=23466783734i,IpReasmFails=0i,IpReasmOKs=0i,IpReasmReqds=0i,IpReasmTimeout=0i,TcpActiveOpens=23308977i,TcpAttemptFails=3757543i,TcpCurrEstab=280i,TcpEstabResets=184792i,TcpInCsumErrors=0i,TcpInErrs=232i,TcpInSegs=17536573089i,TcpMaxConn=-1i,TcpOutRsts=4051451i,TcpOutSegs=29836254873i,TcpPassiveOpens=176546974i,TcpRetransSegs=878085i,TcpRtoAlgorithm=1i,TcpRtoMax=120000i,TcpRtoMin=200i,UdpInCsumErrors=0i,UdpInDatagrams=24441661i,UdpInErrors=0i,UdpLiteInCsumErrors=0i,UdpLiteInDatagrams=0i,UdpLiteInErrors=0i,UdpLiteNoPorts=0i,UdpLiteOutDatagrams=0i,UdpLiteRcvbufErrors=0i,UdpLiteSndbufErrors=0i,UdpNoPorts=17660i,UdpOutDatagrams=51807896i,UdpRcvbufErrors=0i,UdpSndbufErrors=236922i 1496460785000000000
|
|
||||||
`
|
|
||||||
metrics, err := Parse([]byte(lp))
|
|
||||||
require.NoError(t, err)
|
|
||||||
r := NewReader(metrics)
|
|
||||||
buf := make([]byte, 128)
|
|
||||||
_, err = r.Read(buf)
|
|
||||||
require.NoError(t, err)
|
|
||||||
metrics, err = Parse(buf)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package telegraf
|
package plugins
|
||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package telegraf
|
package plugins
|
||||||
|
|
||||||
// Aggregator is an interface for implementing an Aggregator plugin.
|
// Aggregator is an interface for implementing an Aggregator plugin.
|
||||||
// the RunningAggregator wraps this interface and guarantees that
|
// the RunningAggregator wraps this interface and guarantees that
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package minmax
|
package minmax
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/aggregators"
|
"github.com/influxdata/telegraf/plugins/aggregators"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ type MinMax struct {
|
|||||||
cache map[uint64]aggregate
|
cache map[uint64]aggregate
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMinMax() telegraf.Aggregator {
|
func NewMinMax() plugins.Aggregator {
|
||||||
mm := &MinMax{}
|
mm := &MinMax{}
|
||||||
mm.Reset()
|
mm.Reset()
|
||||||
return mm
|
return mm
|
||||||
@@ -43,7 +43,7 @@ func (m *MinMax) Description() string {
|
|||||||
return "Keep the aggregate min/max of each metric passing through."
|
return "Keep the aggregate min/max of each metric passing through."
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MinMax) Add(in telegraf.Metric) {
|
func (m *MinMax) Add(in plugins.Metric) {
|
||||||
id := in.HashID()
|
id := in.HashID()
|
||||||
if _, ok := m.cache[id]; !ok {
|
if _, ok := m.cache[id]; !ok {
|
||||||
// hit an uncached metric, create caches for first time:
|
// hit an uncached metric, create caches for first time:
|
||||||
@@ -86,7 +86,7 @@ func (m *MinMax) Add(in telegraf.Metric) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MinMax) Push(acc telegraf.Accumulator) {
|
func (m *MinMax) Push(acc plugins.Accumulator) {
|
||||||
for _, aggregate := range m.cache {
|
for _, aggregate := range m.cache {
|
||||||
fields := map[string]interface{}{}
|
fields := map[string]interface{}{}
|
||||||
for k, v := range aggregate.fields {
|
for k, v := range aggregate.fields {
|
||||||
@@ -113,7 +113,7 @@ func convert(in interface{}) (float64, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
aggregators.Add("minmax", func() telegraf.Aggregator {
|
aggregators.Add("minmax", func() plugins.Aggregator {
|
||||||
return NewMinMax()
|
return NewMinMax()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package aggregators
|
package aggregators
|
||||||
|
|
||||||
import "github.com/influxdata/telegraf"
|
import "github.com/influxdata/telegraf/plugins"
|
||||||
|
|
||||||
type Creator func() telegraf.Aggregator
|
type Creator func() plugins.Aggregator
|
||||||
|
|
||||||
var Aggregators = map[string]Creator{}
|
var Aggregators = map[string]Creator{}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package telegraf
|
package plugins
|
||||||
|
|
||||||
type Input interface {
|
type Input interface {
|
||||||
// SampleConfig returns the default configuration of the Input
|
// SampleConfig returns the default configuration of the Input
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
# Example Input Plugin
|
# Example Input Plugin
|
||||||
|
|
||||||
The example plugin gathers metrics about example things. This description
|
The example plugin gathers metrics about example things
|
||||||
explains at a high level what the plugin does and provides links to where
|
|
||||||
additional information can be found.
|
|
||||||
|
|
||||||
### Configuration:
|
### Configuration:
|
||||||
|
|
||||||
@@ -14,8 +12,7 @@ additional information can be found.
|
|||||||
|
|
||||||
### Measurements & Fields:
|
### Measurements & Fields:
|
||||||
|
|
||||||
Here you should add an optional description and links to where the user can
|
<optional description>
|
||||||
get more information about the measurements.
|
|
||||||
|
|
||||||
- measurement1
|
- measurement1
|
||||||
- field1 (type, unit)
|
- field1 (type, unit)
|
||||||
@@ -33,11 +30,8 @@ get more information about the measurements.
|
|||||||
|
|
||||||
### Sample Queries:
|
### Sample Queries:
|
||||||
|
|
||||||
This section should contain some useful InfluxDB queries that can be used to
|
These are some useful queries (to generate dashboards or other) to run against data from this plugin:
|
||||||
get started with the plugin or to generate dashboards. For each query listed,
|
|
||||||
describe at a high level what data is returned.
|
|
||||||
|
|
||||||
Get the max, mean, and min for the measurement in the last hour:
|
|
||||||
```
|
```
|
||||||
SELECT max(field1), mean(field1), min(field1) FROM measurement1 WHERE tag1=bar AND time > now() - 1h GROUP BY tag
|
SELECT max(field1), mean(field1), min(field1) FROM measurement1 WHERE tag1=bar AND time > now() - 1h GROUP BY tag
|
||||||
```
|
```
|
||||||
@@ -45,7 +39,7 @@ SELECT max(field1), mean(field1), min(field1) FROM measurement1 WHERE tag1=bar A
|
|||||||
### Example Output:
|
### Example Output:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ telegraf -input-filter example -test
|
$ ./telegraf -config telegraf.conf -input-filter example -test
|
||||||
measurement1,tag1=foo,tag2=bar field1=1i,field2=2.1 1453831884664956455
|
measurement1,tag1=foo,tag2=bar field1=1i,field2=2.1 1453831884664956455
|
||||||
measurement2,tag1=foo,tag2=bar,tag3=baz field3=1i 1453831884664956455
|
measurement2,tag1=foo,tag2=bar,tag3=baz field3=1i 1453831884664956455
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
|
"github.com/influxdata/telegraf/internal/errchan"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
|
||||||
as "github.com/aerospike/aerospike-client-go"
|
as "github.com/aerospike/aerospike-client-go"
|
||||||
@@ -34,25 +35,26 @@ func (a *Aerospike) Description() string {
|
|||||||
return "Read stats from aerospike server(s)"
|
return "Read stats from aerospike server(s)"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Aerospike) Gather(acc telegraf.Accumulator) error {
|
func (a *Aerospike) Gather(acc plugins.Accumulator) error {
|
||||||
if len(a.Servers) == 0 {
|
if len(a.Servers) == 0 {
|
||||||
return a.gatherServer("127.0.0.1:3000", acc)
|
return a.gatherServer("127.0.0.1:3000", acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
errChan := errchan.New(len(a.Servers))
|
||||||
wg.Add(len(a.Servers))
|
wg.Add(len(a.Servers))
|
||||||
for _, server := range a.Servers {
|
for _, server := range a.Servers {
|
||||||
go func(serv string) {
|
go func(serv string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
acc.AddError(a.gatherServer(serv, acc))
|
errChan.C <- a.gatherServer(serv, acc)
|
||||||
}(server)
|
}(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return nil
|
return errChan.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Aerospike) gatherServer(hostport string, acc telegraf.Accumulator) error {
|
func (a *Aerospike) gatherServer(hostport string, acc plugins.Accumulator) error {
|
||||||
host, port, err := net.SplitHostPort(hostport)
|
host, port, err := net.SplitHostPort(hostport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -150,7 +152,7 @@ func copyTags(m map[string]string) map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("aerospike", func() telegraf.Input {
|
inputs.Add("aerospike", func() plugins.Input {
|
||||||
return &Aerospike{}
|
return &Aerospike{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestAerospikeStatistics(t *testing.T) {
|
|||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := acc.GatherError(a.Gather)
|
err := a.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, acc.HasMeasurement("aerospike_node"))
|
assert.True(t, acc.HasMeasurement("aerospike_node"))
|
||||||
@@ -41,7 +41,8 @@ func TestAerospikeStatisticsPartialErr(t *testing.T) {
|
|||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
require.Error(t, acc.GatherError(a.Gather))
|
err := a.Gather(&acc)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
assert.True(t, acc.HasMeasurement("aerospike_node"))
|
assert.True(t, acc.HasMeasurement("aerospike_node"))
|
||||||
assert.True(t, acc.HasMeasurement("aerospike_namespace"))
|
assert.True(t, acc.HasMeasurement("aerospike_namespace"))
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package all
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/aerospike"
|
_ "github.com/influxdata/telegraf/plugins/inputs/aerospike"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/amqp_consumer"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/apache"
|
_ "github.com/influxdata/telegraf/plugins/inputs/apache"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/bcache"
|
_ "github.com/influxdata/telegraf/plugins/inputs/bcache"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/cassandra"
|
_ "github.com/influxdata/telegraf/plugins/inputs/cassandra"
|
||||||
@@ -15,7 +14,6 @@ import (
|
|||||||
_ "github.com/influxdata/telegraf/plugins/inputs/couchbase"
|
_ "github.com/influxdata/telegraf/plugins/inputs/couchbase"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/couchdb"
|
_ "github.com/influxdata/telegraf/plugins/inputs/couchdb"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/disque"
|
_ "github.com/influxdata/telegraf/plugins/inputs/disque"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/dmcache"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/dns_query"
|
_ "github.com/influxdata/telegraf/plugins/inputs/dns_query"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/docker"
|
_ "github.com/influxdata/telegraf/plugins/inputs/docker"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/dovecot"
|
_ "github.com/influxdata/telegraf/plugins/inputs/dovecot"
|
||||||
@@ -30,12 +28,10 @@ import (
|
|||||||
_ "github.com/influxdata/telegraf/plugins/inputs/httpjson"
|
_ "github.com/influxdata/telegraf/plugins/inputs/httpjson"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/influxdb"
|
_ "github.com/influxdata/telegraf/plugins/inputs/influxdb"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/internal"
|
_ "github.com/influxdata/telegraf/plugins/inputs/internal"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/interrupts"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/ipmi_sensor"
|
_ "github.com/influxdata/telegraf/plugins/inputs/ipmi_sensor"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/iptables"
|
_ "github.com/influxdata/telegraf/plugins/inputs/iptables"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/jolokia"
|
_ "github.com/influxdata/telegraf/plugins/inputs/jolokia"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/kafka_consumer"
|
_ "github.com/influxdata/telegraf/plugins/inputs/kafka_consumer"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/kapacitor"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/kubernetes"
|
_ "github.com/influxdata/telegraf/plugins/inputs/kubernetes"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/leofs"
|
_ "github.com/influxdata/telegraf/plugins/inputs/leofs"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/logparser"
|
_ "github.com/influxdata/telegraf/plugins/inputs/logparser"
|
||||||
@@ -70,7 +66,6 @@ import (
|
|||||||
_ "github.com/influxdata/telegraf/plugins/inputs/sensors"
|
_ "github.com/influxdata/telegraf/plugins/inputs/sensors"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/snmp"
|
_ "github.com/influxdata/telegraf/plugins/inputs/snmp"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/snmp_legacy"
|
_ "github.com/influxdata/telegraf/plugins/inputs/snmp_legacy"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/socket_listener"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/sysstat"
|
_ "github.com/influxdata/telegraf/plugins/inputs/sysstat"
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
# AMQP Consumer Input Plugin
|
|
||||||
|
|
||||||
This plugin provides a consumer for use with AMQP 0-9-1, a promenent implementation of this protocol being [RabbitMQ](https://www.rabbitmq.com/).
|
|
||||||
|
|
||||||
Metrics are read from a topic exchange using the configured queue and binding_key.
|
|
||||||
|
|
||||||
Message payload should be formatted in one of the [Telegraf Data Formats](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md).
|
|
||||||
|
|
||||||
For an introduction to AMQP see:
|
|
||||||
- https://www.rabbitmq.com/tutorials/amqp-concepts.html
|
|
||||||
- https://www.rabbitmq.com/getstarted.html
|
|
||||||
|
|
||||||
The following defaults are known to work with RabbitMQ:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# AMQP consumer plugin
|
|
||||||
[[inputs.amqp_consumer]]
|
|
||||||
## AMQP url
|
|
||||||
url = "amqp://localhost:5672/influxdb"
|
|
||||||
## AMQP exchange
|
|
||||||
exchange = "telegraf"
|
|
||||||
## AMQP queue name
|
|
||||||
queue = "telegraf"
|
|
||||||
## Binding Key
|
|
||||||
binding_key = "#"
|
|
||||||
|
|
||||||
## Controls how many messages the server will try to keep on the network
|
|
||||||
## for consumers before receiving delivery acks.
|
|
||||||
#prefetch_count = 50
|
|
||||||
|
|
||||||
## Auth method. PLAIN and EXTERNAL are supported.
|
|
||||||
## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as
|
|
||||||
## described here: https://www.rabbitmq.com/plugins.html
|
|
||||||
# auth_method = "PLAIN"
|
|
||||||
## 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.
|
|
||||||
## Each data format has its own unique set of configuration options, read
|
|
||||||
## more about them here:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
|
||||||
data_format = "influx"
|
|
||||||
```
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
package amqp_consumer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/streadway/amqp"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/internal"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AMQPConsumer is the top level struct for this plugin
|
|
||||||
type AMQPConsumer struct {
|
|
||||||
URL string
|
|
||||||
// AMQP exchange
|
|
||||||
Exchange string
|
|
||||||
// Queue Name
|
|
||||||
Queue string
|
|
||||||
// Binding Key
|
|
||||||
BindingKey string `toml:"binding_key"`
|
|
||||||
|
|
||||||
// Controls how many messages the server will try to keep on the network
|
|
||||||
// for consumers before receiving delivery acks.
|
|
||||||
PrefetchCount int
|
|
||||||
|
|
||||||
// AMQP Auth method
|
|
||||||
AuthMethod string
|
|
||||||
// Path to CA file
|
|
||||||
SSLCA string `toml:"ssl_ca"`
|
|
||||||
// Path to host cert file
|
|
||||||
SSLCert string `toml:"ssl_cert"`
|
|
||||||
// Path to cert key file
|
|
||||||
SSLKey string `toml:"ssl_key"`
|
|
||||||
// Use SSL but skip chain & host verification
|
|
||||||
InsecureSkipVerify bool
|
|
||||||
|
|
||||||
parser parsers.Parser
|
|
||||||
conn *amqp.Connection
|
|
||||||
wg *sync.WaitGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
type externalAuth struct{}
|
|
||||||
|
|
||||||
func (a *externalAuth) Mechanism() string {
|
|
||||||
return "EXTERNAL"
|
|
||||||
}
|
|
||||||
func (a *externalAuth) Response() string {
|
|
||||||
return fmt.Sprintf("\000")
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
DefaultAuthMethod = "PLAIN"
|
|
||||||
DefaultPrefetchCount = 50
|
|
||||||
)
|
|
||||||
|
|
||||||
func (a *AMQPConsumer) SampleConfig() string {
|
|
||||||
return `
|
|
||||||
## AMQP url
|
|
||||||
url = "amqp://localhost:5672/influxdb"
|
|
||||||
## AMQP exchange
|
|
||||||
exchange = "telegraf"
|
|
||||||
## AMQP queue name
|
|
||||||
queue = "telegraf"
|
|
||||||
## Binding Key
|
|
||||||
binding_key = "#"
|
|
||||||
|
|
||||||
## Maximum number of messages server should give to the worker.
|
|
||||||
prefetch_count = 50
|
|
||||||
|
|
||||||
## Auth method. PLAIN and EXTERNAL are supported
|
|
||||||
## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as
|
|
||||||
## described here: https://www.rabbitmq.com/plugins.html
|
|
||||||
# auth_method = "PLAIN"
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
## Each data format has its own unique set of configuration options, read
|
|
||||||
## more about them here:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
|
||||||
data_format = "influx"
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AMQPConsumer) Description() string {
|
|
||||||
return "AMQP consumer plugin"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AMQPConsumer) SetParser(parser parsers.Parser) {
|
|
||||||
a.parser = parser
|
|
||||||
}
|
|
||||||
|
|
||||||
// All gathering is done in the Start function
|
|
||||||
func (a *AMQPConsumer) Gather(_ telegraf.Accumulator) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AMQPConsumer) createConfig() (*amqp.Config, error) {
|
|
||||||
// make new tls config
|
|
||||||
tls, err := internal.GetTLSConfig(
|
|
||||||
a.SSLCert, a.SSLKey, a.SSLCA, a.InsecureSkipVerify)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse auth method
|
|
||||||
var sasl []amqp.Authentication // nil by default
|
|
||||||
|
|
||||||
if strings.ToUpper(a.AuthMethod) == "EXTERNAL" {
|
|
||||||
sasl = []amqp.Authentication{&externalAuth{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
config := amqp.Config{
|
|
||||||
TLSClientConfig: tls,
|
|
||||||
SASL: sasl, // if nil, it will be PLAIN
|
|
||||||
}
|
|
||||||
return &config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start satisfies the telegraf.ServiceInput interface
|
|
||||||
func (a *AMQPConsumer) Start(acc telegraf.Accumulator) error {
|
|
||||||
amqpConf, err := a.createConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msgs, err := a.connect(amqpConf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
a.wg = &sync.WaitGroup{}
|
|
||||||
a.wg.Add(1)
|
|
||||||
go a.process(msgs, acc)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
err := <-a.conn.NotifyClose(make(chan *amqp.Error))
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("I! AMQP consumer connection closed: %s; trying to reconnect", err)
|
|
||||||
for {
|
|
||||||
msgs, err := a.connect(amqpConf)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("E! AMQP connection failed: %s", err)
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
a.wg.Add(1)
|
|
||||||
go a.process(msgs, acc)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AMQPConsumer) connect(amqpConf *amqp.Config) (<-chan amqp.Delivery, error) {
|
|
||||||
conn, err := amqp.DialConfig(a.URL, *amqpConf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
a.conn = conn
|
|
||||||
|
|
||||||
ch, err := conn.Channel()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to open a channel: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ch.ExchangeDeclare(
|
|
||||||
a.Exchange, // name
|
|
||||||
"topic", // type
|
|
||||||
true, // durable
|
|
||||||
false, // auto-deleted
|
|
||||||
false, // internal
|
|
||||||
false, // no-wait
|
|
||||||
nil, // arguments
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to declare an exchange: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
q, err := ch.QueueDeclare(
|
|
||||||
a.Queue, // queue
|
|
||||||
true, // durable
|
|
||||||
false, // delete when unused
|
|
||||||
false, // exclusive
|
|
||||||
false, // no-wait
|
|
||||||
nil, // arguments
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to declare a queue: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ch.QueueBind(
|
|
||||||
q.Name, // queue
|
|
||||||
a.BindingKey, // binding-key
|
|
||||||
a.Exchange, // exchange
|
|
||||||
false,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to bind a queue: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ch.Qos(
|
|
||||||
a.PrefetchCount,
|
|
||||||
0, // prefetch-size
|
|
||||||
false, // global
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to set QoS: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgs, err := ch.Consume(
|
|
||||||
q.Name, // queue
|
|
||||||
"", // consumer
|
|
||||||
false, // auto-ack
|
|
||||||
false, // exclusive
|
|
||||||
false, // no-local
|
|
||||||
false, // no-wait
|
|
||||||
nil, // arguments
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed establishing connection to queue: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("I! Started AMQP consumer")
|
|
||||||
return msgs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read messages from queue and add them to the Accumulator
|
|
||||||
func (a *AMQPConsumer) process(msgs <-chan amqp.Delivery, acc telegraf.Accumulator) {
|
|
||||||
defer a.wg.Done()
|
|
||||||
for d := range msgs {
|
|
||||||
metrics, err := a.parser.Parse(d.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("E! %v: error parsing metric - %v", err, string(d.Body))
|
|
||||||
} else {
|
|
||||||
for _, m := range metrics {
|
|
||||||
acc.AddFields(m.Name(), m.Fields(), m.Tags(), m.Time())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.Ack(false)
|
|
||||||
}
|
|
||||||
log.Printf("I! AMQP consumer queue closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AMQPConsumer) Stop() {
|
|
||||||
err := a.conn.Close()
|
|
||||||
if err != nil && err != amqp.ErrClosed {
|
|
||||||
log.Printf("E! Error closing AMQP connection: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
a.wg.Wait()
|
|
||||||
log.Println("I! Stopped AMQP service")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
inputs.Add("amqp_consumer", func() telegraf.Input {
|
|
||||||
return &AMQPConsumer{
|
|
||||||
AuthMethod: DefaultAuthMethod,
|
|
||||||
PrefetchCount: DefaultPrefetchCount,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
- **urls** []string: List of apache-status URLs to collect from. Default is "http://localhost/server-status?auto".
|
- **urls** []string: List of apache-status URLs to collect from. Default is "http://localhost/server-status?auto".
|
||||||
- **username** string: Username for HTTP basic authentication
|
- **username** string: Username for HTTP basic authentication
|
||||||
- **password** string: Password for HTTP basic authentication
|
- **password** string: Password for HTTP basic authentication
|
||||||
- **timeout** duration: time that the HTTP connection will remain waiting for response. Default 4 seconds ("4s")
|
- **timeout** duration: time that the HTTP connection will remain waiting for response. Defalt 4 seconds ("4s")
|
||||||
|
|
||||||
##### Optional SSL Config
|
##### Optional SSL Config
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
@@ -29,8 +28,6 @@ type Apache struct {
|
|||||||
SSLKey string `toml:"ssl_key"`
|
SSLKey string `toml:"ssl_key"`
|
||||||
// Use SSL but skip chain & host verification
|
// Use SSL but skip chain & host verification
|
||||||
InsecureSkipVerify bool
|
InsecureSkipVerify bool
|
||||||
|
|
||||||
client *http.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
@@ -60,7 +57,7 @@ func (n *Apache) Description() string {
|
|||||||
return "Read Apache status information (mod_status)"
|
return "Read Apache status information (mod_status)"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Apache) Gather(acc telegraf.Accumulator) error {
|
func (n *Apache) Gather(acc plugins.Accumulator) error {
|
||||||
if len(n.Urls) == 0 {
|
if len(n.Urls) == 0 {
|
||||||
n.Urls = []string{"http://localhost/server-status?auto"}
|
n.Urls = []string{"http://localhost/server-status?auto"}
|
||||||
}
|
}
|
||||||
@@ -68,51 +65,55 @@ func (n *Apache) Gather(acc telegraf.Accumulator) error {
|
|||||||
n.ResponseTimeout.Duration = time.Second * 5
|
n.ResponseTimeout.Duration = time.Second * 5
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.client == nil {
|
var outerr error
|
||||||
client, err := n.createHttpClient()
|
var errch = make(chan error)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
n.client = client
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(len(n.Urls))
|
|
||||||
for _, u := range n.Urls {
|
for _, u := range n.Urls {
|
||||||
addr, err := url.Parse(u)
|
addr, err := url.Parse(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(fmt.Errorf("Unable to parse address '%s': %s", u, err))
|
return fmt.Errorf("Unable to parse address '%s': %s", u, err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go func(addr *url.URL) {
|
go func(addr *url.URL) {
|
||||||
defer wg.Done()
|
errch <- n.gatherUrl(addr, acc)
|
||||||
acc.AddError(n.gatherUrl(addr, acc))
|
|
||||||
}(addr)
|
}(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
// Drain channel, waiting for all requests to finish and save last error.
|
||||||
return nil
|
for range n.Urls {
|
||||||
|
if err := <-errch; err != nil {
|
||||||
|
outerr = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Apache) createHttpClient() (*http.Client, error) {
|
return outerr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Apache) gatherUrl(addr *url.URL, acc plugins.Accumulator) error {
|
||||||
|
|
||||||
|
var tr *http.Transport
|
||||||
|
|
||||||
|
if addr.Scheme == "https" {
|
||||||
tlsCfg, err := internal.GetTLSConfig(
|
tlsCfg, err := internal.GetTLSConfig(
|
||||||
n.SSLCert, n.SSLKey, n.SSLCA, n.InsecureSkipVerify)
|
n.SSLCert, n.SSLKey, n.SSLCA, n.InsecureSkipVerify)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
|
}
|
||||||
|
tr = &http.Transport{
|
||||||
|
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
||||||
|
TLSClientConfig: tlsCfg,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tr = &http.Transport{
|
||||||
|
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: tr,
|
||||||
TLSClientConfig: tlsCfg,
|
|
||||||
},
|
|
||||||
Timeout: n.ResponseTimeout.Duration,
|
Timeout: n.ResponseTimeout.Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Apache) gatherUrl(addr *url.URL, acc telegraf.Accumulator) error {
|
|
||||||
req, err := http.NewRequest("GET", addr.String(), nil)
|
req, err := http.NewRequest("GET", addr.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error on new request to %s : %s\n", addr.String(), err)
|
return fmt.Errorf("error on new request to %s : %s\n", addr.String(), err)
|
||||||
@@ -122,7 +123,7 @@ func (n *Apache) gatherUrl(addr *url.URL, acc telegraf.Accumulator) error {
|
|||||||
req.SetBasicAuth(n.Username, n.Password)
|
req.SetBasicAuth(n.Username, n.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := n.client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error on request to %s : %s\n", addr.String(), err)
|
return fmt.Errorf("error on request to %s : %s\n", addr.String(), err)
|
||||||
}
|
}
|
||||||
@@ -227,7 +228,7 @@ func getTags(addr *url.URL) map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("apache", func() telegraf.Input {
|
inputs.Add("apache", func() plugins.Input {
|
||||||
return &Apache{}
|
return &Apache{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func TestHTTPApache(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := acc.GatherError(a.Gather)
|
err := a.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ func prettyToBytes(v string) uint64 {
|
|||||||
return uint64(result)
|
return uint64(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bcache) gatherBcache(bdev string, acc telegraf.Accumulator) error {
|
func (b *Bcache) gatherBcache(bdev string, acc plugins.Accumulator) error {
|
||||||
tags := getTags(bdev)
|
tags := getTags(bdev)
|
||||||
metrics, err := filepath.Glob(bdev + "/stats_total/*")
|
metrics, err := filepath.Glob(bdev + "/stats_total/*")
|
||||||
if len(metrics) < 0 {
|
if len(metrics) < 0 {
|
||||||
@@ -105,7 +105,7 @@ func (b *Bcache) gatherBcache(bdev string, acc telegraf.Accumulator) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bcache) Gather(acc telegraf.Accumulator) error {
|
func (b *Bcache) Gather(acc plugins.Accumulator) error {
|
||||||
bcacheDevsChecked := make(map[string]bool)
|
bcacheDevsChecked := make(map[string]bool)
|
||||||
var restrictDevs bool
|
var restrictDevs bool
|
||||||
if len(b.BcacheDevs) != 0 {
|
if len(b.BcacheDevs) != 0 {
|
||||||
@@ -136,7 +136,7 @@ func (b *Bcache) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("bcache", func() telegraf.Input {
|
inputs.Add("bcache", func() plugins.Input {
|
||||||
return &Bcache{}
|
return &Bcache{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -34,13 +35,13 @@ type Cassandra struct {
|
|||||||
type javaMetric struct {
|
type javaMetric struct {
|
||||||
host string
|
host string
|
||||||
metric string
|
metric string
|
||||||
acc telegraf.Accumulator
|
acc plugins.Accumulator
|
||||||
}
|
}
|
||||||
|
|
||||||
type cassandraMetric struct {
|
type cassandraMetric struct {
|
||||||
host string
|
host string
|
||||||
metric string
|
metric string
|
||||||
acc telegraf.Accumulator
|
acc plugins.Accumulator
|
||||||
}
|
}
|
||||||
|
|
||||||
type jmxMetric interface {
|
type jmxMetric interface {
|
||||||
@@ -48,12 +49,12 @@ type jmxMetric interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newJavaMetric(host string, metric string,
|
func newJavaMetric(host string, metric string,
|
||||||
acc telegraf.Accumulator) *javaMetric {
|
acc plugins.Accumulator) *javaMetric {
|
||||||
return &javaMetric{host: host, metric: metric, acc: acc}
|
return &javaMetric{host: host, metric: metric, acc: acc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCassandraMetric(host string, metric string,
|
func newCassandraMetric(host string, metric string,
|
||||||
acc telegraf.Accumulator) *cassandraMetric {
|
acc plugins.Accumulator) *cassandraMetric {
|
||||||
return &cassandraMetric{host: host, metric: metric, acc: acc}
|
return &cassandraMetric{host: host, metric: metric, acc: acc}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,8 +123,8 @@ func (j javaMetric) addTagsFields(out map[string]interface{}) {
|
|||||||
}
|
}
|
||||||
j.acc.AddFields(tokens["class"]+tokens["type"], fields, tags)
|
j.acc.AddFields(tokens["class"]+tokens["type"], fields, tags)
|
||||||
} else {
|
} else {
|
||||||
j.acc.AddError(fmt.Errorf("Missing key 'value' in '%s' output response\n%v\n",
|
fmt.Printf("Missing key 'value' in '%s' output response\n%v\n",
|
||||||
j.metric, out))
|
j.metric, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,8 +155,8 @@ func (c cassandraMetric) addTagsFields(out map[string]interface{}) {
|
|||||||
addCassandraMetric(k, c, v.(map[string]interface{}))
|
addCassandraMetric(k, c, v.(map[string]interface{}))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.acc.AddError(fmt.Errorf("Missing key 'value' in '%s' output response\n%v\n",
|
fmt.Printf("Missing key 'value' in '%s' output response\n%v\n",
|
||||||
c.metric, out))
|
c.metric, out)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -163,8 +164,8 @@ func (c cassandraMetric) addTagsFields(out map[string]interface{}) {
|
|||||||
addCassandraMetric(r.(map[string]interface{})["mbean"].(string),
|
addCassandraMetric(r.(map[string]interface{})["mbean"].(string),
|
||||||
c, values.(map[string]interface{}))
|
c, values.(map[string]interface{}))
|
||||||
} else {
|
} else {
|
||||||
c.acc.AddError(fmt.Errorf("Missing key 'value' in '%s' output response\n%v\n",
|
fmt.Printf("Missing key 'value' in '%s' output response\n%v\n",
|
||||||
c.metric, out))
|
c.metric, out)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,7 +257,7 @@ func parseServerTokens(server string) map[string]string {
|
|||||||
return serverTokens
|
return serverTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cassandra) Gather(acc telegraf.Accumulator) error {
|
func (c *Cassandra) Gather(acc plugins.Accumulator) error {
|
||||||
context := c.Context
|
context := c.Context
|
||||||
servers := c.Servers
|
servers := c.Servers
|
||||||
metrics := c.Metrics
|
metrics := c.Metrics
|
||||||
@@ -273,8 +274,8 @@ func (c *Cassandra) Gather(acc telegraf.Accumulator) error {
|
|||||||
m = newCassandraMetric(serverTokens["host"], metric, acc)
|
m = newCassandraMetric(serverTokens["host"], metric, acc)
|
||||||
} else {
|
} else {
|
||||||
// unsupported metric type
|
// unsupported metric type
|
||||||
acc.AddError(fmt.Errorf("E! Unsupported Cassandra metric [%s], skipping",
|
log.Printf("I! Unsupported Cassandra metric [%s], skipping",
|
||||||
metric))
|
metric)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,8 +283,7 @@ func (c *Cassandra) Gather(acc telegraf.Accumulator) error {
|
|||||||
requestUrl, err := url.Parse("http://" + serverTokens["host"] + ":" +
|
requestUrl, err := url.Parse("http://" + serverTokens["host"] + ":" +
|
||||||
serverTokens["port"] + context + metric)
|
serverTokens["port"] + context + metric)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(err)
|
return err
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if serverTokens["user"] != "" && serverTokens["passwd"] != "" {
|
if serverTokens["user"] != "" && serverTokens["passwd"] != "" {
|
||||||
requestUrl.User = url.UserPassword(serverTokens["user"],
|
requestUrl.User = url.UserPassword(serverTokens["user"],
|
||||||
@@ -291,12 +291,8 @@ func (c *Cassandra) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out, err := c.getAttr(requestUrl)
|
out, err := c.getAttr(requestUrl)
|
||||||
if err != nil {
|
|
||||||
acc.AddError(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if out["status"] != 200.0 {
|
if out["status"] != 200.0 {
|
||||||
acc.AddError(fmt.Errorf("URL returned with status %v\n", out["status"]))
|
fmt.Printf("URL returned with status %v\n", out["status"])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m.addTagsFields(out)
|
m.addTagsFields(out)
|
||||||
@@ -306,7 +302,7 @@ func (c *Cassandra) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("cassandra", func() telegraf.Input {
|
inputs.Add("cassandra", func() plugins.Input {
|
||||||
return &Cassandra{jClient: &JolokiaClientImpl{client: &http.Client{}}}
|
return &Cassandra{jClient: &JolokiaClientImpl{client: &http.Client{}}}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ func TestHttpJsonJavaMultiValue(t *testing.T) {
|
|||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
acc.SetDebug(true)
|
acc.SetDebug(true)
|
||||||
err := acc.GatherError(cassandra.Gather)
|
err := cassandra.Gather(&acc)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, len(acc.Metrics))
|
assert.Equal(t, 2, len(acc.Metrics))
|
||||||
@@ -180,7 +180,7 @@ func TestHttpJsonJavaMultiType(t *testing.T) {
|
|||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
acc.SetDebug(true)
|
acc.SetDebug(true)
|
||||||
err := acc.GatherError(cassandra.Gather)
|
err := cassandra.Gather(&acc)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, len(acc.Metrics))
|
assert.Equal(t, 2, len(acc.Metrics))
|
||||||
@@ -197,17 +197,16 @@ func TestHttpJsonJavaMultiType(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test that the proper values are ignored or collected
|
// Test that the proper values are ignored or collected
|
||||||
func TestHttp404(t *testing.T) {
|
func TestHttpJsonOn404(t *testing.T) {
|
||||||
|
|
||||||
jolokia := genJolokiaClientStub(invalidJSON, 404, Servers,
|
jolokia := genJolokiaClientStub(validJavaMultiValueJSON, 404, Servers,
|
||||||
[]string{HeapMetric})
|
[]string{HeapMetric})
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := acc.GatherError(jolokia.Gather)
|
err := jolokia.Gather(&acc)
|
||||||
|
|
||||||
assert.Error(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, len(acc.Metrics))
|
assert.Equal(t, 0, len(acc.Metrics))
|
||||||
assert.Contains(t, err.Error(), "has status code 404")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the proper values are ignored or collected for class=Cassandra
|
// Test that the proper values are ignored or collected for class=Cassandra
|
||||||
@@ -215,7 +214,7 @@ func TestHttpJsonCassandraMultiValue(t *testing.T) {
|
|||||||
cassandra := genJolokiaClientStub(validCassandraMultiValueJSON, 200, Servers, []string{ReadLatencyMetric})
|
cassandra := genJolokiaClientStub(validCassandraMultiValueJSON, 200, Servers, []string{ReadLatencyMetric})
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := acc.GatherError(cassandra.Gather)
|
err := cassandra.Gather(&acc)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(acc.Metrics))
|
assert.Equal(t, 1, len(acc.Metrics))
|
||||||
@@ -247,7 +246,7 @@ func TestHttpJsonCassandraNestedMultiValue(t *testing.T) {
|
|||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
acc.SetDebug(true)
|
acc.SetDebug(true)
|
||||||
err := acc.GatherError(cassandra.Gather)
|
err := cassandra.Gather(&acc)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, len(acc.Metrics))
|
assert.Equal(t, 2, len(acc.Metrics))
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ the cluster. The currently supported commands are:
|
|||||||
|
|
||||||
## Whether to gather statistics via ceph commands, requires ceph_user and ceph_config
|
## Whether to gather statistics via ceph commands, requires ceph_user and ceph_config
|
||||||
## to be specified
|
## to be specified
|
||||||
gather_cluster_stats = false
|
gather_cluster_stats = true
|
||||||
```
|
```
|
||||||
|
|
||||||
### Measurements & Fields:
|
### Measurements & Fields:
|
||||||
@@ -117,7 +117,7 @@ All fields are collected under the **ceph** measurement and stored as float64s.
|
|||||||
* recovering\_objects\_per\_sec (float)
|
* recovering\_objects\_per\_sec (float)
|
||||||
|
|
||||||
* ceph\_pgmap\_state
|
* ceph\_pgmap\_state
|
||||||
* count (float)
|
* state name e.g. active+clean (float)
|
||||||
|
|
||||||
* ceph\_usage
|
* ceph\_usage
|
||||||
* bytes\_used (float)
|
* bytes\_used (float)
|
||||||
@@ -186,7 +186,7 @@ All measurements will have the following tags:
|
|||||||
|
|
||||||
*Cluster Stats*
|
*Cluster Stats*
|
||||||
|
|
||||||
* ceph\_pgmap\_state has the following tags:
|
* ceph\_pg\_state has the following tags:
|
||||||
* state (state for which the value applies e.g. active+clean, active+remapped+backfill)
|
* state (state for which the value applies e.g. active+clean, active+remapped+backfill)
|
||||||
* ceph\_pool\_usage has the following tags:
|
* ceph\_pool\_usage has the following tags:
|
||||||
* id
|
* id
|
||||||
@@ -213,8 +213,7 @@ telegraf -test -config /etc/telegraf/telegraf.conf -config-directory /etc/telegr
|
|||||||
<pre>
|
<pre>
|
||||||
> ceph_osdmap,host=ceph-mon-0 epoch=170772,full=false,nearfull=false,num_in_osds=340,num_osds=340,num_remapped_pgs=0,num_up_osds=340 1468841037000000000
|
> ceph_osdmap,host=ceph-mon-0 epoch=170772,full=false,nearfull=false,num_in_osds=340,num_osds=340,num_remapped_pgs=0,num_up_osds=340 1468841037000000000
|
||||||
> ceph_pgmap,host=ceph-mon-0 bytes_avail=634895531270144,bytes_total=812117151809536,bytes_used=177221620539392,data_bytes=56979991615058,num_pgs=22952,op_per_sec=15869,read_bytes_sec=43956026,version=39387592,write_bytes_sec=165344818 1468841037000000000
|
> ceph_pgmap,host=ceph-mon-0 bytes_avail=634895531270144,bytes_total=812117151809536,bytes_used=177221620539392,data_bytes=56979991615058,num_pgs=22952,op_per_sec=15869,read_bytes_sec=43956026,version=39387592,write_bytes_sec=165344818 1468841037000000000
|
||||||
> ceph_pgmap_state,host=ceph-mon-0,state=active+clean count=22952 1468928660000000000
|
> ceph_pgmap_state,host=ceph-mon-0 active+clean=22952 1468928660000000000
|
||||||
> ceph_pgmap_state,host=ceph-mon-0,state=active+degraded count=16 1468928660000000000
|
|
||||||
> ceph_usage,host=ceph-mon-0 total_avail_bytes=634895514791936,total_bytes=812117151809536,total_used_bytes=177221637017600 1468841037000000000
|
> ceph_usage,host=ceph-mon-0 total_avail_bytes=634895514791936,total_bytes=812117151809536,total_used_bytes=177221637017600 1468841037000000000
|
||||||
> ceph_pool_usage,host=ceph-mon-0,id=150,name=cinder.volumes bytes_used=12648553794802,kb_used=12352103316,max_avail=154342562489244,objects=3026295 1468841037000000000
|
> ceph_pool_usage,host=ceph-mon-0,id=150,name=cinder.volumes bytes_used=12648553794802,kb_used=12352103316,max_avail=154342562489244,objects=3026295 1468841037000000000
|
||||||
> ceph_pool_usage,host=ceph-mon-0,id=182,name=cinder.volumes.flash bytes_used=8541308223964,kb_used=8341121313,max_avail=39388593563936,objects=2075066 1468841037000000000
|
> ceph_pool_usage,host=ceph-mon-0,id=182,name=cinder.volumes.flash bytes_used=8541308223964,kb_used=8341121313,max_avail=39388593563936,objects=2075066 1468841037000000000
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/influxdata/telegraf/plugins"
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -69,14 +68,14 @@ var sampleConfig = `
|
|||||||
gather_admin_socket_stats = true
|
gather_admin_socket_stats = true
|
||||||
|
|
||||||
## Whether to gather statistics via ceph commands
|
## Whether to gather statistics via ceph commands
|
||||||
gather_cluster_stats = false
|
gather_cluster_stats = true
|
||||||
`
|
`
|
||||||
|
|
||||||
func (c *Ceph) SampleConfig() string {
|
func (c *Ceph) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ceph) Gather(acc telegraf.Accumulator) error {
|
func (c *Ceph) Gather(acc plugins.Accumulator) error {
|
||||||
if c.GatherAdminSocketStats {
|
if c.GatherAdminSocketStats {
|
||||||
if err := c.gatherAdminSocketStats(acc); err != nil {
|
if err := c.gatherAdminSocketStats(acc); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -92,7 +91,7 @@ func (c *Ceph) Gather(acc telegraf.Accumulator) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ceph) gatherAdminSocketStats(acc telegraf.Accumulator) error {
|
func (c *Ceph) gatherAdminSocketStats(acc plugins.Accumulator) error {
|
||||||
sockets, err := findSockets(c)
|
sockets, err := findSockets(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find sockets at path '%s': %v", c.SocketDir, err)
|
return fmt.Errorf("failed to find sockets at path '%s': %v", c.SocketDir, err)
|
||||||
@@ -101,15 +100,15 @@ func (c *Ceph) gatherAdminSocketStats(acc telegraf.Accumulator) error {
|
|||||||
for _, s := range sockets {
|
for _, s := range sockets {
|
||||||
dump, err := perfDump(c.CephBinary, s)
|
dump, err := perfDump(c.CephBinary, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(fmt.Errorf("E! error reading from socket '%s': %v", s.socket, err))
|
log.Printf("E! error reading from socket '%s': %v", s.socket, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
data, err := parseDump(dump)
|
data, err := parseDump(dump)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(fmt.Errorf("E! error parsing dump from socket '%s': %v", s.socket, err))
|
log.Printf("E! error parsing dump from socket '%s': %v", s.socket, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for tag, metrics := range data {
|
for tag, metrics := range *data {
|
||||||
acc.AddFields(measurement,
|
acc.AddFields(measurement,
|
||||||
map[string]interface{}(metrics),
|
map[string]interface{}(metrics),
|
||||||
map[string]string{"type": s.sockType, "id": s.sockId, "collection": tag})
|
map[string]string{"type": s.sockType, "id": s.sockId, "collection": tag})
|
||||||
@@ -118,10 +117,10 @@ func (c *Ceph) gatherAdminSocketStats(acc telegraf.Accumulator) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ceph) gatherClusterStats(acc telegraf.Accumulator) error {
|
func (c *Ceph) gatherClusterStats(acc plugins.Accumulator) error {
|
||||||
jobs := []struct {
|
jobs := []struct {
|
||||||
command string
|
command string
|
||||||
parser func(telegraf.Accumulator, string) error
|
parser func(plugins.Accumulator, string) error
|
||||||
}{
|
}{
|
||||||
{"status", decodeStatus},
|
{"status", decodeStatus},
|
||||||
{"df", decodeDf},
|
{"df", decodeDf},
|
||||||
@@ -156,7 +155,7 @@ func init() {
|
|||||||
GatherClusterStats: false,
|
GatherClusterStats: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs.Add(measurement, func() telegraf.Input { return &c })
|
inputs.Add(measurement, func() plugins.Input { return &c })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,19 +244,25 @@ type taggedMetricMap map[string]metricMap
|
|||||||
|
|
||||||
// Parses a raw JSON string into a taggedMetricMap
|
// Parses a raw JSON string into a taggedMetricMap
|
||||||
// Delegates the actual parsing to newTaggedMetricMap(..)
|
// Delegates the actual parsing to newTaggedMetricMap(..)
|
||||||
func parseDump(dump string) (taggedMetricMap, error) {
|
func parseDump(dump string) (*taggedMetricMap, error) {
|
||||||
data := make(map[string]interface{})
|
data := make(map[string]interface{})
|
||||||
err := json.Unmarshal([]byte(dump), &data)
|
err := json.Unmarshal([]byte(dump), &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse json: '%s': %v", dump, err)
|
return nil, fmt.Errorf("failed to parse json: '%s': %v", dump, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newTaggedMetricMap(data), nil
|
tmm := newTaggedMetricMap(data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to tag dataset: '%v': %v", tmm, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builds a TaggedMetricMap out of a generic string map.
|
// Builds a TaggedMetricMap out of a generic string map.
|
||||||
// The top-level key is used as a tag and all sub-keys are flattened into metrics
|
// The top-level key is used as a tag and all sub-keys are flattened into metrics
|
||||||
func newTaggedMetricMap(data map[string]interface{}) taggedMetricMap {
|
func newTaggedMetricMap(data map[string]interface{}) *taggedMetricMap {
|
||||||
tmm := make(taggedMetricMap)
|
tmm := make(taggedMetricMap)
|
||||||
for tag, datapoints := range data {
|
for tag, datapoints := range data {
|
||||||
mm := make(metricMap)
|
mm := make(metricMap)
|
||||||
@@ -266,7 +271,7 @@ func newTaggedMetricMap(data map[string]interface{}) taggedMetricMap {
|
|||||||
}
|
}
|
||||||
tmm[tag] = mm
|
tmm[tag] = mm
|
||||||
}
|
}
|
||||||
return tmm
|
return &tmm
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively flattens any k-v hierarchy present in data.
|
// Recursively flattens any k-v hierarchy present in data.
|
||||||
@@ -317,7 +322,7 @@ func (c *Ceph) exec(command string) (string, error) {
|
|||||||
return output, nil
|
return output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeStatus(acc telegraf.Accumulator, input string) error {
|
func decodeStatus(acc plugins.Accumulator, input string) error {
|
||||||
data := make(map[string]interface{})
|
data := make(map[string]interface{})
|
||||||
err := json.Unmarshal([]byte(input), &data)
|
err := json.Unmarshal([]byte(input), &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -342,7 +347,7 @@ func decodeStatus(acc telegraf.Accumulator, input string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeStatusOsdmap(acc telegraf.Accumulator, data map[string]interface{}) error {
|
func decodeStatusOsdmap(acc plugins.Accumulator, data map[string]interface{}) error {
|
||||||
osdmap, ok := data["osdmap"].(map[string]interface{})
|
osdmap, ok := data["osdmap"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("WARNING %s - unable to decode osdmap", measurement)
|
return fmt.Errorf("WARNING %s - unable to decode osdmap", measurement)
|
||||||
@@ -355,7 +360,7 @@ func decodeStatusOsdmap(acc telegraf.Accumulator, data map[string]interface{}) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeStatusPgmap(acc telegraf.Accumulator, data map[string]interface{}) error {
|
func decodeStatusPgmap(acc plugins.Accumulator, data map[string]interface{}) error {
|
||||||
pgmap, ok := data["pgmap"].(map[string]interface{})
|
pgmap, ok := data["pgmap"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("WARNING %s - unable to decode pgmap", measurement)
|
return fmt.Errorf("WARNING %s - unable to decode pgmap", measurement)
|
||||||
@@ -371,57 +376,40 @@ func decodeStatusPgmap(acc telegraf.Accumulator, data map[string]interface{}) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractPgmapStates(data map[string]interface{}) ([]interface{}, error) {
|
func decodeStatusPgmapState(acc plugins.Accumulator, data map[string]interface{}) error {
|
||||||
const key = "pgs_by_state"
|
|
||||||
|
|
||||||
pgmap, ok := data["pgmap"].(map[string]interface{})
|
pgmap, ok := data["pgmap"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("WARNING %s - unable to decode pgmap", measurement)
|
return fmt.Errorf("WARNING %s - unable to decode pgmap", measurement)
|
||||||
}
|
}
|
||||||
|
fields := make(map[string]interface{})
|
||||||
s, ok := pgmap[key]
|
for key, value := range pgmap {
|
||||||
if !ok {
|
switch value.(type) {
|
||||||
return nil, fmt.Errorf("WARNING %s - pgmap is missing the %s field", measurement, key)
|
case []interface{}:
|
||||||
|
if key != "pgs_by_state" {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
for _, state := range value.([]interface{}) {
|
||||||
states, ok := s.([]interface{})
|
state_map, ok := state.(map[string]interface{})
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("WARNING %s - pgmap[%s] is not a list", measurement, key)
|
|
||||||
}
|
|
||||||
return states, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeStatusPgmapState(acc telegraf.Accumulator, data map[string]interface{}) error {
|
|
||||||
states, err := extractPgmapStates(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, state := range states {
|
|
||||||
stateMap, ok := state.(map[string]interface{})
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("WARNING %s - unable to decode pg state", measurement)
|
return fmt.Errorf("WARNING %s - unable to decode pg state", measurement)
|
||||||
}
|
}
|
||||||
stateName, ok := stateMap["state_name"].(string)
|
state_name, ok := state_map["state_name"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("WARNING %s - unable to decode pg state name", measurement)
|
return fmt.Errorf("WARNING %s - unable to decode pg state name", measurement)
|
||||||
}
|
}
|
||||||
stateCount, ok := stateMap["count"].(float64)
|
state_count, ok := state_map["count"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("WARNING %s - unable to decode pg state count", measurement)
|
return fmt.Errorf("WARNING %s - unable to decode pg state count", measurement)
|
||||||
}
|
}
|
||||||
|
fields[state_name] = state_count
|
||||||
tags := map[string]string{
|
|
||||||
"state": stateName,
|
|
||||||
}
|
}
|
||||||
fields := map[string]interface{}{
|
|
||||||
"count": stateCount,
|
|
||||||
}
|
}
|
||||||
acc.AddFields("ceph_pgmap_state", fields, tags)
|
|
||||||
}
|
}
|
||||||
|
acc.AddFields("ceph_pgmap_state", fields, map[string]string{})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeDf(acc telegraf.Accumulator, input string) error {
|
func decodeDf(acc plugins.Accumulator, input string) error {
|
||||||
data := make(map[string]interface{})
|
data := make(map[string]interface{})
|
||||||
err := json.Unmarshal([]byte(input), &data)
|
err := json.Unmarshal([]byte(input), &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -463,7 +451,7 @@ func decodeDf(acc telegraf.Accumulator, input string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeOsdPoolStats(acc telegraf.Accumulator, input string) error {
|
func decodeOsdPoolStats(acc plugins.Accumulator, input string) error {
|
||||||
data := make([]map[string]interface{}, 0)
|
data := make([]map[string]interface{}, 0)
|
||||||
err := json.Unmarshal([]byte(input), &data)
|
err := json.Unmarshal([]byte(input), &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
package ceph
|
package ceph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -26,38 +24,15 @@ func TestParseSockId(t *testing.T) {
|
|||||||
func TestParseMonDump(t *testing.T) {
|
func TestParseMonDump(t *testing.T) {
|
||||||
dump, err := parseDump(monPerfDump)
|
dump, err := parseDump(monPerfDump)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.InEpsilon(t, 5678670180, dump["cluster"]["osd_kb_used"], epsilon)
|
assert.InEpsilon(t, 5678670180, (*dump)["cluster"]["osd_kb_used"], epsilon)
|
||||||
assert.InEpsilon(t, 6866.540527000, dump["paxos"]["store_state_latency.sum"], epsilon)
|
assert.InEpsilon(t, 6866.540527000, (*dump)["paxos"]["store_state_latency.sum"], epsilon)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseOsdDump(t *testing.T) {
|
func TestParseOsdDump(t *testing.T) {
|
||||||
dump, err := parseDump(osdPerfDump)
|
dump, err := parseDump(osdPerfDump)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.InEpsilon(t, 552132.109360000, dump["filestore"]["commitcycle_interval.sum"], epsilon)
|
assert.InEpsilon(t, 552132.109360000, (*dump)["filestore"]["commitcycle_interval.sum"], epsilon)
|
||||||
assert.Equal(t, float64(0), dump["mutex-FileJournal::finisher_lock"]["wait.avgcount"])
|
assert.Equal(t, float64(0), (*dump)["mutex-FileJournal::finisher_lock"]["wait.avgcount"])
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeStatusPgmapState(t *testing.T) {
|
|
||||||
data := make(map[string]interface{})
|
|
||||||
err := json.Unmarshal([]byte(clusterStatusDump), &data)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
acc := &testutil.Accumulator{}
|
|
||||||
err = decodeStatusPgmapState(acc, data)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
var results = []struct {
|
|
||||||
fields map[string]interface{}
|
|
||||||
tags map[string]string
|
|
||||||
}{
|
|
||||||
{map[string]interface{}{"count": float64(2560)}, map[string]string{"state": "active+clean"}},
|
|
||||||
{map[string]interface{}{"count": float64(10)}, map[string]string{"state": "active+scrubbing"}},
|
|
||||||
{map[string]interface{}{"count": float64(5)}, map[string]string{"state": "active+backfilling"}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range results {
|
|
||||||
acc.AssertContainsTaggedFields(t, "ceph_pgmap_state", r.fields, r.tags)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather(t *testing.T) {
|
func TestGather(t *testing.T) {
|
||||||
@@ -710,127 +685,3 @@ var osdPerfDump = `
|
|||||||
"wait": { "avgcount": 0,
|
"wait": { "avgcount": 0,
|
||||||
"sum": 0.000000000}}}
|
"sum": 0.000000000}}}
|
||||||
`
|
`
|
||||||
var clusterStatusDump = `
|
|
||||||
{
|
|
||||||
"health": {
|
|
||||||
"health": {
|
|
||||||
"health_services": [
|
|
||||||
{
|
|
||||||
"mons": [
|
|
||||||
{
|
|
||||||
"name": "a",
|
|
||||||
"kb_total": 114289256,
|
|
||||||
"kb_used": 26995516,
|
|
||||||
"kb_avail": 81465132,
|
|
||||||
"avail_percent": 71,
|
|
||||||
"last_updated": "2017-01-03 17:20:57.595004",
|
|
||||||
"store_stats": {
|
|
||||||
"bytes_total": 942117141,
|
|
||||||
"bytes_sst": 0,
|
|
||||||
"bytes_log": 4345406,
|
|
||||||
"bytes_misc": 937771735,
|
|
||||||
"last_updated": "0.000000"
|
|
||||||
},
|
|
||||||
"health": "HEALTH_OK"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "b",
|
|
||||||
"kb_total": 114289256,
|
|
||||||
"kb_used": 27871624,
|
|
||||||
"kb_avail": 80589024,
|
|
||||||
"avail_percent": 70,
|
|
||||||
"last_updated": "2017-01-03 17:20:47.784331",
|
|
||||||
"store_stats": {
|
|
||||||
"bytes_total": 454853104,
|
|
||||||
"bytes_sst": 0,
|
|
||||||
"bytes_log": 5788320,
|
|
||||||
"bytes_misc": 449064784,
|
|
||||||
"last_updated": "0.000000"
|
|
||||||
},
|
|
||||||
"health": "HEALTH_OK"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "c",
|
|
||||||
"kb_total": 130258508,
|
|
||||||
"kb_used": 38076996,
|
|
||||||
"kb_avail": 85541692,
|
|
||||||
"avail_percent": 65,
|
|
||||||
"last_updated": "2017-01-03 17:21:03.311123",
|
|
||||||
"store_stats": {
|
|
||||||
"bytes_total": 455555199,
|
|
||||||
"bytes_sst": 0,
|
|
||||||
"bytes_log": 6950876,
|
|
||||||
"bytes_misc": 448604323,
|
|
||||||
"last_updated": "0.000000"
|
|
||||||
},
|
|
||||||
"health": "HEALTH_OK"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"timechecks": {
|
|
||||||
"epoch": 504,
|
|
||||||
"round": 34642,
|
|
||||||
"round_status": "finished",
|
|
||||||
"mons": [
|
|
||||||
{ "name": "a", "skew": 0, "latency": 0, "health": "HEALTH_OK" },
|
|
||||||
{ "name": "b", "skew": -0, "latency": 0.000951, "health": "HEALTH_OK" },
|
|
||||||
{ "name": "c", "skew": -0, "latency": 0.000946, "health": "HEALTH_OK" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"summary": [],
|
|
||||||
"overall_status": "HEALTH_OK",
|
|
||||||
"detail": []
|
|
||||||
},
|
|
||||||
"fsid": "01234567-abcd-9876-0123-ffeeddccbbaa",
|
|
||||||
"election_epoch": 504,
|
|
||||||
"quorum": [ 0, 1, 2 ],
|
|
||||||
"quorum_names": [ "a", "b", "c" ],
|
|
||||||
"monmap": {
|
|
||||||
"epoch": 17,
|
|
||||||
"fsid": "01234567-abcd-9876-0123-ffeeddccbbaa",
|
|
||||||
"modified": "2016-04-11 14:01:52.600198",
|
|
||||||
"created": "0.000000",
|
|
||||||
"mons": [
|
|
||||||
{ "rank": 0, "name": "a", "addr": "192.168.0.1:6789/0" },
|
|
||||||
{ "rank": 1, "name": "b", "addr": "192.168.0.2:6789/0" },
|
|
||||||
{ "rank": 2, "name": "c", "addr": "192.168.0.3:6789/0" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osdmap": {
|
|
||||||
"osdmap": {
|
|
||||||
"epoch": 21734,
|
|
||||||
"num_osds": 24,
|
|
||||||
"num_up_osds": 24,
|
|
||||||
"num_in_osds": 24,
|
|
||||||
"full": false,
|
|
||||||
"nearfull": false,
|
|
||||||
"num_remapped_pgs": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pgmap": {
|
|
||||||
"pgs_by_state": [
|
|
||||||
{ "state_name": "active+clean", "count": 2560 },
|
|
||||||
{ "state_name": "active+scrubbing", "count": 10 },
|
|
||||||
{ "state_name": "active+backfilling", "count": 5 }
|
|
||||||
],
|
|
||||||
"version": 52314277,
|
|
||||||
"num_pgs": 2560,
|
|
||||||
"data_bytes": 2700031960713,
|
|
||||||
"bytes_used": 7478347665408,
|
|
||||||
"bytes_avail": 9857462382592,
|
|
||||||
"bytes_total": 17335810048000,
|
|
||||||
"read_bytes_sec": 0,
|
|
||||||
"write_bytes_sec": 367217,
|
|
||||||
"op_per_sec": 98
|
|
||||||
},
|
|
||||||
"mdsmap": {
|
|
||||||
"epoch": 1,
|
|
||||||
"up": 0,
|
|
||||||
"in": 0,
|
|
||||||
"max": 0,
|
|
||||||
"by_rank": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cgroup
|
package cgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,5 +34,5 @@ func (g *CGroup) Description() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("cgroup", func() telegraf.Input { return &CGroup{} })
|
inputs.Add("cgroup", func() plugins.Input { return &CGroup{} })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,29 +11,28 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
const metricName = "cgroup"
|
const metricName = "cgroup"
|
||||||
|
|
||||||
func (g *CGroup) Gather(acc telegraf.Accumulator) error {
|
func (g *CGroup) Gather(acc plugins.Accumulator) error {
|
||||||
list := make(chan pathInfo)
|
list := make(chan pathInfo)
|
||||||
go g.generateDirs(list)
|
go g.generateDirs(list)
|
||||||
|
|
||||||
for dir := range list {
|
for dir := range list {
|
||||||
if dir.err != nil {
|
if dir.err != nil {
|
||||||
acc.AddError(dir.err)
|
return dir.err
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if err := g.gatherDir(dir.path, acc); err != nil {
|
if err := g.gatherDir(dir.path, acc); err != nil {
|
||||||
acc.AddError(err)
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *CGroup) gatherDir(dir string, acc telegraf.Accumulator) error {
|
func (g *CGroup) gatherDir(dir string, acc plugins.Accumulator) error {
|
||||||
fields := make(map[string]interface{})
|
fields := make(map[string]interface{})
|
||||||
|
|
||||||
list := make(chan pathInfo)
|
list := make(chan pathInfo)
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
package cgroup
|
package cgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *CGroup) Gather(acc telegraf.Accumulator) error {
|
func (g *CGroup) Gather(acc plugins.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var cg1 = &CGroup{
|
|||||||
func TestCgroupStatistics_1(t *testing.T) {
|
func TestCgroupStatistics_1(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := acc.GatherError(cg1.Gather)
|
err := cg1.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
@@ -56,7 +56,7 @@ var cg2 = &CGroup{
|
|||||||
func TestCgroupStatistics_2(t *testing.T) {
|
func TestCgroupStatistics_2(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := acc.GatherError(cg2.Gather)
|
err := cg2.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
@@ -81,7 +81,7 @@ var cg3 = &CGroup{
|
|||||||
func TestCgroupStatistics_3(t *testing.T) {
|
func TestCgroupStatistics_3(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := acc.GatherError(cg3.Gather)
|
err := cg3.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
@@ -108,7 +108,7 @@ var cg4 = &CGroup{
|
|||||||
func TestCgroupStatistics_4(t *testing.T) {
|
func TestCgroupStatistics_4(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := acc.GatherError(cg4.Gather)
|
err := cg4.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
@@ -140,7 +140,7 @@ var cg5 = &CGroup{
|
|||||||
func TestCgroupStatistics_5(t *testing.T) {
|
func TestCgroupStatistics_5(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := acc.GatherError(cg5.Gather)
|
err := cg5.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
@@ -167,7 +167,7 @@ var cg6 = &CGroup{
|
|||||||
func TestCgroupStatistics_6(t *testing.T) {
|
func TestCgroupStatistics_6(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := acc.GatherError(cg6.Gather)
|
err := cg6.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ Delete second or Not synchronised.
|
|||||||
### Measurements & Fields:
|
### Measurements & Fields:
|
||||||
|
|
||||||
- chrony
|
- chrony
|
||||||
- system_time (float, seconds)
|
|
||||||
- last_offset (float, seconds)
|
- last_offset (float, seconds)
|
||||||
- rms_offset (float, seconds)
|
- rms_offset (float, seconds)
|
||||||
- frequency (float, ppm)
|
- frequency (float, ppm)
|
||||||
@@ -85,7 +84,7 @@ Delete second or Not synchronised.
|
|||||||
```
|
```
|
||||||
$ telegraf -config telegraf.conf -input-filter chrony -test
|
$ telegraf -config telegraf.conf -input-filter chrony -test
|
||||||
* Plugin: chrony, Collection 1
|
* Plugin: chrony, Collection 1
|
||||||
> chrony,leap_status=normal,reference_id=192.168.1.1,stratum=3 frequency=-35.657,system_time=0.000027073,last_offset=-0.000013616,residual_freq=-0,rms_offset=0.000027073,root_delay=0.000644,root_dispersion=0.003444,skew=0.001,update_interval=1031.2 1463750789687639161
|
> chrony,leap_status=normal,reference_id=192.168.1.1,stratum=3 frequency=-35.657,last_offset=-0.000013616,residual_freq=-0,rms_offset=0.000027073,root_delay=0.000644,root_dispersion=0.003444,skew=0.001,update_interval=1031.2 1463750789687639161
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
@@ -35,7 +35,7 @@ func (*Chrony) SampleConfig() string {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chrony) Gather(acc telegraf.Accumulator) error {
|
func (c *Chrony) Gather(acc plugins.Accumulator) error {
|
||||||
if len(c.path) == 0 {
|
if len(c.path) == 0 {
|
||||||
return errors.New("chronyc not found: verify that chrony is installed and that chronyc is in your PATH")
|
return errors.New("chronyc not found: verify that chrony is installed and that chronyc is in your PATH")
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ func processChronycOutput(out string) (map[string]interface{}, map[string]string
|
|||||||
}
|
}
|
||||||
name := strings.ToLower(strings.Replace(strings.TrimSpace(stats[0]), " ", "_", -1))
|
name := strings.ToLower(strings.Replace(strings.TrimSpace(stats[0]), " ", "_", -1))
|
||||||
// ignore reference time
|
// ignore reference time
|
||||||
if strings.Contains(name, "ref_time") {
|
if strings.Contains(name, "time") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
valueFields := strings.Fields(stats[1])
|
valueFields := strings.Fields(stats[1])
|
||||||
@@ -127,7 +127,7 @@ func init() {
|
|||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
c.path = path
|
c.path = path
|
||||||
}
|
}
|
||||||
inputs.Add("chrony", func() telegraf.Input {
|
inputs.Add("chrony", func() plugins.Input {
|
||||||
return &c
|
return &c
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ func TestGather(t *testing.T) {
|
|||||||
"stratum": "3",
|
"stratum": "3",
|
||||||
}
|
}
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"system_time": 0.000020390,
|
|
||||||
"last_offset": 0.000012651,
|
"last_offset": 0.000012651,
|
||||||
"rms_offset": 0.000025577,
|
"rms_offset": 0.000025577,
|
||||||
"frequency": -16.001,
|
"frequency": -16.001,
|
||||||
|
|||||||
@@ -42,10 +42,9 @@ API endpoint. In the following order the plugin will attempt to authenticate.
|
|||||||
namespace = "AWS/ELB"
|
namespace = "AWS/ELB"
|
||||||
|
|
||||||
## Maximum requests per second. Note that the global default AWS rate limit is
|
## Maximum requests per second. Note that the global default AWS rate limit is
|
||||||
## 400 reqs/sec, so if you define multiple namespaces, these should add up to a
|
## 10 reqs/sec, so if you define multiple namespaces, these should add up to a
|
||||||
## maximum of 400. Optional - default value is 200.
|
## maximum of 10. Optional - default value is 10.
|
||||||
## See http://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_limits.html
|
ratelimit = 10
|
||||||
ratelimit = 200
|
|
||||||
|
|
||||||
## Metrics to Pull (optional)
|
## Metrics to Pull (optional)
|
||||||
## Defaults to all Metrics in Namespace if nothing is provided
|
## Defaults to all Metrics in Namespace if nothing is provided
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
internalaws "github.com/influxdata/telegraf/internal/config/aws"
|
internalaws "github.com/influxdata/telegraf/internal/config/aws"
|
||||||
|
"github.com/influxdata/telegraf/internal/errchan"
|
||||||
"github.com/influxdata/telegraf/internal/limiter"
|
"github.com/influxdata/telegraf/internal/limiter"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
@@ -104,10 +105,9 @@ func (c *CloudWatch) SampleConfig() string {
|
|||||||
namespace = "AWS/ELB"
|
namespace = "AWS/ELB"
|
||||||
|
|
||||||
## Maximum requests per second. Note that the global default AWS rate limit is
|
## Maximum requests per second. Note that the global default AWS rate limit is
|
||||||
## 400 reqs/sec, so if you define multiple namespaces, these should add up to a
|
## 10 reqs/sec, so if you define multiple namespaces, these should add up to a
|
||||||
## maximum of 400. Optional - default value is 200.
|
## maximum of 10. Optional - default value is 10.
|
||||||
## See http://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_limits.html
|
ratelimit = 10
|
||||||
ratelimit = 200
|
|
||||||
|
|
||||||
## Metrics to Pull (optional)
|
## Metrics to Pull (optional)
|
||||||
## Defaults to all Metrics in Namespace if nothing is provided
|
## Defaults to all Metrics in Namespace if nothing is provided
|
||||||
@@ -176,7 +176,7 @@ func SelectMetrics(c *CloudWatch) ([]*cloudwatch.Metric, error) {
|
|||||||
return metrics, nil
|
return metrics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CloudWatch) Gather(acc telegraf.Accumulator) error {
|
func (c *CloudWatch) Gather(acc plugins.Accumulator) error {
|
||||||
if c.client == nil {
|
if c.client == nil {
|
||||||
c.initializeCloudWatch()
|
c.initializeCloudWatch()
|
||||||
}
|
}
|
||||||
@@ -185,6 +185,8 @@ func (c *CloudWatch) Gather(acc telegraf.Accumulator) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
metricCount := len(metrics)
|
||||||
|
errChan := errchan.New(metricCount)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
@@ -199,20 +201,20 @@ func (c *CloudWatch) Gather(acc telegraf.Accumulator) error {
|
|||||||
<-lmtr.C
|
<-lmtr.C
|
||||||
go func(inm *cloudwatch.Metric) {
|
go func(inm *cloudwatch.Metric) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
acc.AddError(c.gatherMetric(acc, inm, now))
|
c.gatherMetric(acc, inm, now, errChan.C)
|
||||||
}(m)
|
}(m)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
return nil
|
return errChan.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("cloudwatch", func() telegraf.Input {
|
inputs.Add("cloudwatch", func() plugins.Input {
|
||||||
ttl, _ := time.ParseDuration("1hr")
|
ttl, _ := time.ParseDuration("1hr")
|
||||||
return &CloudWatch{
|
return &CloudWatch{
|
||||||
CacheTTL: internal.Duration{Duration: ttl},
|
CacheTTL: internal.Duration{Duration: ttl},
|
||||||
RateLimit: 200,
|
RateLimit: 10,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -279,14 +281,16 @@ func (c *CloudWatch) fetchNamespaceMetrics() ([]*cloudwatch.Metric, error) {
|
|||||||
* Gather given Metric and emit any error
|
* Gather given Metric and emit any error
|
||||||
*/
|
*/
|
||||||
func (c *CloudWatch) gatherMetric(
|
func (c *CloudWatch) gatherMetric(
|
||||||
acc telegraf.Accumulator,
|
acc plugins.Accumulator,
|
||||||
metric *cloudwatch.Metric,
|
metric *cloudwatch.Metric,
|
||||||
now time.Time,
|
now time.Time,
|
||||||
) error {
|
errChan chan error,
|
||||||
|
) {
|
||||||
params := c.getStatisticsInput(metric, now)
|
params := c.getStatisticsInput(metric, now)
|
||||||
resp, err := c.client.GetMetricStatistics(params)
|
resp, err := c.client.GetMetricStatistics(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
errChan <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, point := range resp.Datapoints {
|
for _, point := range resp.Datapoints {
|
||||||
@@ -321,7 +325,7 @@ func (c *CloudWatch) gatherMetric(
|
|||||||
acc.AddFields(formatMeasurement(c.Namespace), fields, tags, *point.Timestamp)
|
acc.AddFields(formatMeasurement(c.Namespace), fields, tags, *point.Timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
errChan <- nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -58,13 +58,13 @@ func TestGather(t *testing.T) {
|
|||||||
Namespace: "AWS/ELB",
|
Namespace: "AWS/ELB",
|
||||||
Delay: internalDuration,
|
Delay: internalDuration,
|
||||||
Period: internalDuration,
|
Period: internalDuration,
|
||||||
RateLimit: 200,
|
RateLimit: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
c.client = &mockGatherCloudWatchClient{}
|
c.client = &mockGatherCloudWatchClient{}
|
||||||
|
|
||||||
acc.GatherError(c.Gather)
|
c.Gather(&acc)
|
||||||
|
|
||||||
fields := map[string]interface{}{}
|
fields := map[string]interface{}{}
|
||||||
fields["latency_minimum"] = 0.1
|
fields["latency_minimum"] = 0.1
|
||||||
@@ -146,7 +146,7 @@ func TestSelectMetrics(t *testing.T) {
|
|||||||
Namespace: "AWS/ELB",
|
Namespace: "AWS/ELB",
|
||||||
Delay: internalDuration,
|
Delay: internalDuration,
|
||||||
Period: internalDuration,
|
Period: internalDuration,
|
||||||
RateLimit: 200,
|
RateLimit: 10,
|
||||||
Metrics: []*Metric{
|
Metrics: []*Metric{
|
||||||
&Metric{
|
&Metric{
|
||||||
MetricNames: []string{"Latency", "RequestCount"},
|
MetricNames: []string{"Latency", "RequestCount"},
|
||||||
@@ -207,13 +207,14 @@ func TestGenerateStatisticsInputParams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMetricsCacheTimeout(t *testing.T) {
|
func TestMetricsCacheTimeout(t *testing.T) {
|
||||||
|
ttl, _ := time.ParseDuration("5ms")
|
||||||
cache := &MetricCache{
|
cache := &MetricCache{
|
||||||
Metrics: []*cloudwatch.Metric{},
|
Metrics: []*cloudwatch.Metric{},
|
||||||
Fetched: time.Now(),
|
Fetched: time.Now(),
|
||||||
TTL: time.Minute,
|
TTL: ttl,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.True(t, cache.IsValid())
|
assert.True(t, cache.IsValid())
|
||||||
cache.Fetched = time.Now().Add(-time.Minute)
|
time.Sleep(ttl)
|
||||||
assert.False(t, cache.IsValid())
|
assert.False(t, cache.IsValid())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ func (c *Conntrack) SampleConfig() string {
|
|||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conntrack) Gather(acc telegraf.Accumulator) error {
|
func (c *Conntrack) Gather(acc plugins.Accumulator) error {
|
||||||
c.setDefaults()
|
c.setDefaults()
|
||||||
|
|
||||||
var metricKey string
|
var metricKey string
|
||||||
@@ -92,15 +93,15 @@ func (c *Conntrack) Gather(acc telegraf.Accumulator) error {
|
|||||||
|
|
||||||
contents, err := ioutil.ReadFile(fName)
|
contents, err := ioutil.ReadFile(fName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(fmt.Errorf("E! failed to read file '%s': %v", fName, err))
|
log.Printf("E! failed to read file '%s': %v", fName, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
v := strings.TrimSpace(string(contents))
|
v := strings.TrimSpace(string(contents))
|
||||||
fields[metricKey], err = strconv.ParseFloat(v, 64)
|
fields[metricKey], err = strconv.ParseFloat(v, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(fmt.Errorf("E! failed to parse metric, expected number but "+
|
log.Printf("E! failed to parse metric, expected number but "+
|
||||||
" found '%s': %v", v, err))
|
" found '%s': %v", v, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,5 +116,5 @@ func (c *Conntrack) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add(inputName, func() telegraf.Input { return &Conntrack{} })
|
inputs.Add(inputName, func() plugins.Input { return &Conntrack{} })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,19 +35,12 @@ Fields:
|
|||||||
- check_name
|
- check_name
|
||||||
- service_id
|
- service_id
|
||||||
- status
|
- status
|
||||||
- passing
|
|
||||||
- critical
|
|
||||||
- warning
|
|
||||||
|
|
||||||
`passing`, `critical`, and `warning` are integer representations of the health
|
|
||||||
check state. A value of `1` represents that the status was the state of the
|
|
||||||
the health check at this sample.
|
|
||||||
|
|
||||||
## Example output
|
## Example output
|
||||||
|
|
||||||
```
|
```
|
||||||
$ telegraf --config ./telegraf.conf -input-filter consul -test
|
$ telegraf --config ./telegraf.conf -input-filter consul -test
|
||||||
* Plugin: consul, Collection 1
|
* Plugin: consul, Collection 1
|
||||||
> consul_health_checks,host=wolfpit,node=consul-server-node,check_id="serfHealth" check_name="Serf Health Status",service_id="",status="passing",passing=1i,critical=0i,warning=0i 1464698464486439902
|
> consul_health_checks,host=wolfpit,node=consul-server-node,check_id="serfHealth" check_name="Serf Health Status",service_id="",status="passing" 1464698464486439902
|
||||||
> consul_health_checks,host=wolfpit,node=consul-server-node,service_name=www.example.com,check_id="service:www-example-com.test01" check_name="Service 'www.example.com' check",service_id="www-example-com.test01",status="critical",passing=0i,critical=1i,warning=0i 1464698464486519036
|
> consul_health_checks,host=wolfpit,node=consul-server-node,service_name=www.example.com,check_id="service:www-example-com.test01" check_name="Service 'www.example.com' check",service_id="www-example-com.test01",status="critical" 1464698464486519036
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
@@ -90,19 +90,14 @@ func (c *Consul) createAPIClient() (*api.Client, error) {
|
|||||||
return api.NewClient(config)
|
return api.NewClient(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Consul) GatherHealthCheck(acc telegraf.Accumulator, checks []*api.HealthCheck) {
|
func (c *Consul) GatherHealthCheck(acc plugins.Accumulator, checks []*api.HealthCheck) {
|
||||||
for _, check := range checks {
|
for _, check := range checks {
|
||||||
record := make(map[string]interface{})
|
record := make(map[string]interface{})
|
||||||
tags := make(map[string]string)
|
tags := make(map[string]string)
|
||||||
|
|
||||||
record["check_name"] = check.Name
|
record["check_name"] = check.Name
|
||||||
record["service_id"] = check.ServiceID
|
record["service_id"] = check.ServiceID
|
||||||
|
|
||||||
record["status"] = check.Status
|
record["status"] = check.Status
|
||||||
record["passing"] = 0
|
|
||||||
record["critical"] = 0
|
|
||||||
record["warning"] = 0
|
|
||||||
record[check.Status] = 1
|
|
||||||
|
|
||||||
tags["node"] = check.Node
|
tags["node"] = check.Node
|
||||||
tags["service_name"] = check.ServiceName
|
tags["service_name"] = check.ServiceName
|
||||||
@@ -112,7 +107,7 @@ func (c *Consul) GatherHealthCheck(acc telegraf.Accumulator, checks []*api.Healt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Consul) Gather(acc telegraf.Accumulator) error {
|
func (c *Consul) Gather(acc plugins.Accumulator) error {
|
||||||
if c.client == nil {
|
if c.client == nil {
|
||||||
newClient, err := c.createAPIClient()
|
newClient, err := c.createAPIClient()
|
||||||
|
|
||||||
@@ -135,7 +130,7 @@ func (c *Consul) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("consul", func() telegraf.Input {
|
inputs.Add("consul", func() plugins.Input {
|
||||||
return &Consul{}
|
return &Consul{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ func TestGatherHealtCheck(t *testing.T) {
|
|||||||
expectedFields := map[string]interface{}{
|
expectedFields := map[string]interface{}{
|
||||||
"check_name": "foo.health",
|
"check_name": "foo.health",
|
||||||
"status": "passing",
|
"status": "passing",
|
||||||
"passing": 1,
|
|
||||||
"critical": 0,
|
|
||||||
"warning": 0,
|
|
||||||
"service_id": "foo.123",
|
"service_id": "foo.123",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package couchbase
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
couchbase "github.com/couchbase/go-couchbase"
|
couchbase "github.com/couchbase/go-couchbase"
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@@ -34,7 +34,7 @@ func (r *Couchbase) Description() string {
|
|||||||
|
|
||||||
// Reads stats from all configured clusters. Accumulates stats.
|
// Reads stats from all configured clusters. Accumulates stats.
|
||||||
// Returns one of the errors encountered while gathering stats (if any).
|
// Returns one of the errors encountered while gathering stats (if any).
|
||||||
func (r *Couchbase) Gather(acc telegraf.Accumulator) error {
|
func (r *Couchbase) Gather(acc plugins.Accumulator) error {
|
||||||
if len(r.Servers) == 0 {
|
if len(r.Servers) == 0 {
|
||||||
r.gatherServer("http://localhost:8091/", acc, nil)
|
r.gatherServer("http://localhost:8091/", acc, nil)
|
||||||
return nil
|
return nil
|
||||||
@@ -42,20 +42,22 @@ func (r *Couchbase) Gather(acc telegraf.Accumulator) error {
|
|||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
var outerr error
|
||||||
|
|
||||||
for _, serv := range r.Servers {
|
for _, serv := range r.Servers {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(serv string) {
|
go func(serv string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
acc.AddError(r.gatherServer(serv, acc, nil))
|
outerr = r.gatherServer(serv, acc, nil)
|
||||||
}(serv)
|
}(serv)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
return nil
|
return outerr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Couchbase) gatherServer(addr string, acc telegraf.Accumulator, pool *couchbase.Pool) error {
|
func (r *Couchbase) gatherServer(addr string, acc plugins.Accumulator, pool *couchbase.Pool) error {
|
||||||
if pool == nil {
|
if pool == nil {
|
||||||
client, err := couchbase.Connect(addr)
|
client, err := couchbase.Connect(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -96,7 +98,7 @@ func (r *Couchbase) gatherServer(addr string, acc telegraf.Accumulator, pool *co
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("couchbase", func() telegraf.Input {
|
inputs.Add("couchbase", func() plugins.Input {
|
||||||
return &Couchbase{}
|
return &Couchbase{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package couchdb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -80,23 +82,37 @@ func (*CouchDB) SampleConfig() string {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CouchDB) Gather(accumulator telegraf.Accumulator) error {
|
func (c *CouchDB) Gather(accumulator plugins.Accumulator) error {
|
||||||
|
errorChannel := make(chan error, len(c.HOSTs))
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for _, u := range c.HOSTs {
|
for _, u := range c.HOSTs {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(host string) {
|
go func(host string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := c.fetchAndInsertData(accumulator, host); err != nil {
|
if err := c.fetchAndInsertData(accumulator, host); err != nil {
|
||||||
accumulator.AddError(fmt.Errorf("[host=%s]: %s", host, err))
|
errorChannel <- fmt.Errorf("[host=%s]: %s", host, err)
|
||||||
}
|
}
|
||||||
}(u)
|
}(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
close(errorChannel)
|
||||||
|
|
||||||
|
// If there weren't any errors, we can return nil now.
|
||||||
|
if len(errorChannel) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There were errors, so join them all together as one big error.
|
||||||
|
errorStrings := make([]string, 0, len(errorChannel))
|
||||||
|
for err := range errorChannel {
|
||||||
|
errorStrings = append(errorStrings, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(strings.Join(errorStrings, "\n"))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var tr = &http.Transport{
|
var tr = &http.Transport{
|
||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
||||||
}
|
}
|
||||||
@@ -106,7 +122,7 @@ var client = &http.Client{
|
|||||||
Timeout: time.Duration(4 * time.Second),
|
Timeout: time.Duration(4 * time.Second),
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CouchDB) fetchAndInsertData(accumulator telegraf.Accumulator, host string) error {
|
func (c *CouchDB) fetchAndInsertData(accumulator plugins.Accumulator, host string) error {
|
||||||
|
|
||||||
response, error := client.Get(host)
|
response, error := client.Get(host)
|
||||||
if error != nil {
|
if error != nil {
|
||||||
@@ -193,7 +209,7 @@ func (c *CouchDB) generateFields(prefix string, obj metaData) map[string]interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("couchdb", func() telegraf.Input {
|
inputs.Add("couchdb", func() plugins.Input {
|
||||||
return &CouchDB{}
|
return &CouchDB{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,5 +316,5 @@ func TestBasic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
require.NoError(t, acc.GatherError(plugin.Gather))
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ var ErrProtocolError = errors.New("disque protocol error")
|
|||||||
|
|
||||||
// Reads stats from all configured servers accumulates stats.
|
// Reads stats from all configured servers accumulates stats.
|
||||||
// Returns one of the errors encountered while gather stats (if any).
|
// Returns one of the errors encountered while gather stats (if any).
|
||||||
func (g *Disque) Gather(acc telegraf.Accumulator) error {
|
func (g *Disque) Gather(acc plugins.Accumulator) error {
|
||||||
if len(g.Servers) == 0 {
|
if len(g.Servers) == 0 {
|
||||||
url := &url.URL{
|
url := &url.URL{
|
||||||
Host: ":7711",
|
Host: ":7711",
|
||||||
@@ -75,11 +75,12 @@ func (g *Disque) Gather(acc telegraf.Accumulator) error {
|
|||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
var outerr error
|
||||||
|
|
||||||
for _, serv := range g.Servers {
|
for _, serv := range g.Servers {
|
||||||
u, err := url.Parse(serv)
|
u, err := url.Parse(serv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(fmt.Errorf("Unable to parse to address '%s': %s", serv, err))
|
return fmt.Errorf("Unable to parse to address '%s': %s", serv, err)
|
||||||
continue
|
|
||||||
} else if u.Scheme == "" {
|
} else if u.Scheme == "" {
|
||||||
// fallback to simple string based address (i.e. "10.0.0.1:10000")
|
// fallback to simple string based address (i.e. "10.0.0.1:10000")
|
||||||
u.Scheme = "tcp"
|
u.Scheme = "tcp"
|
||||||
@@ -89,18 +90,18 @@ func (g *Disque) Gather(acc telegraf.Accumulator) error {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(serv string) {
|
go func(serv string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
acc.AddError(g.gatherServer(u, acc))
|
outerr = g.gatherServer(u, acc)
|
||||||
}(serv)
|
}(serv)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
return nil
|
return outerr
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPort = "7711"
|
const defaultPort = "7711"
|
||||||
|
|
||||||
func (g *Disque) gatherServer(addr *url.URL, acc telegraf.Accumulator) error {
|
func (g *Disque) gatherServer(addr *url.URL, acc plugins.Accumulator) error {
|
||||||
if g.c == nil {
|
if g.c == nil {
|
||||||
|
|
||||||
_, _, err := net.SplitHostPort(addr.Host)
|
_, _, err := net.SplitHostPort(addr.Host)
|
||||||
@@ -203,7 +204,7 @@ func (g *Disque) gatherServer(addr *url.URL, acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("disque", func() telegraf.Input {
|
inputs.Add("disque", func() plugins.Input {
|
||||||
return &Disque{}
|
return &Disque{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func TestDisqueGeneratesMetrics(t *testing.T) {
|
|||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err = acc.GatherError(r.Gather)
|
err = r.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
@@ -117,7 +117,7 @@ func TestDisqueCanPullStatsFromMultipleServers(t *testing.T) {
|
|||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err = acc.GatherError(r.Gather)
|
err = r.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
# DMCache Input Plugin
|
|
||||||
|
|
||||||
This plugin provide a native collection for dmsetup based statistics for dm-cache.
|
|
||||||
|
|
||||||
This plugin requires sudo, that is why you should setup and be sure that the telegraf is able to execute sudo without a password.
|
|
||||||
|
|
||||||
`sudo /sbin/dmsetup status --target cache` is the full command that telegraf will run for debugging purposes.
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[inputs.dmcache]]
|
|
||||||
## Whether to report per-device stats or not
|
|
||||||
per_device = true
|
|
||||||
```
|
|
||||||
|
|
||||||
### Measurements & Fields:
|
|
||||||
|
|
||||||
- dmcache
|
|
||||||
- length
|
|
||||||
- target
|
|
||||||
- metadata_blocksize
|
|
||||||
- metadata_used
|
|
||||||
- metadata_total
|
|
||||||
- cache_blocksize
|
|
||||||
- cache_used
|
|
||||||
- cache_total
|
|
||||||
- read_hits
|
|
||||||
- read_misses
|
|
||||||
- write_hits
|
|
||||||
- write_misses
|
|
||||||
- demotions
|
|
||||||
- promotions
|
|
||||||
- dirty
|
|
||||||
|
|
||||||
### Tags:
|
|
||||||
|
|
||||||
- All measurements have the following tags:
|
|
||||||
- device
|
|
||||||
|
|
||||||
### Example Output:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ./telegraf --test --config /etc/telegraf/telegraf.conf --input-filter dmcache
|
|
||||||
* Plugin: inputs.dmcache, Collection 1
|
|
||||||
> dmcache,device=example cache_blocksize=0i,read_hits=995134034411520i,read_misses=916807089127424i,write_hits=195107267543040i,metadata_used=12861440i,write_misses=563725346013184i,promotions=3265223720960i,dirty=0i,metadata_blocksize=0i,cache_used=1099511627776ii,cache_total=0i,length=0i,metadata_total=1073741824i,demotions=3265223720960i 1491482035000000000
|
|
||||||
```
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package dmcache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DMCache struct {
|
|
||||||
PerDevice bool `toml:"per_device"`
|
|
||||||
getCurrentStatus func() ([]string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var sampleConfig = `
|
|
||||||
## Whether to report per-device stats or not
|
|
||||||
per_device = true
|
|
||||||
`
|
|
||||||
|
|
||||||
func (c *DMCache) SampleConfig() string {
|
|
||||||
return sampleConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *DMCache) Description() string {
|
|
||||||
return "Provide a native collection for dmsetup based statistics for dm-cache"
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
inputs.Add("dmcache", func() telegraf.Input {
|
|
||||||
return &DMCache{
|
|
||||||
PerDevice: true,
|
|
||||||
getCurrentStatus: dmSetupStatus,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
package dmcache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
)
|
|
||||||
|
|
||||||
const metricName = "dmcache"
|
|
||||||
|
|
||||||
type cacheStatus struct {
|
|
||||||
device string
|
|
||||||
length int
|
|
||||||
target string
|
|
||||||
metadataBlocksize int
|
|
||||||
metadataUsed int
|
|
||||||
metadataTotal int
|
|
||||||
cacheBlocksize int
|
|
||||||
cacheUsed int
|
|
||||||
cacheTotal int
|
|
||||||
readHits int
|
|
||||||
readMisses int
|
|
||||||
writeHits int
|
|
||||||
writeMisses int
|
|
||||||
demotions int
|
|
||||||
promotions int
|
|
||||||
dirty int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *DMCache) Gather(acc telegraf.Accumulator) error {
|
|
||||||
outputLines, err := c.getCurrentStatus()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
totalStatus := cacheStatus{}
|
|
||||||
|
|
||||||
for _, s := range outputLines {
|
|
||||||
status, err := parseDMSetupStatus(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.PerDevice {
|
|
||||||
tags := map[string]string{"device": status.device}
|
|
||||||
acc.AddFields(metricName, toFields(status), tags)
|
|
||||||
}
|
|
||||||
aggregateStats(&totalStatus, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AddFields(metricName, toFields(totalStatus), map[string]string{"device": "all"})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDMSetupStatus(line string) (cacheStatus, error) {
|
|
||||||
var err error
|
|
||||||
parseError := errors.New("Output from dmsetup could not be parsed")
|
|
||||||
status := cacheStatus{}
|
|
||||||
values := strings.Fields(line)
|
|
||||||
if len(values) < 15 {
|
|
||||||
return cacheStatus{}, parseError
|
|
||||||
}
|
|
||||||
|
|
||||||
status.device = strings.TrimRight(values[0], ":")
|
|
||||||
status.length, err = strconv.Atoi(values[2])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.target = values[3]
|
|
||||||
status.metadataBlocksize, err = strconv.Atoi(values[4])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
metadata := strings.Split(values[5], "/")
|
|
||||||
if len(metadata) != 2 {
|
|
||||||
return cacheStatus{}, parseError
|
|
||||||
}
|
|
||||||
status.metadataUsed, err = strconv.Atoi(metadata[0])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.metadataTotal, err = strconv.Atoi(metadata[1])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.cacheBlocksize, err = strconv.Atoi(values[6])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
cache := strings.Split(values[7], "/")
|
|
||||||
if len(cache) != 2 {
|
|
||||||
return cacheStatus{}, parseError
|
|
||||||
}
|
|
||||||
status.cacheUsed, err = strconv.Atoi(cache[0])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.cacheTotal, err = strconv.Atoi(cache[1])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.readHits, err = strconv.Atoi(values[8])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.readMisses, err = strconv.Atoi(values[9])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.writeHits, err = strconv.Atoi(values[10])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.writeMisses, err = strconv.Atoi(values[11])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.demotions, err = strconv.Atoi(values[12])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.promotions, err = strconv.Atoi(values[13])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
status.dirty, err = strconv.Atoi(values[14])
|
|
||||||
if err != nil {
|
|
||||||
return cacheStatus{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func aggregateStats(totalStatus *cacheStatus, status cacheStatus) {
|
|
||||||
totalStatus.length += status.length
|
|
||||||
totalStatus.metadataBlocksize += status.metadataBlocksize
|
|
||||||
totalStatus.metadataUsed += status.metadataUsed
|
|
||||||
totalStatus.metadataTotal += status.metadataTotal
|
|
||||||
totalStatus.cacheBlocksize += status.cacheBlocksize
|
|
||||||
totalStatus.cacheUsed += status.cacheUsed
|
|
||||||
totalStatus.cacheTotal += status.cacheTotal
|
|
||||||
totalStatus.readHits += status.readHits
|
|
||||||
totalStatus.readMisses += status.readMisses
|
|
||||||
totalStatus.writeHits += status.writeHits
|
|
||||||
totalStatus.writeMisses += status.writeMisses
|
|
||||||
totalStatus.demotions += status.demotions
|
|
||||||
totalStatus.promotions += status.promotions
|
|
||||||
totalStatus.dirty += status.dirty
|
|
||||||
}
|
|
||||||
|
|
||||||
func toFields(status cacheStatus) map[string]interface{} {
|
|
||||||
fields := make(map[string]interface{})
|
|
||||||
fields["length"] = status.length
|
|
||||||
fields["metadata_blocksize"] = status.metadataBlocksize
|
|
||||||
fields["metadata_used"] = status.metadataUsed
|
|
||||||
fields["metadata_total"] = status.metadataTotal
|
|
||||||
fields["cache_blocksize"] = status.cacheBlocksize
|
|
||||||
fields["cache_used"] = status.cacheUsed
|
|
||||||
fields["cache_total"] = status.cacheTotal
|
|
||||||
fields["read_hits"] = status.readHits
|
|
||||||
fields["read_misses"] = status.readMisses
|
|
||||||
fields["write_hits"] = status.writeHits
|
|
||||||
fields["write_misses"] = status.writeMisses
|
|
||||||
fields["demotions"] = status.demotions
|
|
||||||
fields["promotions"] = status.promotions
|
|
||||||
fields["dirty"] = status.dirty
|
|
||||||
return fields
|
|
||||||
}
|
|
||||||
|
|
||||||
func dmSetupStatus() ([]string, error) {
|
|
||||||
out, err := exec.Command("/bin/sh", "-c", "sudo /sbin/dmsetup status --target cache").Output()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if string(out) == "No devices found\n" {
|
|
||||||
return []string{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
outString := strings.TrimRight(string(out), "\n")
|
|
||||||
status := strings.Split(outString, "\n")
|
|
||||||
|
|
||||||
return status, nil
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// +build !linux
|
|
||||||
|
|
||||||
package dmcache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *DMCache) Gather(acc telegraf.Accumulator) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dmSetupStatus() ([]string, error) {
|
|
||||||
return []string{}, nil
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
package dmcache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
measurement = "dmcache"
|
|
||||||
badFormatOutput = []string{"cs-1: 0 4883791872 cache 8 1018/1501122 512 7/464962 139 352643 "}
|
|
||||||
good2DevicesFormatOutput = []string{
|
|
||||||
"cs-1: 0 4883791872 cache 8 1018/1501122 512 7/464962 139 352643 15 46 0 7 0 1 writeback 2 migration_threshold 2048 mq 10 random_threshold 4 sequential_threshold 512 discard_promote_adjustment 1 read_promote_adjustment 4 write_promote_adjustment 8",
|
|
||||||
"cs-2: 0 4294967296 cache 8 72352/1310720 128 26/24327168 2409 286 265 524682 0 0 0 1 writethrough 2 migration_threshold 2048 mq 10 random_threshold 4 sequential_threshold 512 discard_promote_adjustment 1 read_promote_adjustment 4 write_promote_adjustment 8",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPerDeviceGoodOutput(t *testing.T) {
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
var plugin = &DMCache{
|
|
||||||
PerDevice: true,
|
|
||||||
getCurrentStatus: func() ([]string, error) {
|
|
||||||
return good2DevicesFormatOutput, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := plugin.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tags1 := map[string]string{
|
|
||||||
"device": "cs-1",
|
|
||||||
}
|
|
||||||
fields1 := map[string]interface{}{
|
|
||||||
"length": 4883791872,
|
|
||||||
"metadata_blocksize": 8,
|
|
||||||
"metadata_used": 1018,
|
|
||||||
"metadata_total": 1501122,
|
|
||||||
"cache_blocksize": 512,
|
|
||||||
"cache_used": 7,
|
|
||||||
"cache_total": 464962,
|
|
||||||
"read_hits": 139,
|
|
||||||
"read_misses": 352643,
|
|
||||||
"write_hits": 15,
|
|
||||||
"write_misses": 46,
|
|
||||||
"demotions": 0,
|
|
||||||
"promotions": 7,
|
|
||||||
"dirty": 0,
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, measurement, fields1, tags1)
|
|
||||||
|
|
||||||
tags2 := map[string]string{
|
|
||||||
"device": "cs-2",
|
|
||||||
}
|
|
||||||
fields2 := map[string]interface{}{
|
|
||||||
"length": 4294967296,
|
|
||||||
"metadata_blocksize": 8,
|
|
||||||
"metadata_used": 72352,
|
|
||||||
"metadata_total": 1310720,
|
|
||||||
"cache_blocksize": 128,
|
|
||||||
"cache_used": 26,
|
|
||||||
"cache_total": 24327168,
|
|
||||||
"read_hits": 2409,
|
|
||||||
"read_misses": 286,
|
|
||||||
"write_hits": 265,
|
|
||||||
"write_misses": 524682,
|
|
||||||
"demotions": 0,
|
|
||||||
"promotions": 0,
|
|
||||||
"dirty": 0,
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, measurement, fields2, tags2)
|
|
||||||
|
|
||||||
tags3 := map[string]string{
|
|
||||||
"device": "all",
|
|
||||||
}
|
|
||||||
|
|
||||||
fields3 := map[string]interface{}{
|
|
||||||
"length": 9178759168,
|
|
||||||
"metadata_blocksize": 16,
|
|
||||||
"metadata_used": 73370,
|
|
||||||
"metadata_total": 2811842,
|
|
||||||
"cache_blocksize": 640,
|
|
||||||
"cache_used": 33,
|
|
||||||
"cache_total": 24792130,
|
|
||||||
"read_hits": 2548,
|
|
||||||
"read_misses": 352929,
|
|
||||||
"write_hits": 280,
|
|
||||||
"write_misses": 524728,
|
|
||||||
"demotions": 0,
|
|
||||||
"promotions": 7,
|
|
||||||
"dirty": 0,
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, measurement, fields3, tags3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotPerDeviceGoodOutput(t *testing.T) {
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
var plugin = &DMCache{
|
|
||||||
PerDevice: false,
|
|
||||||
getCurrentStatus: func() ([]string, error) {
|
|
||||||
return good2DevicesFormatOutput, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := plugin.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tags := map[string]string{
|
|
||||||
"device": "all",
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"length": 9178759168,
|
|
||||||
"metadata_blocksize": 16,
|
|
||||||
"metadata_used": 73370,
|
|
||||||
"metadata_total": 2811842,
|
|
||||||
"cache_blocksize": 640,
|
|
||||||
"cache_used": 33,
|
|
||||||
"cache_total": 24792130,
|
|
||||||
"read_hits": 2548,
|
|
||||||
"read_misses": 352929,
|
|
||||||
"write_hits": 280,
|
|
||||||
"write_misses": 524728,
|
|
||||||
"demotions": 0,
|
|
||||||
"promotions": 7,
|
|
||||||
"dirty": 0,
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, measurement, fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoDevicesOutput(t *testing.T) {
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
var plugin = &DMCache{
|
|
||||||
PerDevice: true,
|
|
||||||
getCurrentStatus: func() ([]string, error) {
|
|
||||||
return []string{}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := plugin.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorDuringGettingStatus(t *testing.T) {
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
var plugin = &DMCache{
|
|
||||||
PerDevice: true,
|
|
||||||
getCurrentStatus: func() ([]string, error) {
|
|
||||||
return nil, errors.New("dmsetup doesn't exist")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := plugin.Gather(&acc)
|
|
||||||
require.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBadFormatOfStatus(t *testing.T) {
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
var plugin = &DMCache{
|
|
||||||
PerDevice: true,
|
|
||||||
getCurrentStatus: func() ([]string, error) {
|
|
||||||
return badFormatOutput, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := plugin.Gather(&acc)
|
|
||||||
require.Error(t, err)
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
|
"github.com/influxdata/telegraf/internal/errchan"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,13 +55,14 @@ func (d *DnsQuery) SampleConfig() string {
|
|||||||
func (d *DnsQuery) Description() string {
|
func (d *DnsQuery) Description() string {
|
||||||
return "Query given DNS server and gives statistics"
|
return "Query given DNS server and gives statistics"
|
||||||
}
|
}
|
||||||
func (d *DnsQuery) Gather(acc telegraf.Accumulator) error {
|
func (d *DnsQuery) Gather(acc plugins.Accumulator) error {
|
||||||
d.setDefaultValues()
|
d.setDefaultValues()
|
||||||
|
|
||||||
|
errChan := errchan.New(len(d.Domains) * len(d.Servers))
|
||||||
for _, domain := range d.Domains {
|
for _, domain := range d.Domains {
|
||||||
for _, server := range d.Servers {
|
for _, server := range d.Servers {
|
||||||
dnsQueryTime, err := d.getDnsQueryTime(domain, server)
|
dnsQueryTime, err := d.getDnsQueryTime(domain, server)
|
||||||
acc.AddError(err)
|
errChan.C <- err
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
"server": server,
|
"server": server,
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
@@ -72,7 +74,7 @@ func (d *DnsQuery) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return errChan.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DnsQuery) setDefaultValues() {
|
func (d *DnsQuery) setDefaultValues() {
|
||||||
@@ -154,7 +156,7 @@ func (d *DnsQuery) parseRecordType() (uint16, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("dns_query", func() telegraf.Input {
|
inputs.Add("dns_query", func() plugins.Input {
|
||||||
return &DnsQuery{}
|
return &DnsQuery{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestGathering(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := acc.GatherError(dnsConfig.Gather)
|
err := dnsConfig.Gather(&acc)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
metric, ok := acc.Get("dns_query")
|
metric, ok := acc.Get("dns_query")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
@@ -44,7 +44,7 @@ func TestGatheringMxRecord(t *testing.T) {
|
|||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
dnsConfig.RecordType = "MX"
|
dnsConfig.RecordType = "MX"
|
||||||
|
|
||||||
err := acc.GatherError(dnsConfig.Gather)
|
err := dnsConfig.Gather(&acc)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
metric, ok := acc.Get("dns_query")
|
metric, ok := acc.Get("dns_query")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
@@ -70,7 +70,7 @@ func TestGatheringRootDomain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fields := map[string]interface{}{}
|
fields := map[string]interface{}{}
|
||||||
|
|
||||||
err := acc.GatherError(dnsConfig.Gather)
|
err := dnsConfig.Gather(&acc)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
metric, ok := acc.Get("dns_query")
|
metric, ok := acc.Get("dns_query")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
@@ -96,7 +96,7 @@ func TestMetricContainsServerAndDomainAndRecordTypeTags(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fields := map[string]interface{}{}
|
fields := map[string]interface{}{}
|
||||||
|
|
||||||
err := acc.GatherError(dnsConfig.Gather)
|
err := dnsConfig.Gather(&acc)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
metric, ok := acc.Get("dns_query")
|
metric, ok := acc.Get("dns_query")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
@@ -121,7 +121,7 @@ func TestGatheringTimeout(t *testing.T) {
|
|||||||
|
|
||||||
channel := make(chan error, 1)
|
channel := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
channel <- acc.GatherError(dnsConfig.Gather)
|
channel <- dnsConfig.Gather(&acc)
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
case res := <-channel:
|
case res := <-channel:
|
||||||
|
|||||||
@@ -16,26 +16,12 @@ for the stat structure can be found
|
|||||||
```
|
```
|
||||||
# Read metrics about docker containers
|
# Read metrics about docker containers
|
||||||
[[inputs.docker]]
|
[[inputs.docker]]
|
||||||
## Docker Endpoint
|
# Docker Endpoint
|
||||||
## To use TCP, set endpoint = "tcp://[ip]:[port]"
|
# To use TCP, set endpoint = "tcp://[ip]:[port]"
|
||||||
## To use environment variables (ie, docker-machine), set endpoint = "ENV"
|
# To use environment variables (ie, docker-machine), set endpoint = "ENV"
|
||||||
endpoint = "unix:///var/run/docker.sock"
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
## Only collect metrics for these containers, collect all if empty
|
# Only collect metrics for these containers, collect all if empty
|
||||||
container_names = []
|
container_names = []
|
||||||
## Timeout for docker list, info, and stats commands
|
|
||||||
timeout = "5s"
|
|
||||||
|
|
||||||
## Whether to report for each container per-device blkio (8:0, 8:1...) and
|
|
||||||
## network (eth0, eth1, ...) stats or not
|
|
||||||
perdevice = true
|
|
||||||
## Whether to report for each container total blkio and network stats or not
|
|
||||||
total = false
|
|
||||||
|
|
||||||
## docker labels to include and exclude as tags. Globs accepted.
|
|
||||||
## Note that an empty array for both will include all labels as tags
|
|
||||||
docker_label_include = []
|
|
||||||
docker_label_exclude = []
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Measurements & Fields:
|
### Measurements & Fields:
|
||||||
@@ -136,32 +122,30 @@ based on the availability of per-cpu stats on your system.
|
|||||||
|
|
||||||
|
|
||||||
### Tags:
|
### Tags:
|
||||||
#### Docker Engine tags
|
|
||||||
- docker (memory_total)
|
- docker (memory_total)
|
||||||
- unit=bytes
|
- unit=bytes
|
||||||
- engine_host
|
|
||||||
- docker (pool_blocksize)
|
- docker (pool_blocksize)
|
||||||
- unit=bytes
|
- unit=bytes
|
||||||
- engine_host
|
|
||||||
- docker_data
|
- docker_data
|
||||||
- unit=bytes
|
- unit=bytes
|
||||||
- engine_host
|
|
||||||
- docker_metadata
|
- docker_metadata
|
||||||
- unit=bytes
|
- unit=bytes
|
||||||
- engine_host
|
|
||||||
|
|
||||||
#### Docker Container tags
|
- docker_container_mem specific:
|
||||||
- Tags on all containers:
|
|
||||||
- engine_host
|
|
||||||
- container_image
|
- container_image
|
||||||
- container_name
|
- container_name
|
||||||
- container_version
|
|
||||||
- docker_container_mem specific:
|
|
||||||
- docker_container_cpu specific:
|
- docker_container_cpu specific:
|
||||||
|
- container_image
|
||||||
|
- container_name
|
||||||
- cpu
|
- cpu
|
||||||
- docker_container_net specific:
|
- docker_container_net specific:
|
||||||
|
- container_image
|
||||||
|
- container_name
|
||||||
- network
|
- network
|
||||||
- docker_container_blkio specific:
|
- docker_container_blkio specific:
|
||||||
|
- container_image
|
||||||
|
- container_name
|
||||||
- device
|
- device
|
||||||
|
|
||||||
### Example Output:
|
### Example Output:
|
||||||
|
|||||||
@@ -1,29 +1,25 @@
|
|||||||
package docker
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"golang.org/x/net/context"
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/docker/engine-api/client"
|
||||||
"github.com/influxdata/telegraf/filter"
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DockerLabelFilter struct {
|
|
||||||
labelInclude filter.Filter
|
|
||||||
labelExclude filter.Filter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Docker object
|
// Docker object
|
||||||
type Docker struct {
|
type Docker struct {
|
||||||
Endpoint string
|
Endpoint string
|
||||||
@@ -31,52 +27,16 @@ type Docker struct {
|
|||||||
Timeout internal.Duration
|
Timeout internal.Duration
|
||||||
PerDevice bool `toml:"perdevice"`
|
PerDevice bool `toml:"perdevice"`
|
||||||
Total bool `toml:"total"`
|
Total bool `toml:"total"`
|
||||||
LabelInclude []string `toml:"docker_label_include"`
|
|
||||||
LabelExclude []string `toml:"docker_label_exclude"`
|
|
||||||
|
|
||||||
LabelFilter DockerLabelFilter
|
client DockerClient
|
||||||
|
|
||||||
client *client.Client
|
|
||||||
engine_host string
|
engine_host string
|
||||||
|
|
||||||
testing bool
|
|
||||||
labelFiltersCreated bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// infoWrapper wraps client.Client.List for testing.
|
// DockerClient interface, useful for testing
|
||||||
func infoWrapper(c *client.Client, ctx context.Context) (types.Info, error) {
|
type DockerClient interface {
|
||||||
if c != nil {
|
Info(ctx context.Context) (types.Info, error)
|
||||||
return c.Info(ctx)
|
ContainerList(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error)
|
||||||
}
|
ContainerStats(ctx context.Context, containerID string, stream bool) (io.ReadCloser, error)
|
||||||
fc := FakeDockerClient{}
|
|
||||||
return fc.Info(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// listWrapper wraps client.Client.ContainerList for testing.
|
|
||||||
func listWrapper(
|
|
||||||
c *client.Client,
|
|
||||||
ctx context.Context,
|
|
||||||
options types.ContainerListOptions,
|
|
||||||
) ([]types.Container, error) {
|
|
||||||
if c != nil {
|
|
||||||
return c.ContainerList(ctx, options)
|
|
||||||
}
|
|
||||||
fc := FakeDockerClient{}
|
|
||||||
return fc.ContainerList(ctx, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// statsWrapper wraps client.Client.ContainerStats for testing.
|
|
||||||
func statsWrapper(
|
|
||||||
c *client.Client,
|
|
||||||
ctx context.Context,
|
|
||||||
containerID string,
|
|
||||||
stream bool,
|
|
||||||
) (types.ContainerStats, error) {
|
|
||||||
if c != nil {
|
|
||||||
return c.ContainerStats(ctx, containerID, stream)
|
|
||||||
}
|
|
||||||
fc := FakeDockerClient{}
|
|
||||||
return fc.ContainerStats(ctx, containerID, stream)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// KB, MB, GB, TB, PB...human friendly
|
// KB, MB, GB, TB, PB...human friendly
|
||||||
@@ -108,10 +68,6 @@ var sampleConfig = `
|
|||||||
## Whether to report for each container total blkio and network stats or not
|
## Whether to report for each container total blkio and network stats or not
|
||||||
total = false
|
total = false
|
||||||
|
|
||||||
## docker labels to include and exclude as tags. Globs accepted.
|
|
||||||
## Note that an empty array for both will include all labels as tags
|
|
||||||
docker_label_include = []
|
|
||||||
docker_label_exclude = []
|
|
||||||
`
|
`
|
||||||
|
|
||||||
// Description returns input description
|
// Description returns input description
|
||||||
@@ -123,8 +79,8 @@ func (d *Docker) Description() string {
|
|||||||
func (d *Docker) SampleConfig() string { return sampleConfig }
|
func (d *Docker) SampleConfig() string { return sampleConfig }
|
||||||
|
|
||||||
// Gather starts stats collection
|
// Gather starts stats collection
|
||||||
func (d *Docker) Gather(acc telegraf.Accumulator) error {
|
func (d *Docker) Gather(acc plugins.Accumulator) error {
|
||||||
if d.client == nil && !d.testing {
|
if d.client == nil {
|
||||||
var c *client.Client
|
var c *client.Client
|
||||||
var err error
|
var err error
|
||||||
defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
|
defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
|
||||||
@@ -146,26 +102,18 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
d.client = c
|
d.client = c
|
||||||
}
|
}
|
||||||
// Create label filters if not already created
|
|
||||||
if !d.labelFiltersCreated {
|
|
||||||
err := d.createLabelFilters()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.labelFiltersCreated = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get daemon info
|
// Get daemon info
|
||||||
err := d.gatherInfo(acc)
|
err := d.gatherInfo(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(err)
|
fmt.Println(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// List containers
|
// List containers
|
||||||
opts := types.ContainerListOptions{}
|
opts := types.ContainerListOptions{}
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
|
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
containers, err := listWrapper(d.client, ctx, opts)
|
containers, err := d.client.ContainerList(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -178,8 +126,8 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
err := d.gatherContainer(c, acc)
|
err := d.gatherContainer(c, acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(fmt.Errorf("E! Error gathering container %s stats: %s\n",
|
log.Printf("E! Error gathering container %s stats: %s\n",
|
||||||
c.Names, err.Error()))
|
c.Names, err.Error())
|
||||||
}
|
}
|
||||||
}(container)
|
}(container)
|
||||||
}
|
}
|
||||||
@@ -188,7 +136,7 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
|
func (d *Docker) gatherInfo(acc plugins.Accumulator) error {
|
||||||
// Init vars
|
// Init vars
|
||||||
dataFields := make(map[string]interface{})
|
dataFields := make(map[string]interface{})
|
||||||
metadataFields := make(map[string]interface{})
|
metadataFields := make(map[string]interface{})
|
||||||
@@ -196,7 +144,7 @@ func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
|
|||||||
// Get info from docker daemon
|
// Get info from docker daemon
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
|
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
info, err := infoWrapper(d.client, ctx)
|
info, err := d.client.Info(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -263,7 +211,7 @@ func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
|
|||||||
|
|
||||||
func (d *Docker) gatherContainer(
|
func (d *Docker) gatherContainer(
|
||||||
container types.Container,
|
container types.Container,
|
||||||
acc telegraf.Accumulator,
|
acc plugins.Accumulator,
|
||||||
) error {
|
) error {
|
||||||
var v *types.StatsJSON
|
var v *types.StatsJSON
|
||||||
// Parse container name
|
// Parse container name
|
||||||
@@ -299,12 +247,12 @@ func (d *Docker) gatherContainer(
|
|||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
|
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
r, err := statsWrapper(d.client, ctx, container.ID, false)
|
r, err := d.client.ContainerStats(ctx, container.ID, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error getting docker stats: %s", err.Error())
|
return fmt.Errorf("Error getting docker stats: %s", err.Error())
|
||||||
}
|
}
|
||||||
defer r.Body.Close()
|
defer r.Close()
|
||||||
dec := json.NewDecoder(r.Body)
|
dec := json.NewDecoder(r)
|
||||||
if err = dec.Decode(&v); err != nil {
|
if err = dec.Decode(&v); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil
|
return nil
|
||||||
@@ -314,12 +262,8 @@ func (d *Docker) gatherContainer(
|
|||||||
|
|
||||||
// Add labels to tags
|
// Add labels to tags
|
||||||
for k, label := range container.Labels {
|
for k, label := range container.Labels {
|
||||||
if len(d.LabelInclude) == 0 || d.LabelFilter.labelInclude.Match(k) {
|
|
||||||
if len(d.LabelExclude) == 0 || !d.LabelFilter.labelExclude.Match(k) {
|
|
||||||
tags[k] = label
|
tags[k] = label
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gatherContainerStats(v, acc, tags, container.ID, d.PerDevice, d.Total)
|
gatherContainerStats(v, acc, tags, container.ID, d.PerDevice, d.Total)
|
||||||
|
|
||||||
@@ -328,7 +272,7 @@ func (d *Docker) gatherContainer(
|
|||||||
|
|
||||||
func gatherContainerStats(
|
func gatherContainerStats(
|
||||||
stat *types.StatsJSON,
|
stat *types.StatsJSON,
|
||||||
acc telegraf.Accumulator,
|
acc plugins.Accumulator,
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
id string,
|
id string,
|
||||||
perDevice bool,
|
perDevice bool,
|
||||||
@@ -478,7 +422,7 @@ func calculateCPUPercent(stat *types.StatsJSON) float64 {
|
|||||||
|
|
||||||
func gatherBlockIOMetrics(
|
func gatherBlockIOMetrics(
|
||||||
stat *types.StatsJSON,
|
stat *types.StatsJSON,
|
||||||
acc telegraf.Accumulator,
|
acc plugins.Accumulator,
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
now time.Time,
|
now time.Time,
|
||||||
id string,
|
id string,
|
||||||
@@ -624,32 +568,11 @@ func parseSize(sizeStr string) (int64, error) {
|
|||||||
return int64(size), nil
|
return int64(size), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Docker) createLabelFilters() error {
|
|
||||||
if len(d.LabelInclude) != 0 && d.LabelFilter.labelInclude == nil {
|
|
||||||
var err error
|
|
||||||
d.LabelFilter.labelInclude, err = filter.Compile(d.LabelInclude)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(d.LabelExclude) != 0 && d.LabelFilter.labelExclude == nil {
|
|
||||||
var err error
|
|
||||||
d.LabelFilter.labelExclude, err = filter.Compile(d.LabelExclude)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("docker", func() telegraf.Input {
|
inputs.Add("docker", func() plugins.Input {
|
||||||
return &Docker{
|
return &Docker{
|
||||||
PerDevice: true,
|
PerDevice: true,
|
||||||
Timeout: internal.Duration{Duration: time.Second * 5},
|
Timeout: internal.Duration{Duration: time.Second * 5},
|
||||||
labelFiltersCreated: false,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
package docker
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/registry"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -244,65 +250,147 @@ func testStats() *types.StatsJSON {
|
|||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
var gatherLabelsTests = []struct {
|
type FakeDockerClient struct {
|
||||||
include []string
|
|
||||||
exclude []string
|
|
||||||
expected []string
|
|
||||||
notexpected []string
|
|
||||||
}{
|
|
||||||
{[]string{}, []string{}, []string{"label1", "label2"}, []string{}},
|
|
||||||
{[]string{"*"}, []string{}, []string{"label1", "label2"}, []string{}},
|
|
||||||
{[]string{"lab*"}, []string{}, []string{"label1", "label2"}, []string{}},
|
|
||||||
{[]string{"label1"}, []string{}, []string{"label1"}, []string{"label2"}},
|
|
||||||
{[]string{"label1*"}, []string{}, []string{"label1"}, []string{"label2"}},
|
|
||||||
{[]string{}, []string{"*"}, []string{}, []string{"label1", "label2"}},
|
|
||||||
{[]string{}, []string{"lab*"}, []string{}, []string{"label1", "label2"}},
|
|
||||||
{[]string{}, []string{"label1"}, []string{"label2"}, []string{"label1"}},
|
|
||||||
{[]string{"*"}, []string{"*"}, []string{}, []string{"label1", "label2"}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDockerGatherLabels(t *testing.T) {
|
func (d FakeDockerClient) Info(ctx context.Context) (types.Info, error) {
|
||||||
for _, tt := range gatherLabelsTests {
|
env := types.Info{
|
||||||
var acc testutil.Accumulator
|
Containers: 108,
|
||||||
d := Docker{
|
ContainersRunning: 98,
|
||||||
client: nil,
|
ContainersStopped: 6,
|
||||||
testing: true,
|
ContainersPaused: 3,
|
||||||
|
OomKillDisable: false,
|
||||||
|
SystemTime: "2016-02-24T00:55:09.15073105-05:00",
|
||||||
|
NEventsListener: 0,
|
||||||
|
ID: "5WQQ:TFWR:FDNG:OKQ3:37Y4:FJWG:QIKK:623T:R3ME:QTKB:A7F7:OLHD",
|
||||||
|
Debug: false,
|
||||||
|
LoggingDriver: "json-file",
|
||||||
|
KernelVersion: "4.3.0-1-amd64",
|
||||||
|
IndexServerAddress: "https://index.docker.io/v1/",
|
||||||
|
MemTotal: 3840757760,
|
||||||
|
Images: 199,
|
||||||
|
CPUCfsQuota: true,
|
||||||
|
Name: "absol",
|
||||||
|
SwapLimit: false,
|
||||||
|
IPv4Forwarding: true,
|
||||||
|
ExecutionDriver: "native-0.2",
|
||||||
|
ExperimentalBuild: false,
|
||||||
|
CPUCfsPeriod: true,
|
||||||
|
RegistryConfig: ®istry.ServiceConfig{
|
||||||
|
IndexConfigs: map[string]*registry.IndexInfo{
|
||||||
|
"docker.io": {
|
||||||
|
Name: "docker.io",
|
||||||
|
Mirrors: []string{},
|
||||||
|
Official: true,
|
||||||
|
Secure: true,
|
||||||
|
},
|
||||||
|
}, InsecureRegistryCIDRs: []*registry.NetIPNet{{IP: []byte{127, 0, 0, 0}, Mask: []byte{255, 0, 0, 0}}}, Mirrors: []string{}},
|
||||||
|
OperatingSystem: "Linux Mint LMDE (containerized)",
|
||||||
|
BridgeNfIptables: true,
|
||||||
|
HTTPSProxy: "",
|
||||||
|
Labels: []string{},
|
||||||
|
MemoryLimit: false,
|
||||||
|
DriverStatus: [][2]string{{"Pool Name", "docker-8:1-1182287-pool"}, {"Pool Blocksize", "65.54 kB"}, {"Backing Filesystem", "extfs"}, {"Data file", "/dev/loop0"}, {"Metadata file", "/dev/loop1"}, {"Data Space Used", "17.3 GB"}, {"Data Space Total", "107.4 GB"}, {"Data Space Available", "36.53 GB"}, {"Metadata Space Used", "20.97 MB"}, {"Metadata Space Total", "2.147 GB"}, {"Metadata Space Available", "2.127 GB"}, {"Udev Sync Supported", "true"}, {"Deferred Removal Enabled", "false"}, {"Data loop file", "/var/lib/docker/devicemapper/devicemapper/data"}, {"Metadata loop file", "/var/lib/docker/devicemapper/devicemapper/metadata"}, {"Library Version", "1.02.115 (2016-01-25)"}},
|
||||||
|
NFd: 19,
|
||||||
|
HTTPProxy: "",
|
||||||
|
Driver: "devicemapper",
|
||||||
|
NGoroutines: 39,
|
||||||
|
NCPU: 4,
|
||||||
|
DockerRootDir: "/var/lib/docker",
|
||||||
|
NoProxy: "",
|
||||||
|
BridgeNfIP6tables: true,
|
||||||
|
}
|
||||||
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, label := range tt.include {
|
func (d FakeDockerClient) ContainerList(octx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
|
||||||
d.LabelInclude = append(d.LabelInclude, label)
|
container1 := types.Container{
|
||||||
|
ID: "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
||||||
|
Names: []string{"/etcd"},
|
||||||
|
Image: "quay.io/coreos/etcd:v2.2.2",
|
||||||
|
Command: "/etcd -name etcd0 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379",
|
||||||
|
Created: 1455941930,
|
||||||
|
Status: "Up 4 hours",
|
||||||
|
Ports: []types.Port{
|
||||||
|
types.Port{
|
||||||
|
PrivatePort: 7001,
|
||||||
|
PublicPort: 0,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
types.Port{
|
||||||
|
PrivatePort: 4001,
|
||||||
|
PublicPort: 0,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
types.Port{
|
||||||
|
PrivatePort: 2380,
|
||||||
|
PublicPort: 0,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
types.Port{
|
||||||
|
PrivatePort: 2379,
|
||||||
|
PublicPort: 2379,
|
||||||
|
Type: "tcp",
|
||||||
|
IP: "0.0.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SizeRw: 0,
|
||||||
|
SizeRootFs: 0,
|
||||||
}
|
}
|
||||||
for _, label := range tt.exclude {
|
container2 := types.Container{
|
||||||
d.LabelExclude = append(d.LabelExclude, label)
|
ID: "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173",
|
||||||
|
Names: []string{"/etcd2"},
|
||||||
|
Image: "quay.io:4443/coreos/etcd:v2.2.2",
|
||||||
|
Command: "/etcd -name etcd2 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379",
|
||||||
|
Created: 1455941933,
|
||||||
|
Status: "Up 4 hours",
|
||||||
|
Ports: []types.Port{
|
||||||
|
types.Port{
|
||||||
|
PrivatePort: 7002,
|
||||||
|
PublicPort: 0,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
types.Port{
|
||||||
|
PrivatePort: 4002,
|
||||||
|
PublicPort: 0,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
types.Port{
|
||||||
|
PrivatePort: 2381,
|
||||||
|
PublicPort: 0,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
types.Port{
|
||||||
|
PrivatePort: 2382,
|
||||||
|
PublicPort: 2382,
|
||||||
|
Type: "tcp",
|
||||||
|
IP: "0.0.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SizeRw: 0,
|
||||||
|
SizeRootFs: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := d.Gather(&acc)
|
containers := []types.Container{container1, container2}
|
||||||
require.NoError(t, err)
|
return containers, nil
|
||||||
|
|
||||||
for _, label := range tt.expected {
|
//#{e6a96c84ca91a5258b7cb752579fb68826b68b49ff957487695cd4d13c343b44 titilambert/snmpsim /bin/sh -c 'snmpsimd --agent-udpv4-endpoint=0.0.0.0:31161 --process-user=root --process-group=user' 1455724831 Up 4 hours [{31161 31161 udp 0.0.0.0}] 0 0 [/snmp] map[]}]2016/02/24 01:05:01 Gathered metrics, (3s interval), from 1 inputs in 1.233836656s
|
||||||
if !acc.HasTag("docker_container_cpu", label) {
|
|
||||||
t.Errorf("Didn't get expected label of %s. Test was: Include: %s Exclude %s",
|
|
||||||
label, tt.include, tt.exclude)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, label := range tt.notexpected {
|
func (d FakeDockerClient) ContainerStats(ctx context.Context, containerID string, stream bool) (io.ReadCloser, error) {
|
||||||
if acc.HasTag("docker_container_cpu", label) {
|
var stat io.ReadCloser
|
||||||
t.Errorf("Got unexpected label of %s. Test was: Include: %s Exclude %s",
|
jsonStat := `{"read":"2016-02-24T11:42:27.472459608-05:00","memory_stats":{"stats":{},"limit":18935443456},"blkio_stats":{"io_service_bytes_recursive":[{"major":252,"minor":1,"op":"Read","value":753664},{"major":252,"minor":1,"op":"Write"},{"major":252,"minor":1,"op":"Sync"},{"major":252,"minor":1,"op":"Async","value":753664},{"major":252,"minor":1,"op":"Total","value":753664}],"io_serviced_recursive":[{"major":252,"minor":1,"op":"Read","value":26},{"major":252,"minor":1,"op":"Write"},{"major":252,"minor":1,"op":"Sync"},{"major":252,"minor":1,"op":"Async","value":26},{"major":252,"minor":1,"op":"Total","value":26}]},"cpu_stats":{"cpu_usage":{"percpu_usage":[17871,4959158,1646137,1231652,11829401,244656,369972,0],"usage_in_usermode":10000000,"total_usage":20298847},"system_cpu_usage":24052607520000000,"throttling_data":{}},"precpu_stats":{"cpu_usage":{"percpu_usage":[17871,4959158,1646137,1231652,11829401,244656,369972,0],"usage_in_usermode":10000000,"total_usage":20298847},"system_cpu_usage":24052599550000000,"throttling_data":{}}}`
|
||||||
label, tt.include, tt.exclude)
|
stat = ioutil.NopCloser(strings.NewReader(jsonStat))
|
||||||
}
|
return stat, nil
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDockerGatherInfo(t *testing.T) {
|
func TestDockerGatherInfo(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
d := Docker{
|
client := FakeDockerClient{}
|
||||||
client: nil,
|
d := Docker{client: client}
|
||||||
testing: true,
|
|
||||||
}
|
err := d.Gather(&acc)
|
||||||
|
|
||||||
err := acc.GatherError(d.Gather)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
acc.AssertContainsTaggedFields(t,
|
acc.AssertContainsTaggedFields(t,
|
||||||
@@ -345,8 +433,6 @@ func TestDockerGatherInfo(t *testing.T) {
|
|||||||
"cpu": "cpu3",
|
"cpu": "cpu3",
|
||||||
"container_version": "v2.2.2",
|
"container_version": "v2.2.2",
|
||||||
"engine_host": "absol",
|
"engine_host": "absol",
|
||||||
"label1": "test_value_1",
|
|
||||||
"label2": "test_value_2",
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
acc.AssertContainsTaggedFields(t,
|
acc.AssertContainsTaggedFields(t,
|
||||||
@@ -393,8 +479,6 @@ func TestDockerGatherInfo(t *testing.T) {
|
|||||||
"container_name": "etcd2",
|
"container_name": "etcd2",
|
||||||
"container_image": "quay.io:4443/coreos/etcd",
|
"container_image": "quay.io:4443/coreos/etcd",
|
||||||
"container_version": "v2.2.2",
|
"container_version": "v2.2.2",
|
||||||
"label1": "test_value_1",
|
|
||||||
"label2": "test_value_2",
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,151 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FakeDockerClient struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d FakeDockerClient) Info(ctx context.Context) (types.Info, error) {
|
|
||||||
env := types.Info{
|
|
||||||
Containers: 108,
|
|
||||||
ContainersRunning: 98,
|
|
||||||
ContainersStopped: 6,
|
|
||||||
ContainersPaused: 3,
|
|
||||||
OomKillDisable: false,
|
|
||||||
SystemTime: "2016-02-24T00:55:09.15073105-05:00",
|
|
||||||
NEventsListener: 0,
|
|
||||||
ID: "5WQQ:TFWR:FDNG:OKQ3:37Y4:FJWG:QIKK:623T:R3ME:QTKB:A7F7:OLHD",
|
|
||||||
Debug: false,
|
|
||||||
LoggingDriver: "json-file",
|
|
||||||
KernelVersion: "4.3.0-1-amd64",
|
|
||||||
IndexServerAddress: "https://index.docker.io/v1/",
|
|
||||||
MemTotal: 3840757760,
|
|
||||||
Images: 199,
|
|
||||||
CPUCfsQuota: true,
|
|
||||||
Name: "absol",
|
|
||||||
SwapLimit: false,
|
|
||||||
IPv4Forwarding: true,
|
|
||||||
ExperimentalBuild: false,
|
|
||||||
CPUCfsPeriod: true,
|
|
||||||
RegistryConfig: ®istry.ServiceConfig{
|
|
||||||
IndexConfigs: map[string]*registry.IndexInfo{
|
|
||||||
"docker.io": {
|
|
||||||
Name: "docker.io",
|
|
||||||
Mirrors: []string{},
|
|
||||||
Official: true,
|
|
||||||
Secure: true,
|
|
||||||
},
|
|
||||||
}, InsecureRegistryCIDRs: []*registry.NetIPNet{{IP: []byte{127, 0, 0, 0}, Mask: []byte{255, 0, 0, 0}}}, Mirrors: []string{}},
|
|
||||||
OperatingSystem: "Linux Mint LMDE (containerized)",
|
|
||||||
BridgeNfIptables: true,
|
|
||||||
HTTPSProxy: "",
|
|
||||||
Labels: []string{},
|
|
||||||
MemoryLimit: false,
|
|
||||||
DriverStatus: [][2]string{{"Pool Name", "docker-8:1-1182287-pool"}, {"Pool Blocksize", "65.54 kB"}, {"Backing Filesystem", "extfs"}, {"Data file", "/dev/loop0"}, {"Metadata file", "/dev/loop1"}, {"Data Space Used", "17.3 GB"}, {"Data Space Total", "107.4 GB"}, {"Data Space Available", "36.53 GB"}, {"Metadata Space Used", "20.97 MB"}, {"Metadata Space Total", "2.147 GB"}, {"Metadata Space Available", "2.127 GB"}, {"Udev Sync Supported", "true"}, {"Deferred Removal Enabled", "false"}, {"Data loop file", "/var/lib/docker/devicemapper/devicemapper/data"}, {"Metadata loop file", "/var/lib/docker/devicemapper/devicemapper/metadata"}, {"Library Version", "1.02.115 (2016-01-25)"}},
|
|
||||||
NFd: 19,
|
|
||||||
HTTPProxy: "",
|
|
||||||
Driver: "devicemapper",
|
|
||||||
NGoroutines: 39,
|
|
||||||
NCPU: 4,
|
|
||||||
DockerRootDir: "/var/lib/docker",
|
|
||||||
NoProxy: "",
|
|
||||||
BridgeNfIP6tables: true,
|
|
||||||
}
|
|
||||||
return env, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d FakeDockerClient) ContainerList(octx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
|
|
||||||
container1 := types.Container{
|
|
||||||
ID: "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
|
||||||
Names: []string{"/etcd"},
|
|
||||||
Image: "quay.io/coreos/etcd:v2.2.2",
|
|
||||||
Command: "/etcd -name etcd0 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379",
|
|
||||||
Created: 1455941930,
|
|
||||||
Status: "Up 4 hours",
|
|
||||||
Ports: []types.Port{
|
|
||||||
types.Port{
|
|
||||||
PrivatePort: 7001,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
types.Port{
|
|
||||||
PrivatePort: 4001,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
types.Port{
|
|
||||||
PrivatePort: 2380,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
types.Port{
|
|
||||||
PrivatePort: 2379,
|
|
||||||
PublicPort: 2379,
|
|
||||||
Type: "tcp",
|
|
||||||
IP: "0.0.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Labels: map[string]string{
|
|
||||||
"label1": "test_value_1",
|
|
||||||
"label2": "test_value_2",
|
|
||||||
},
|
|
||||||
SizeRw: 0,
|
|
||||||
SizeRootFs: 0,
|
|
||||||
}
|
|
||||||
container2 := types.Container{
|
|
||||||
ID: "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173",
|
|
||||||
Names: []string{"/etcd2"},
|
|
||||||
Image: "quay.io:4443/coreos/etcd:v2.2.2",
|
|
||||||
Command: "/etcd -name etcd2 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379",
|
|
||||||
Created: 1455941933,
|
|
||||||
Status: "Up 4 hours",
|
|
||||||
Ports: []types.Port{
|
|
||||||
types.Port{
|
|
||||||
PrivatePort: 7002,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
types.Port{
|
|
||||||
PrivatePort: 4002,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
types.Port{
|
|
||||||
PrivatePort: 2381,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
types.Port{
|
|
||||||
PrivatePort: 2382,
|
|
||||||
PublicPort: 2382,
|
|
||||||
Type: "tcp",
|
|
||||||
IP: "0.0.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Labels: map[string]string{
|
|
||||||
"label1": "test_value_1",
|
|
||||||
"label2": "test_value_2",
|
|
||||||
},
|
|
||||||
SizeRw: 0,
|
|
||||||
SizeRootFs: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
containers := []types.Container{container1, container2}
|
|
||||||
return containers, nil
|
|
||||||
|
|
||||||
//#{e6a96c84ca91a5258b7cb752579fb68826b68b49ff957487695cd4d13c343b44 titilambert/snmpsim /bin/sh -c 'snmpsimd --agent-udpv4-endpoint=0.0.0.0:31161 --process-user=root --process-group=user' 1455724831 Up 4 hours [{31161 31161 udp 0.0.0.0}] 0 0 [/snmp] map[]}]2016/02/24 01:05:01 Gathered metrics, (3s interval), from 1 inputs in 1.233836656s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d FakeDockerClient) ContainerStats(ctx context.Context, containerID string, stream bool) (types.ContainerStats, error) {
|
|
||||||
var stat types.ContainerStats
|
|
||||||
jsonStat := `{"read":"2016-02-24T11:42:27.472459608-05:00","memory_stats":{"stats":{},"limit":18935443456},"blkio_stats":{"io_service_bytes_recursive":[{"major":252,"minor":1,"op":"Read","value":753664},{"major":252,"minor":1,"op":"Write"},{"major":252,"minor":1,"op":"Sync"},{"major":252,"minor":1,"op":"Async","value":753664},{"major":252,"minor":1,"op":"Total","value":753664}],"io_serviced_recursive":[{"major":252,"minor":1,"op":"Read","value":26},{"major":252,"minor":1,"op":"Write"},{"major":252,"minor":1,"op":"Sync"},{"major":252,"minor":1,"op":"Async","value":26},{"major":252,"minor":1,"op":"Total","value":26}]},"cpu_stats":{"cpu_usage":{"percpu_usage":[17871,4959158,1646137,1231652,11829401,244656,369972,0],"usage_in_usermode":10000000,"total_usage":20298847},"system_cpu_usage":24052607520000000,"throttling_data":{}},"precpu_stats":{"cpu_usage":{"percpu_usage":[17871,4959158,1646137,1231652,11829401,244656,369972,0],"usage_in_usermode":10000000,"total_usage":20298847},"system_cpu_usage":24052599550000000,"throttling_data":{}}}`
|
|
||||||
stat.Body = ioutil.NopCloser(strings.NewReader(jsonStat))
|
|
||||||
return stat, nil
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
|
"github.com/influxdata/telegraf/internal/errchan"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ func (d *Dovecot) SampleConfig() string { return sampleConfig }
|
|||||||
const defaultPort = "24242"
|
const defaultPort = "24242"
|
||||||
|
|
||||||
// Reads stats from all configured servers.
|
// Reads stats from all configured servers.
|
||||||
func (d *Dovecot) Gather(acc telegraf.Accumulator) error {
|
func (d *Dovecot) Gather(acc plugins.Accumulator) error {
|
||||||
if !validQuery[d.Type] {
|
if !validQuery[d.Type] {
|
||||||
return fmt.Errorf("Error: %s is not a valid query type\n",
|
return fmt.Errorf("Error: %s is not a valid query type\n",
|
||||||
d.Type)
|
d.Type)
|
||||||
@@ -65,21 +66,22 @@ func (d *Dovecot) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
errChan := errchan.New(len(d.Servers) * len(d.Filters))
|
||||||
for _, server := range d.Servers {
|
for _, server := range d.Servers {
|
||||||
for _, filter := range d.Filters {
|
for _, filter := range d.Filters {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(s string, f string) {
|
go func(s string, f string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
acc.AddError(d.gatherServer(s, acc, d.Type, f))
|
errChan.C <- d.gatherServer(s, acc, d.Type, f)
|
||||||
}(server, filter)
|
}(server, filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return nil
|
return errChan.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dovecot) gatherServer(addr string, acc telegraf.Accumulator, qtype string, filter string) error {
|
func (d *Dovecot) gatherServer(addr string, acc plugins.Accumulator, qtype string, filter string) error {
|
||||||
_, _, err := net.SplitHostPort(addr)
|
_, _, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error: %s on url %s\n", err, addr)
|
return fmt.Errorf("Error: %s on url %s\n", err, addr)
|
||||||
@@ -109,7 +111,7 @@ func (d *Dovecot) gatherServer(addr string, acc telegraf.Accumulator, qtype stri
|
|||||||
return gatherStats(&buf, acc, host, qtype)
|
return gatherStats(&buf, acc, host, qtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
func gatherStats(buf *bytes.Buffer, acc telegraf.Accumulator, host string, qtype string) error {
|
func gatherStats(buf *bytes.Buffer, acc plugins.Accumulator, host string, qtype string) error {
|
||||||
|
|
||||||
lines := strings.Split(buf.String(), "\n")
|
lines := strings.Split(buf.String(), "\n")
|
||||||
head := strings.Split(lines[0], "\t")
|
head := strings.Split(lines[0], "\t")
|
||||||
@@ -181,7 +183,7 @@ func secParser(tm string) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("dovecot", func() telegraf.Input {
|
inputs.Add("dovecot", func() plugins.Input {
|
||||||
return &Dovecot{}
|
return &Dovecot{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
|
"github.com/influxdata/telegraf/internal/errchan"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
jsonparser "github.com/influxdata/telegraf/plugins/parsers/json"
|
jsonparser "github.com/influxdata/telegraf/plugins/parsers/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -142,7 +143,7 @@ func (e *Elasticsearch) Description() string {
|
|||||||
|
|
||||||
// Gather reads the stats from Elasticsearch and writes it to the
|
// Gather reads the stats from Elasticsearch and writes it to the
|
||||||
// Accumulator.
|
// Accumulator.
|
||||||
func (e *Elasticsearch) Gather(acc telegraf.Accumulator) error {
|
func (e *Elasticsearch) Gather(acc plugins.Accumulator) error {
|
||||||
if e.client == nil {
|
if e.client == nil {
|
||||||
client, err := e.createHttpClient()
|
client, err := e.createHttpClient()
|
||||||
|
|
||||||
@@ -152,11 +153,12 @@ func (e *Elasticsearch) Gather(acc telegraf.Accumulator) error {
|
|||||||
e.client = client
|
e.client = client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errChan := errchan.New(len(e.Servers) * 3)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(e.Servers))
|
wg.Add(len(e.Servers))
|
||||||
|
|
||||||
for _, serv := range e.Servers {
|
for _, serv := range e.Servers {
|
||||||
go func(s string, acc telegraf.Accumulator) {
|
go func(s string, acc plugins.Accumulator) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
var url string
|
var url string
|
||||||
if e.Local {
|
if e.Local {
|
||||||
@@ -169,29 +171,29 @@ func (e *Elasticsearch) Gather(acc telegraf.Accumulator) error {
|
|||||||
if e.ClusterStats {
|
if e.ClusterStats {
|
||||||
// get cat/master information here so NodeStats can determine
|
// get cat/master information here so NodeStats can determine
|
||||||
// whether this node is the Master
|
// whether this node is the Master
|
||||||
if err := e.setCatMaster(s + "/_cat/master"); err != nil {
|
e.setCatMaster(s + "/_cat/master")
|
||||||
acc.AddError(fmt.Errorf(mask.ReplaceAllString(err.Error(), "http(s)://XXX:XXX@")))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always gather node states
|
// Always gather node states
|
||||||
if err := e.gatherNodeStats(url, acc); err != nil {
|
if err := e.gatherNodeStats(url, acc); err != nil {
|
||||||
acc.AddError(fmt.Errorf(mask.ReplaceAllString(err.Error(), "http(s)://XXX:XXX@")))
|
err = fmt.Errorf(mask.ReplaceAllString(err.Error(), "http(s)://XXX:XXX@"))
|
||||||
|
errChan.C <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.ClusterHealth {
|
if e.ClusterHealth {
|
||||||
url = s + "/_cluster/health?level=indices"
|
url = s + "/_cluster/health?level=indices"
|
||||||
if err := e.gatherClusterHealth(url, acc); err != nil {
|
if err := e.gatherClusterHealth(url, acc); err != nil {
|
||||||
acc.AddError(fmt.Errorf(mask.ReplaceAllString(err.Error(), "http(s)://XXX:XXX@")))
|
err = fmt.Errorf(mask.ReplaceAllString(err.Error(), "http(s)://XXX:XXX@"))
|
||||||
|
errChan.C <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.ClusterStats && e.isMaster {
|
if e.ClusterStats && e.isMaster {
|
||||||
if err := e.gatherClusterStats(s+"/_cluster/stats", acc); err != nil {
|
if err := e.gatherClusterStats(s+"/_cluster/stats", acc); err != nil {
|
||||||
acc.AddError(fmt.Errorf(mask.ReplaceAllString(err.Error(), "http(s)://XXX:XXX@")))
|
err = fmt.Errorf(mask.ReplaceAllString(err.Error(), "http(s)://XXX:XXX@"))
|
||||||
|
errChan.C <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,7 +201,7 @@ func (e *Elasticsearch) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return nil
|
return errChan.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Elasticsearch) createHttpClient() (*http.Client, error) {
|
func (e *Elasticsearch) createHttpClient() (*http.Client, error) {
|
||||||
@@ -219,7 +221,7 @@ func (e *Elasticsearch) createHttpClient() (*http.Client, error) {
|
|||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Elasticsearch) gatherNodeStats(url string, acc telegraf.Accumulator) error {
|
func (e *Elasticsearch) gatherNodeStats(url string, acc plugins.Accumulator) error {
|
||||||
nodeStats := &struct {
|
nodeStats := &struct {
|
||||||
ClusterName string `json:"cluster_name"`
|
ClusterName string `json:"cluster_name"`
|
||||||
Nodes map[string]*nodeStat `json:"nodes"`
|
Nodes map[string]*nodeStat `json:"nodes"`
|
||||||
@@ -271,7 +273,7 @@ func (e *Elasticsearch) gatherNodeStats(url string, acc telegraf.Accumulator) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Elasticsearch) gatherClusterHealth(url string, acc telegraf.Accumulator) error {
|
func (e *Elasticsearch) gatherClusterHealth(url string, acc plugins.Accumulator) error {
|
||||||
healthStats := &clusterHealth{}
|
healthStats := &clusterHealth{}
|
||||||
if err := e.gatherJsonData(url, healthStats); err != nil {
|
if err := e.gatherJsonData(url, healthStats); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -316,7 +318,7 @@ func (e *Elasticsearch) gatherClusterHealth(url string, acc telegraf.Accumulator
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Elasticsearch) gatherClusterStats(url string, acc telegraf.Accumulator) error {
|
func (e *Elasticsearch) gatherClusterStats(url string, acc plugins.Accumulator) error {
|
||||||
clusterStats := &clusterStats{}
|
clusterStats := &clusterStats{}
|
||||||
if err := e.gatherJsonData(url, clusterStats); err != nil {
|
if err := e.gatherJsonData(url, clusterStats); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -356,7 +358,7 @@ func (e *Elasticsearch) setCatMaster(url string) error {
|
|||||||
// NOTE: we are not going to read/discard r.Body under the assumption we'd prefer
|
// NOTE: we are not going to read/discard r.Body under the assumption we'd prefer
|
||||||
// to let the underlying transport close the connection and re-establish a new one for
|
// to let the underlying transport close the connection and re-establish a new one for
|
||||||
// future calls.
|
// future calls.
|
||||||
return fmt.Errorf("elasticsearch: Unable to retrieve master node information. API responded with status-code %d, expected %d", r.StatusCode, http.StatusOK)
|
return fmt.Errorf("status-code %d, expected %d", r.StatusCode, http.StatusOK)
|
||||||
}
|
}
|
||||||
response, err := ioutil.ReadAll(r.Body)
|
response, err := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
@@ -391,7 +393,7 @@ func (e *Elasticsearch) gatherJsonData(url string, v interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("elasticsearch", func() telegraf.Input {
|
inputs.Add("elasticsearch", func() plugins.Input {
|
||||||
return NewElasticsearch()
|
return NewElasticsearch()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func TestGather(t *testing.T) {
|
|||||||
es.client.Transport = newTransportMock(http.StatusOK, nodeStatsResponse)
|
es.client.Transport = newTransportMock(http.StatusOK, nodeStatsResponse)
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
if err := acc.GatherError(es.Gather); err != nil {
|
if err := es.Gather(&acc); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ import (
|
|||||||
|
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/kballard/go-shellquote"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
|
"github.com/influxdata/telegraf/internal/errchan"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
"github.com/influxdata/telegraf/plugins/parsers"
|
||||||
"github.com/influxdata/telegraf/plugins/parsers/nagios"
|
"github.com/influxdata/telegraf/plugins/parsers/nagios"
|
||||||
@@ -35,7 +36,7 @@ const sampleConfig = `
|
|||||||
name_suffix = "_mycollector"
|
name_suffix = "_mycollector"
|
||||||
|
|
||||||
## Data format to consume.
|
## Data format to consume.
|
||||||
## Each data format has its own unique set of configuration options, read
|
## Each data format has it's own unique set of configuration options, read
|
||||||
## more about them here:
|
## more about them here:
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "influx"
|
data_format = "influx"
|
||||||
@@ -49,6 +50,7 @@ type Exec struct {
|
|||||||
parser parsers.Parser
|
parser parsers.Parser
|
||||||
|
|
||||||
runner Runner
|
runner Runner
|
||||||
|
errChan chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExec() *Exec {
|
func NewExec() *Exec {
|
||||||
@@ -59,12 +61,12 @@ func NewExec() *Exec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Runner interface {
|
type Runner interface {
|
||||||
Run(*Exec, string, telegraf.Accumulator) ([]byte, error)
|
Run(*Exec, string, plugins.Accumulator) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandRunner struct{}
|
type CommandRunner struct{}
|
||||||
|
|
||||||
func AddNagiosState(exitCode error, acc telegraf.Accumulator) error {
|
func AddNagiosState(exitCode error, acc plugins.Accumulator) error {
|
||||||
nagiosState := 0
|
nagiosState := 0
|
||||||
if exitCode != nil {
|
if exitCode != nil {
|
||||||
exiterr, ok := exitCode.(*exec.ExitError)
|
exiterr, ok := exitCode.(*exec.ExitError)
|
||||||
@@ -87,7 +89,7 @@ func AddNagiosState(exitCode error, acc telegraf.Accumulator) error {
|
|||||||
func (c CommandRunner) Run(
|
func (c CommandRunner) Run(
|
||||||
e *Exec,
|
e *Exec,
|
||||||
command string,
|
command string,
|
||||||
acc telegraf.Accumulator,
|
acc plugins.Accumulator,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
split_cmd, err := shellquote.Split(command)
|
split_cmd, err := shellquote.Split(command)
|
||||||
if err != nil || len(split_cmd) == 0 {
|
if err != nil || len(split_cmd) == 0 {
|
||||||
@@ -143,18 +145,18 @@ func removeCarriageReturns(b bytes.Buffer) bytes.Buffer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Exec) ProcessCommand(command string, acc telegraf.Accumulator, wg *sync.WaitGroup) {
|
func (e *Exec) ProcessCommand(command string, acc plugins.Accumulator, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
out, err := e.runner.Run(e, command, acc)
|
out, err := e.runner.Run(e, command, acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(err)
|
e.errChan <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics, err := e.parser.Parse(out)
|
metrics, err := e.parser.Parse(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(err)
|
e.errChan <- err
|
||||||
} else {
|
} else {
|
||||||
for _, metric := range metrics {
|
for _, metric := range metrics {
|
||||||
acc.AddFields(metric.Name(), metric.Fields(), metric.Tags(), metric.Time())
|
acc.AddFields(metric.Name(), metric.Fields(), metric.Tags(), metric.Time())
|
||||||
@@ -174,7 +176,7 @@ func (e *Exec) SetParser(parser parsers.Parser) {
|
|||||||
e.parser = parser
|
e.parser = parser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Exec) Gather(acc telegraf.Accumulator) error {
|
func (e *Exec) Gather(acc plugins.Accumulator) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
// Legacy single command support
|
// Legacy single command support
|
||||||
if e.Command != "" {
|
if e.Command != "" {
|
||||||
@@ -191,8 +193,7 @@ func (e *Exec) Gather(acc telegraf.Accumulator) error {
|
|||||||
|
|
||||||
matches, err := filepath.Glob(cmdAndArgs[0])
|
matches, err := filepath.Glob(cmdAndArgs[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(err)
|
return err
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
@@ -213,16 +214,19 @@ func (e *Exec) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errChan := errchan.New(len(commands))
|
||||||
|
e.errChan = errChan.C
|
||||||
|
|
||||||
wg.Add(len(commands))
|
wg.Add(len(commands))
|
||||||
for _, command := range commands {
|
for _, command := range commands {
|
||||||
go e.ProcessCommand(command, acc, &wg)
|
go e.ProcessCommand(command, acc, &wg)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return nil
|
return errChan.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("exec", func() telegraf.Input {
|
inputs.Add("exec", func() plugins.Input {
|
||||||
return NewExec()
|
return NewExec()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
"github.com/influxdata/telegraf/plugins/parsers"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
@@ -37,8 +37,6 @@ const malformedJson = `
|
|||||||
`
|
`
|
||||||
|
|
||||||
const lineProtocol = "cpu,host=foo,datacenter=us-east usage_idle=99,usage_busy=1\n"
|
const lineProtocol = "cpu,host=foo,datacenter=us-east usage_idle=99,usage_busy=1\n"
|
||||||
const lineProtocolEmpty = ""
|
|
||||||
const lineProtocolShort = "ab"
|
|
||||||
|
|
||||||
const lineProtocolMulti = `
|
const lineProtocolMulti = `
|
||||||
cpu,cpu=cpu0,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
cpu,cpu=cpu0,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
||||||
@@ -85,7 +83,7 @@ func newRunnerMock(out []byte, err error) Runner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r runnerMock) Run(e *Exec, command string, acc telegraf.Accumulator) ([]byte, error) {
|
func (r runnerMock) Run(e *Exec, command string, acc plugins.Accumulator) ([]byte, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil, r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
@@ -101,7 +99,7 @@ func TestExec(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := acc.GatherError(e.Gather)
|
err := e.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, acc.NFields(), 8, "non-numeric measurements should be ignored")
|
assert.Equal(t, acc.NFields(), 8, "non-numeric measurements should be ignored")
|
||||||
|
|
||||||
@@ -127,7 +125,8 @@ func TestExecMalformed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
require.Error(t, acc.GatherError(e.Gather))
|
err := e.Gather(&acc)
|
||||||
|
require.Error(t, err)
|
||||||
assert.Equal(t, acc.NFields(), 0, "No new points should have been added")
|
assert.Equal(t, acc.NFields(), 0, "No new points should have been added")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +139,8 @@ func TestCommandError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
require.Error(t, acc.GatherError(e.Gather))
|
err := e.Gather(&acc)
|
||||||
|
require.Error(t, err)
|
||||||
assert.Equal(t, acc.NFields(), 0, "No new points should have been added")
|
assert.Equal(t, acc.NFields(), 0, "No new points should have been added")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +153,8 @@ func TestLineProtocolParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
require.NoError(t, acc.GatherError(e.Gather))
|
err := e.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"usage_idle": float64(99),
|
"usage_idle": float64(99),
|
||||||
@@ -166,33 +167,6 @@ func TestLineProtocolParse(t *testing.T) {
|
|||||||
acc.AssertContainsTaggedFields(t, "cpu", fields, tags)
|
acc.AssertContainsTaggedFields(t, "cpu", fields, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLineProtocolEmptyParse(t *testing.T) {
|
|
||||||
parser, _ := parsers.NewInfluxParser()
|
|
||||||
e := &Exec{
|
|
||||||
runner: newRunnerMock([]byte(lineProtocolEmpty), nil),
|
|
||||||
Commands: []string{"line-protocol"},
|
|
||||||
parser: parser,
|
|
||||||
}
|
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
err := e.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLineProtocolShortParse(t *testing.T) {
|
|
||||||
parser, _ := parsers.NewInfluxParser()
|
|
||||||
e := &Exec{
|
|
||||||
runner: newRunnerMock([]byte(lineProtocolShort), nil),
|
|
||||||
Commands: []string{"line-protocol"},
|
|
||||||
parser: parser,
|
|
||||||
}
|
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
err := acc.GatherError(e.Gather)
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "buffer too short", "A buffer too short error was expected")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLineProtocolParseMultiple(t *testing.T) {
|
func TestLineProtocolParseMultiple(t *testing.T) {
|
||||||
parser, _ := parsers.NewInfluxParser()
|
parser, _ := parsers.NewInfluxParser()
|
||||||
e := &Exec{
|
e := &Exec{
|
||||||
@@ -202,7 +176,7 @@ func TestLineProtocolParseMultiple(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := acc.GatherError(e.Gather)
|
err := e.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
@@ -228,7 +202,7 @@ func TestExecCommandWithGlob(t *testing.T) {
|
|||||||
e.SetParser(parser)
|
e.SetParser(parser)
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := acc.GatherError(e.Gather)
|
err := e.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
@@ -244,7 +218,7 @@ func TestExecCommandWithoutGlob(t *testing.T) {
|
|||||||
e.SetParser(parser)
|
e.SetParser(parser)
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := acc.GatherError(e.Gather)
|
err := e.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
@@ -260,7 +234,7 @@ func TestExecCommandWithoutGlobAndPath(t *testing.T) {
|
|||||||
e.SetParser(parser)
|
e.SetParser(parser)
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := acc.GatherError(e.Gather)
|
err := e.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf/plugins"
|
||||||
"github.com/influxdata/telegraf/internal/globpath"
|
"github.com/influxdata/telegraf/internal/globpath"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
@@ -47,7 +47,8 @@ func (_ *FileStat) Description() string {
|
|||||||
|
|
||||||
func (_ *FileStat) SampleConfig() string { return sampleConfig }
|
func (_ *FileStat) SampleConfig() string { return sampleConfig }
|
||||||
|
|
||||||
func (f *FileStat) Gather(acc telegraf.Accumulator) error {
|
func (f *FileStat) Gather(acc plugins.Accumulator) error {
|
||||||
|
var errS string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for _, filepath := range f.Files {
|
for _, filepath := range f.Files {
|
||||||
@@ -55,7 +56,7 @@ func (f *FileStat) Gather(acc telegraf.Accumulator) error {
|
|||||||
g, ok := f.globs[filepath]
|
g, ok := f.globs[filepath]
|
||||||
if !ok {
|
if !ok {
|
||||||
if g, err = globpath.Compile(filepath); err != nil {
|
if g, err = globpath.Compile(filepath); err != nil {
|
||||||
acc.AddError(err)
|
errS += err.Error() + " "
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.globs[filepath] = g
|
f.globs[filepath] = g
|
||||||
@@ -91,7 +92,7 @@ func (f *FileStat) Gather(acc telegraf.Accumulator) error {
|
|||||||
if f.Md5 {
|
if f.Md5 {
|
||||||
md5, err := getMd5(fileName)
|
md5, err := getMd5(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(err)
|
errS += err.Error() + " "
|
||||||
} else {
|
} else {
|
||||||
fields["md5_sum"] = md5
|
fields["md5_sum"] = md5
|
||||||
}
|
}
|
||||||
@@ -101,6 +102,9 @@ func (f *FileStat) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errS != "" {
|
||||||
|
return fmt.Errorf(errS)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +126,7 @@ func getMd5(file string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("filestat", func() telegraf.Input {
|
inputs.Add("filestat", func() plugins.Input {
|
||||||
return NewFileStat()
|
return NewFileStat()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user