Compare commits
41 Commits
ga-azure-m
...
1.7.0-rc3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c31d16fb90 | ||
|
|
5fe1aa1a24 | ||
|
|
b4b3beb765 | ||
|
|
c3a969d34b | ||
|
|
ef67222bc6 | ||
|
|
d616ce7a9e | ||
|
|
b4a0c854f5 | ||
|
|
27cd582f27 | ||
|
|
7103077b3f | ||
|
|
7332ce0e95 | ||
|
|
2be32f0a80 | ||
|
|
701e157ef0 | ||
|
|
eb94bb29fd | ||
|
|
449bd5c3b9 | ||
|
|
96abff0660 | ||
|
|
9eab3572ff | ||
|
|
be8b87000c | ||
|
|
ff93c3c326 | ||
|
|
df1fe7a2b4 | ||
|
|
a04cfee349 | ||
|
|
da6ad34fc8 | ||
|
|
179bcfdcbb | ||
|
|
e3f1d28908 | ||
|
|
fcea745e99 | ||
|
|
90bcb5bc3c | ||
|
|
312116c101 | ||
|
|
2cc2913d81 | ||
|
|
b556eb8b2f | ||
|
|
8b28f40cc0 | ||
|
|
cabee8f8e0 | ||
|
|
e0071f365a | ||
|
|
5ae2b02f5d | ||
|
|
59f0a5354f | ||
|
|
c8b68430f0 | ||
|
|
1ac64596bf | ||
|
|
b78984554c | ||
|
|
2def31bc3d | ||
|
|
010e4f5b0b | ||
|
|
ce3b367dac | ||
|
|
f3f753310f | ||
|
|
50d721ae05 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -19,6 +19,7 @@
|
||||
- [jti_openconfig_telemetry](./plugins/inputs/jti_openconfig_telemetry/README.md) - Contributed by @ajhai
|
||||
- [mcrouter](./plugins/inputs/mcrouter/README.md) - Contributed by @cthayer
|
||||
- [nvidia_smi](./plugins/inputs/nvidia_smi/README.md) - Contributed by @jackzampolin
|
||||
- [syslog](./plugins/inputs/syslog/README.md) - Contributed by @influxdata
|
||||
|
||||
### New Processors
|
||||
|
||||
@@ -56,6 +57,12 @@
|
||||
- [#3489](https://github.com/influxdata/telegraf/pull/3489): Add burrow input plugin.
|
||||
- [#3969](https://github.com/influxdata/telegraf/pull/3969): Add option to unbound module to use threads as tags.
|
||||
- [#4183](https://github.com/influxdata/telegraf/pull/4183): Add support for TLS and username/password auth to aerospike input.
|
||||
- [#4190](https://github.com/influxdata/telegraf/pull/4190): Add special syslog timestamp parser to grok parser that uses current year.
|
||||
- [#4181](https://github.com/influxdata/telegraf/pull/4181): Add syslog input plugin.
|
||||
- [#4212](https://github.com/influxdata/telegraf/pull/4212): Print the enabled aggregator and processor plugins on startup.
|
||||
- [#3994](https://github.com/influxdata/telegraf/pull/3994): Add static routing_key option to amqp output.
|
||||
- [#3995](https://github.com/influxdata/telegraf/pull/3995): Add passive mode exchange declaration option to amqp consumer input.
|
||||
- [#4216](https://github.com/influxdata/telegraf/pull/4216): Add counter fields to pf input.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
@@ -63,6 +70,19 @@
|
||||
- [#4036](https://github.com/influxdata/telegraf/pull/4036): Add all win_perf_counters fields for a series in a single metric.
|
||||
- [#4118](https://github.com/influxdata/telegraf/pull/4118): Report results of dns_query instead of 0ms on timeout.
|
||||
- [#4155](https://github.com/influxdata/telegraf/pull/4155): Add consul service tags to metric.
|
||||
- [#2879](https://github.com/influxdata/telegraf/issues/2879): Fix wildcards and multi instance processes in win_perf_counters.
|
||||
- [#2468](https://github.com/influxdata/telegraf/issues/2468): Fix crash on 32-bit Windows in win_perf_counters.
|
||||
- [#4198](https://github.com/influxdata/telegraf/issues/4198): Fix win_perf_counters not collecting at every interval.
|
||||
- [#4227](https://github.com/influxdata/telegraf/issues/4227): Use same flags for all BSD family ping variants.
|
||||
|
||||
## v1.6.4 [2018-06-05]
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- [#4203](https://github.com/influxdata/telegraf/issues/4203): Fix snmp overriding of auto-configured table fields.
|
||||
- [#4218](https://github.com/influxdata/telegraf/issues/4218): Fix uint support in cloudwatch output.
|
||||
- [#4188](https://github.com/influxdata/telegraf/pull/4188): Fix documentation of instance_name option in varnish input.
|
||||
- [#4195](https://github.com/influxdata/telegraf/pull/4195): Revert to previous aerospike library version due to memory leak.
|
||||
|
||||
## v1.6.3 [2018-05-21]
|
||||
|
||||
|
||||
5
Godeps
5
Godeps
@@ -1,6 +1,6 @@
|
||||
code.cloudfoundry.org/clock e9dc86bbf0e5bbe6bf7ff5a6f71e048959b61f71
|
||||
collectd.org 2ce144541b8903101fb8f1483cc0497a68798122
|
||||
github.com/aerospike/aerospike-client-go 9701404f4c60a6ea256595d24bf318f721a7e8b8
|
||||
github.com/aerospike/aerospike-client-go 95e1ad7791bdbca44707fedbb29be42024900d9c
|
||||
github.com/amir/raidman c74861fe6a7bb8ede0a010ce4485bdbb4fc4c985
|
||||
github.com/apache/thrift 4aaa92ece8503a6da9bc6701604f69acf2b99d07
|
||||
github.com/aws/aws-sdk-go c861d27d0304a79f727e9a8a4e2ac1e74602fdc0
|
||||
@@ -32,8 +32,9 @@ github.com/go-redis/redis 73b70592cdaa9e6abdfcfbf97b4a90d80728c836
|
||||
github.com/go-sql-driver/mysql 2e00b5cd70399450106cec6431c2e2ce3cae5034
|
||||
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
|
||||
github.com/hashicorp/consul 5174058f0d2bda63fa5198ab96c33d9a909c58ed
|
||||
github.com/influxdata/go-syslog eecd51df3ad85464a2bab9b7d3a45bc1e299059e
|
||||
github.com/influxdata/tail c43482518d410361b6c383d7aebce33d0471d7bc
|
||||
github.com/influxdata/toml 5d1d907f22ead1cd47adde17ceec5bda9cacaf8f
|
||||
github.com/influxdata/toml 2a2e3012f7cfbef64091cc79776311e65dfa211b
|
||||
github.com/influxdata/wlog 7c63b0a71ef8300adc255344d275e10e5c3a71ec
|
||||
github.com/fsnotify/fsnotify c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9
|
||||
github.com/jackc/pgx 63f58fd32edb5684b9e9f4cfaac847c6b42b3917
|
||||
|
||||
@@ -211,6 +211,7 @@ configuration options.
|
||||
* [snmp_legacy](./plugins/inputs/snmp_legacy)
|
||||
* [solr](./plugins/inputs/solr)
|
||||
* [sql server](./plugins/inputs/sqlserver) (microsoft)
|
||||
* [syslog](./plugins/inputs/syslog)
|
||||
* [teamspeak](./plugins/inputs/teamspeak)
|
||||
* [tomcat](./plugins/inputs/tomcat)
|
||||
* [twemproxy](./plugins/inputs/twemproxy)
|
||||
|
||||
@@ -166,8 +166,10 @@ func reloadLoop(
|
||||
}()
|
||||
|
||||
log.Printf("I! Starting Telegraf %s\n", displayVersion())
|
||||
log.Printf("I! Loaded outputs: %s", strings.Join(c.OutputNames(), " "))
|
||||
log.Printf("I! Loaded inputs: %s", strings.Join(c.InputNames(), " "))
|
||||
log.Printf("I! Loaded aggregators: %s", strings.Join(c.AggregatorNames(), " "))
|
||||
log.Printf("I! Loaded processors: %s", strings.Join(c.ProcessorNames(), " "))
|
||||
log.Printf("I! Loaded outputs: %s", strings.Join(c.OutputNames(), " "))
|
||||
log.Printf("I! Tags enabled: %s", c.ListTags())
|
||||
|
||||
if *fPidfile != "" {
|
||||
|
||||
@@ -44,6 +44,7 @@ following works:
|
||||
- github.com/hashicorp/raft [MPL](https://github.com/hashicorp/raft/blob/master/LICENSE)
|
||||
- github.com/influxdata/tail [MIT](https://github.com/influxdata/tail/blob/master/LICENSE.txt)
|
||||
- github.com/influxdata/toml [MIT](https://github.com/influxdata/toml/blob/master/LICENSE)
|
||||
- github.com/influxdata/go-syslog [MIT](https://github.com/influxdata/go-syslog/blob/develop/LICENSE)
|
||||
- github.com/influxdata/wlog [MIT](https://github.com/influxdata/wlog/blob/master/LICENSE)
|
||||
- github.com/jackc/pgx [MIT](https://github.com/jackc/pgx/blob/master/LICENSE)
|
||||
- github.com/jmespath/go-jmespath [APACHE](https://github.com/jmespath/go-jmespath/blob/master/LICENSE)
|
||||
|
||||
@@ -158,30 +158,74 @@
|
||||
# # timeout = "5s"
|
||||
|
||||
|
||||
# # Configuration for the AMQP server to send metrics to
|
||||
# # Publishes metrics to an AMQP broker
|
||||
# [[outputs.amqp]]
|
||||
# ## AMQP url
|
||||
# url = "amqp://localhost:5672/influxdb"
|
||||
# ## AMQP exchange
|
||||
# ## Broker to publish to.
|
||||
# ## deprecated in 1.7; use the brokers option
|
||||
# # url = "amqp://localhost:5672/influxdb"
|
||||
#
|
||||
# ## Brokers to publish to. If multiple brokers are specified a random broker
|
||||
# ## will be selected anytime a connection is established. This can be
|
||||
# ## helpful for load balancing when not using a dedicated load balancer.
|
||||
# brokers = ["amqp://localhost:5672/influxdb"]
|
||||
#
|
||||
# ## Maximum messages to send over a connection. Once this is reached, the
|
||||
# ## connection is closed and a new connection is made. This can be helpful for
|
||||
# ## load balancing when not using a dedicated load balancer.
|
||||
# # max_messages = 0
|
||||
#
|
||||
# ## Exchange to declare and publish to.
|
||||
# exchange = "telegraf"
|
||||
#
|
||||
# ## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
|
||||
# # exchange_type = "topic"
|
||||
#
|
||||
# ## If true, exchange will be passively declared.
|
||||
# # exchange_declare_passive = false
|
||||
#
|
||||
# ## If true, exchange will be created as a durable exchange.
|
||||
# # exchange_durable = true
|
||||
#
|
||||
# ## Additional exchange arguments.
|
||||
# # exchange_arguments = { }
|
||||
# # exchange_arguments = {"hash_propery" = "timestamp"}
|
||||
#
|
||||
# ## Authentication credentials for the PLAIN auth_method.
|
||||
# # username = ""
|
||||
# # password = ""
|
||||
#
|
||||
# ## 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"
|
||||
# ## Telegraf tag to use as a routing key
|
||||
# ## ie, if this tag exists, its value will be used as the routing key
|
||||
# routing_tag = "host"
|
||||
# ## Delivery Mode controls if a published message is persistent
|
||||
# ## Valid options are "transient" and "persistent". default: "transient"
|
||||
# delivery_mode = "transient"
|
||||
#
|
||||
# ## InfluxDB retention policy
|
||||
# # retention_policy = "default"
|
||||
# ## InfluxDB database
|
||||
# ## Metric tag to use as a routing key.
|
||||
# ## ie, if this tag exists, its value will be used as the routing key
|
||||
# # routing_tag = "host"
|
||||
#
|
||||
# ## Static routing key. Used when no routing_tag is set or as a fallback
|
||||
# ## when the tag specified in routing tag is not found.
|
||||
# # routing_key = ""
|
||||
# # routing_key = "telegraf"
|
||||
#
|
||||
# ## Delivery Mode controls if a published message is persistent.
|
||||
# ## One of "transient" or "persistent".
|
||||
# # delivery_mode = "transient"
|
||||
#
|
||||
# ## InfluxDB database added as a message header.
|
||||
# ## deprecated in 1.7; use the headers option
|
||||
# # database = "telegraf"
|
||||
#
|
||||
# ## Write timeout, formatted as a string. If not provided, will default
|
||||
# ## to 5s. 0s means no timeout (not recommended).
|
||||
# ## InfluxDB retention policy added as a message header
|
||||
# ## deprecated in 1.7; use the headers option
|
||||
# # retention_policy = "default"
|
||||
#
|
||||
# ## Static headers added to each published message.
|
||||
# # headers = { }
|
||||
# # headers = {"database" = "telegraf", "retention_policy" = "default"}
|
||||
#
|
||||
# ## Connection timeout. If not provided, will default to 5s. 0s means no
|
||||
# ## timeout (not recommended).
|
||||
# # timeout = "5s"
|
||||
#
|
||||
# ## Optional TLS Config
|
||||
@@ -191,11 +235,16 @@
|
||||
# ## Use TLS but skip chain & host verification
|
||||
# # insecure_skip_verify = false
|
||||
#
|
||||
# ## If true use batch serialization format instead of line based delimiting.
|
||||
# ## Only applies to data formats which are not line based such as JSON.
|
||||
# ## Recommended to set to true.
|
||||
# # use_batch_format = 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"
|
||||
# # data_format = "influx"
|
||||
|
||||
|
||||
# # Send metrics to Azure Application Insights
|
||||
@@ -344,6 +393,10 @@
|
||||
# ## Graphite output template
|
||||
# ## see https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||
# template = "host.tags.measurement.field"
|
||||
#
|
||||
# ## Enable Graphite tags support
|
||||
# # graphite_tag_support = false
|
||||
#
|
||||
# ## timeout in seconds for the write connection to graphite
|
||||
# timeout = 2
|
||||
#
|
||||
@@ -376,11 +429,6 @@
|
||||
# # username = "username"
|
||||
# # password = "pa$$word"
|
||||
#
|
||||
# ## Additional HTTP headers
|
||||
# # [outputs.http.headers]
|
||||
# # # Should be set to "application/json" for json data_format
|
||||
# # Content-Type = "text/plain; charset=utf-8"
|
||||
#
|
||||
# ## Optional TLS Config
|
||||
# # tls_ca = "/etc/telegraf/ca.pem"
|
||||
# # tls_cert = "/etc/telegraf/cert.pem"
|
||||
@@ -393,6 +441,11 @@
|
||||
# ## more about them here:
|
||||
# ## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||
# # data_format = "influx"
|
||||
#
|
||||
# ## Additional HTTP headers
|
||||
# # [outputs.http.headers]
|
||||
# # # Should be set manually to "application/json" for json data_format
|
||||
# # Content-Type = "text/plain; charset=utf-8"
|
||||
|
||||
|
||||
# # Configuration for sending metrics to an Instrumental project
|
||||
@@ -825,6 +878,34 @@
|
||||
# PROCESSOR PLUGINS #
|
||||
###############################################################################
|
||||
|
||||
# # Convert values to another metric value type
|
||||
# [[processors.converter]]
|
||||
# ## Tags to convert
|
||||
# ##
|
||||
# ## The table key determines the target type, and the array of key-values
|
||||
# ## select the keys to convert. The array may contain globs.
|
||||
# ## <target-type> = [<tag-key>...]
|
||||
# [processors.converter.tags]
|
||||
# string = []
|
||||
# integer = []
|
||||
# unsigned = []
|
||||
# boolean = []
|
||||
# float = []
|
||||
#
|
||||
# ## Fields to convert
|
||||
# ##
|
||||
# ## The table key determines the target type, and the array of key-values
|
||||
# ## select the keys to convert. The array may contain globs.
|
||||
# ## <target-type> = [<field-key>...]
|
||||
# [processors.converter.fields]
|
||||
# tag = []
|
||||
# string = []
|
||||
# integer = []
|
||||
# unsigned = []
|
||||
# boolean = []
|
||||
# float = []
|
||||
|
||||
|
||||
# # Apply metric modifications using override semantics.
|
||||
# [[processors.override]]
|
||||
# ## All modifications on inputs and aggregators can be overridden:
|
||||
@@ -841,6 +922,36 @@
|
||||
# [[processors.printer]]
|
||||
|
||||
|
||||
# # Transforms tag and field values with regex pattern
|
||||
# [[processors.regex]]
|
||||
# ## Tag and field conversions defined in a separate sub-tables
|
||||
# # [[processors.regex.tags]]
|
||||
# # ## Tag to change
|
||||
# # key = "resp_code"
|
||||
# # ## Regular expression to match on a tag value
|
||||
# # pattern = "^(\\d)\\d\\d$"
|
||||
# # ## Pattern for constructing a new value (${1} represents first subgroup)
|
||||
# # replacement = "${1}xx"
|
||||
#
|
||||
# # [[processors.regex.fields]]
|
||||
# # key = "request"
|
||||
# # ## All the power of the Go regular expressions available here
|
||||
# # ## For example, named subgroups
|
||||
# # pattern = "^/api(?P<method>/[\\w/]+)\\S*"
|
||||
# # replacement = "${method}"
|
||||
# # ## If result_key is present, a new field will be created
|
||||
# # ## instead of changing existing field
|
||||
# # result_key = "method"
|
||||
#
|
||||
# ## Multiple conversions may be applied for one field sequentially
|
||||
# ## Let's extract one more value
|
||||
# # [[processors.regex.fields]]
|
||||
# # key = "request"
|
||||
# # pattern = ".*category=(\\w+).*"
|
||||
# # replacement = "${1}"
|
||||
# # result_key = "search_category"
|
||||
|
||||
|
||||
# # Print all metrics that pass through this filter.
|
||||
# [[processors.topk]]
|
||||
# ## How many seconds between aggregations
|
||||
@@ -1027,6 +1138,17 @@
|
||||
# ## This plugin will query all namespaces the aerospike
|
||||
# ## server has configured and get stats for them.
|
||||
# servers = ["localhost:3000"]
|
||||
#
|
||||
# # username = "telegraf"
|
||||
# # password = "pa$$word"
|
||||
#
|
||||
# ## Optional TLS Config
|
||||
# # enable_tls = false
|
||||
# # tls_ca = "/etc/telegraf/ca.pem"
|
||||
# # tls_cert = "/etc/telegraf/cert.pem"
|
||||
# # tls_key = "/etc/telegraf/key.pem"
|
||||
# ## If false, skip chain & host verification
|
||||
# # insecure_skip_verify = true
|
||||
|
||||
|
||||
# # Read Apache status information (mod_status)
|
||||
@@ -1051,6 +1173,32 @@
|
||||
# # insecure_skip_verify = false
|
||||
|
||||
|
||||
# # Gather metrics from Apache Aurora schedulers
|
||||
# [[inputs.aurora]]
|
||||
# ## Schedulers are the base addresses of your Aurora Schedulers
|
||||
# schedulers = ["http://127.0.0.1:8081"]
|
||||
#
|
||||
# ## Set of role types to collect metrics from.
|
||||
# ##
|
||||
# ## The scheduler roles are checked each interval by contacting the
|
||||
# ## scheduler nodes; zookeeper is not contacted.
|
||||
# # roles = ["leader", "follower"]
|
||||
#
|
||||
# ## Timeout is the max time for total network operations.
|
||||
# # timeout = "5s"
|
||||
#
|
||||
# ## Username and password are sent using HTTP Basic Auth.
|
||||
# # username = "username"
|
||||
# # password = "pa$$word"
|
||||
#
|
||||
# ## Optional TLS Config
|
||||
# # tls_ca = "/etc/telegraf/ca.pem"
|
||||
# # tls_cert = "/etc/telegraf/cert.pem"
|
||||
# # tls_key = "/etc/telegraf/key.pem"
|
||||
# ## Use TLS but skip chain & host verification
|
||||
# # insecure_skip_verify = false
|
||||
|
||||
|
||||
# # Read metrics of bcache from stats_total and dirty_data
|
||||
# [[inputs.bcache]]
|
||||
# ## Bcache sets path
|
||||
@@ -1075,6 +1223,49 @@
|
||||
# # bond_interfaces = ["bond0"]
|
||||
|
||||
|
||||
# # Collect Kafka topics and consumers status from Burrow HTTP API.
|
||||
# [[inputs.burrow]]
|
||||
# ## Burrow API endpoints in format "schema://host:port".
|
||||
# ## Default is "http://localhost:8000".
|
||||
# servers = ["http://localhost:8000"]
|
||||
#
|
||||
# ## Override Burrow API prefix.
|
||||
# ## Useful when Burrow is behind reverse-proxy.
|
||||
# # api_prefix = "/v3/kafka"
|
||||
#
|
||||
# ## Maximum time to receive response.
|
||||
# # response_timeout = "5s"
|
||||
#
|
||||
# ## Limit per-server concurrent connections.
|
||||
# ## Useful in case of large number of topics or consumer groups.
|
||||
# # concurrent_connections = 20
|
||||
#
|
||||
# ## Filter clusters, default is no filtering.
|
||||
# ## Values can be specified as glob patterns.
|
||||
# # clusters_include = []
|
||||
# # clusters_exclude = []
|
||||
#
|
||||
# ## Filter consumer groups, default is no filtering.
|
||||
# ## Values can be specified as glob patterns.
|
||||
# # groups_include = []
|
||||
# # groups_exclude = []
|
||||
#
|
||||
# ## Filter topics, default is no filtering.
|
||||
# ## Values can be specified as glob patterns.
|
||||
# # topics_include = []
|
||||
# # topics_exclude = []
|
||||
#
|
||||
# ## Credentials for basic HTTP authentication.
|
||||
# # username = ""
|
||||
# # password = ""
|
||||
#
|
||||
# ## Optional SSL config
|
||||
# # ssl_ca = "/etc/telegraf/ca.pem"
|
||||
# # ssl_cert = "/etc/telegraf/cert.pem"
|
||||
# # ssl_key = "/etc/telegraf/key.pem"
|
||||
# # insecure_skip_verify = false
|
||||
|
||||
|
||||
# # Collects performance metrics from the MON and OSD nodes in a Ceph storage cluster.
|
||||
# [[inputs.ceph]]
|
||||
# ## This is the recommended interval to poll. Too frequent and you will lose
|
||||
@@ -2596,6 +2787,9 @@
|
||||
# ## Remove numbers from field names.
|
||||
# ## If true, a field name like 'temp1_input' will be changed to 'temp_input'.
|
||||
# # remove_numbers = true
|
||||
#
|
||||
# ## Timeout is the maximum amount of time that the sensors command can run.
|
||||
# # timeout = "5s"
|
||||
|
||||
|
||||
# # Read metrics from storage devices supporting S.M.A.R.T.
|
||||
@@ -2947,23 +3141,27 @@
|
||||
# pools = ["redis_pool", "mc_pool"]
|
||||
|
||||
|
||||
# # A plugin to collect stats from Unbound - a validating, recursive, and caching DNS resolver
|
||||
# # A plugin to collect stats from the Unbound DNS resolver
|
||||
# [[inputs.unbound]]
|
||||
# ## If running as a restricted user you can prepend sudo for additional access:
|
||||
# #use_sudo = false
|
||||
#
|
||||
# ## The default location of the unbound-control binary can be overridden with:
|
||||
# binary = "/usr/sbin/unbound-control"
|
||||
#
|
||||
# ## The default timeout of 1s can be overriden with:
|
||||
# timeout = "1s"
|
||||
#
|
||||
# ## Use the builtin fielddrop/fieldpass telegraf filters in order to keep/remove specific fields
|
||||
# fieldpass = ["total_*", "num_*","time_up", "mem_*"]
|
||||
#
|
||||
# ## IP of server to connect to, read from unbound conf default, optionally ':port'
|
||||
# ## Address of server to connect to, read from unbound conf default, optionally ':port'
|
||||
# ## Will lookup IP if given a hostname
|
||||
# server = "127.0.0.1:8953"
|
||||
#
|
||||
# ## If running as a restricted user you can prepend sudo for additional access:
|
||||
# # use_sudo = false
|
||||
#
|
||||
# ## The default location of the unbound-control binary can be overridden with:
|
||||
# # binary = "/usr/sbin/unbound-control"
|
||||
#
|
||||
# ## The default timeout of 1s can be overriden with:
|
||||
# # timeout = "1s"
|
||||
#
|
||||
# ## When set to true, thread metrics are tagged with the thread id.
|
||||
# ##
|
||||
# ## The default is false for backwards compatibility, and will be change to
|
||||
# ## true in a future version. It is recommended to set to true on new
|
||||
# ## deployments.
|
||||
# thread_as_tag = false
|
||||
|
||||
|
||||
# # A plugin to collect stats from Varnish HTTP Cache
|
||||
@@ -2982,7 +3180,7 @@
|
||||
#
|
||||
# ## Optional name for the varnish instance (or working directory) to query
|
||||
# ## Usually appened after -n in varnish cli
|
||||
# #name = instanceName
|
||||
# # instance_name = instanceName
|
||||
|
||||
|
||||
# # Read metrics of ZFS from arcstats, zfetchstats, vdev_cache_stats, and pools
|
||||
@@ -3029,17 +3227,43 @@
|
||||
|
||||
# # AMQP consumer plugin
|
||||
# [[inputs.amqp_consumer]]
|
||||
# ## AMQP url
|
||||
# url = "amqp://localhost:5672/influxdb"
|
||||
# ## AMQP exchange
|
||||
# ## Broker to consume from.
|
||||
# ## deprecated in 1.7; use the brokers option
|
||||
# # url = "amqp://localhost:5672/influxdb"
|
||||
#
|
||||
# ## Brokers to consume from. If multiple brokers are specified a random broker
|
||||
# ## will be selected anytime a connection is established. This can be
|
||||
# ## helpful for load balancing when not using a dedicated load balancer.
|
||||
# brokers = ["amqp://localhost:5672/influxdb"]
|
||||
#
|
||||
# ## Authentication credentials for the PLAIN auth_method.
|
||||
# # username = ""
|
||||
# # password = ""
|
||||
#
|
||||
# ## Exchange to declare and consume from.
|
||||
# exchange = "telegraf"
|
||||
#
|
||||
# ## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
|
||||
# # exchange_type = "topic"
|
||||
#
|
||||
# ## If true, exchange will be passively declared.
|
||||
# # exchange_passive = false
|
||||
#
|
||||
# ## Exchange durability can be either "transient" or "durable".
|
||||
# # exchange_durability = "durable"
|
||||
#
|
||||
# ## Additional exchange arguments.
|
||||
# # exchange_arguments = { }
|
||||
# # exchange_arguments = {"hash_propery" = "timestamp"}
|
||||
#
|
||||
# ## AMQP queue name
|
||||
# queue = "telegraf"
|
||||
#
|
||||
# ## Binding Key
|
||||
# binding_key = "#"
|
||||
#
|
||||
# ## Maximum number of messages server should give to the worker.
|
||||
# prefetch_count = 50
|
||||
# # prefetch_count = 50
|
||||
#
|
||||
# ## Auth method. PLAIN and EXTERNAL are supported
|
||||
# ## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as
|
||||
@@ -3560,6 +3784,46 @@
|
||||
# percentile_limit = 1000
|
||||
|
||||
|
||||
# # Accepts syslog messages per RFC5425
|
||||
# [[inputs.syslog]]
|
||||
# ## Specify an ip or hostname with port - eg., tcp://localhost:6514, tcp://10.0.0.1:6514
|
||||
# ## Protocol, address and port to host the syslog receiver.
|
||||
# ## If no host is specified, then localhost is used.
|
||||
# ## If no port is specified, 6514 is used (RFC5425#section-4.1).
|
||||
# server = "tcp://:6514"
|
||||
#
|
||||
# ## TLS Config
|
||||
# # tls_allowed_cacerts = ["/etc/telegraf/ca.pem"]
|
||||
# # tls_cert = "/etc/telegraf/cert.pem"
|
||||
# # tls_key = "/etc/telegraf/key.pem"
|
||||
#
|
||||
# ## Period between keep alive probes.
|
||||
# ## 0 disables keep alive probes.
|
||||
# ## Defaults to the OS configuration.
|
||||
# ## Only applies to stream sockets (e.g. TCP).
|
||||
# # keep_alive_period = "5m"
|
||||
#
|
||||
# ## Maximum number of concurrent connections (default = 0).
|
||||
# ## 0 means unlimited.
|
||||
# ## Only applies to stream sockets (e.g. TCP).
|
||||
# # max_connections = 1024
|
||||
#
|
||||
# ## Read timeout (default = 500ms).
|
||||
# ## 0 means unlimited.
|
||||
# # read_timeout = 500ms
|
||||
#
|
||||
# ## Whether to parse in best effort mode or not (default = false).
|
||||
# ## By default best effort parsing is off.
|
||||
# # best_effort = false
|
||||
#
|
||||
# ## Character to prepend to SD-PARAMs (default = "_").
|
||||
# ## A syslog message can contain multiple parameters and multiple identifiers within structured data section.
|
||||
# ## Eg., [id1 name1="val1" name2="val2"][id2 name1="val1" nameA="valA"]
|
||||
# ## For each combination a field is created.
|
||||
# ## Its name is created concatenating identifier, sdparam_separator, and parameter name.
|
||||
# # sdparam_separator = "_"
|
||||
|
||||
|
||||
# # Stream a log file, like the tail -f command
|
||||
# [[inputs.tail]]
|
||||
# ## files to tail.
|
||||
|
||||
@@ -156,6 +156,24 @@ func (c *Config) InputNames() []string {
|
||||
return name
|
||||
}
|
||||
|
||||
// Outputs returns a list of strings of the configured aggregators.
|
||||
func (c *Config) AggregatorNames() []string {
|
||||
var name []string
|
||||
for _, aggregator := range c.Aggregators {
|
||||
name = append(name, aggregator.Name())
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Outputs returns a list of strings of the configured processors.
|
||||
func (c *Config) ProcessorNames() []string {
|
||||
var name []string
|
||||
for _, processor := range c.Processors {
|
||||
name = append(name, processor.Name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Outputs returns a list of strings of the configured outputs.
|
||||
func (c *Config) OutputNames() []string {
|
||||
var name []string
|
||||
|
||||
@@ -17,7 +17,7 @@ type ClientConfig struct {
|
||||
// Deprecated in 1.7; use TLS variables above
|
||||
SSLCA string `toml:"ssl_ca"`
|
||||
SSLCert string `toml:"ssl_cert"`
|
||||
SSLKey string `toml:"ssl_ca"`
|
||||
SSLKey string `toml:"ssl_key"`
|
||||
}
|
||||
|
||||
// ServerConfig represents the standard server TLS config.
|
||||
|
||||
@@ -97,6 +97,7 @@ import (
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/solr"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/syslog"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/sysstat"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/system"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/tail"
|
||||
|
||||
@@ -15,23 +15,48 @@ 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
|
||||
## Broker to consume from.
|
||||
## deprecated in 1.7; use the brokers option
|
||||
# url = "amqp://localhost:5672/influxdb"
|
||||
|
||||
## Brokers to consume from. If multiple brokers are specified a random broker
|
||||
## will be selected anytime a connection is established. This can be
|
||||
## helpful for load balancing when not using a dedicated load balancer.
|
||||
brokers = ["amqp://localhost:5672/influxdb"]
|
||||
|
||||
## Authentication credentials for the PLAIN auth_method.
|
||||
# username = ""
|
||||
# password = ""
|
||||
|
||||
## Exchange to declare and consume from.
|
||||
exchange = "telegraf"
|
||||
|
||||
## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
|
||||
# exchange_type = "topic"
|
||||
|
||||
## If true, exchange will be passively declared.
|
||||
# exchange_passive = false
|
||||
|
||||
## Exchange durability can be either "transient" or "durable".
|
||||
# exchange_durability = "durable"
|
||||
|
||||
## Additional exchange arguments.
|
||||
# exchange_arguments = { }
|
||||
# exchange_arguments = {"hash_propery" = "timestamp"}
|
||||
|
||||
## 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
|
||||
## Maximum number of messages server should give to the worker.
|
||||
# prefetch_count = 50
|
||||
|
||||
## Auth method. PLAIN and EXTERNAL are supported.
|
||||
## 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 TLS Config
|
||||
# tls_ca = "/etc/telegraf/ca.pem"
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package amqp_consumer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -17,9 +19,16 @@ import (
|
||||
|
||||
// AMQPConsumer is the top level struct for this plugin
|
||||
type AMQPConsumer struct {
|
||||
URL string
|
||||
// AMQP exchange
|
||||
Exchange string
|
||||
URL string `toml:"url"` // deprecated in 1.7; use brokers
|
||||
Brokers []string `toml:"brokers"`
|
||||
Username string `toml:"username"`
|
||||
Password string `toml:"password"`
|
||||
Exchange string `toml:"exchange"`
|
||||
ExchangeType string `toml:"exchange_type"`
|
||||
ExchangeDurability string `toml:"exchange_durability"`
|
||||
ExchangePassive bool `toml:"exchange_passive"`
|
||||
ExchangeArguments map[string]string `toml:"exchange_arguments"`
|
||||
|
||||
// Queue Name
|
||||
Queue string
|
||||
// Binding Key
|
||||
@@ -48,23 +57,55 @@ func (a *externalAuth) Response() string {
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultAuthMethod = "PLAIN"
|
||||
DefaultAuthMethod = "PLAIN"
|
||||
|
||||
DefaultBroker = "amqp://localhost:5672/influxdb"
|
||||
|
||||
DefaultExchangeType = "topic"
|
||||
DefaultExchangeDurability = "durable"
|
||||
|
||||
DefaultPrefetchCount = 50
|
||||
)
|
||||
|
||||
func (a *AMQPConsumer) SampleConfig() string {
|
||||
return `
|
||||
## AMQP url
|
||||
url = "amqp://localhost:5672/influxdb"
|
||||
## AMQP exchange
|
||||
## Broker to consume from.
|
||||
## deprecated in 1.7; use the brokers option
|
||||
# url = "amqp://localhost:5672/influxdb"
|
||||
|
||||
## Brokers to consume from. If multiple brokers are specified a random broker
|
||||
## will be selected anytime a connection is established. This can be
|
||||
## helpful for load balancing when not using a dedicated load balancer.
|
||||
brokers = ["amqp://localhost:5672/influxdb"]
|
||||
|
||||
## Authentication credentials for the PLAIN auth_method.
|
||||
# username = ""
|
||||
# password = ""
|
||||
|
||||
## Exchange to declare and consume from.
|
||||
exchange = "telegraf"
|
||||
|
||||
## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
|
||||
# exchange_type = "topic"
|
||||
|
||||
## If true, exchange will be passively declared.
|
||||
# exchange_passive = false
|
||||
|
||||
## Exchange durability can be either "transient" or "durable".
|
||||
# exchange_durability = "durable"
|
||||
|
||||
## Additional exchange arguments.
|
||||
# exchange_arguments = { }
|
||||
# exchange_arguments = {"hash_propery" = "timestamp"}
|
||||
|
||||
## AMQP queue name
|
||||
queue = "telegraf"
|
||||
|
||||
## Binding Key
|
||||
binding_key = "#"
|
||||
|
||||
## Maximum number of messages server should give to the worker.
|
||||
prefetch_count = 50
|
||||
# prefetch_count = 50
|
||||
|
||||
## Auth method. PLAIN and EXTERNAL are supported
|
||||
## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as
|
||||
@@ -106,16 +147,21 @@ func (a *AMQPConsumer) createConfig() (*amqp.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// parse auth method
|
||||
var sasl []amqp.Authentication // nil by default
|
||||
|
||||
var auth []amqp.Authentication
|
||||
if strings.ToUpper(a.AuthMethod) == "EXTERNAL" {
|
||||
sasl = []amqp.Authentication{&externalAuth{}}
|
||||
auth = []amqp.Authentication{&externalAuth{}}
|
||||
} else if a.Username != "" || a.Password != "" {
|
||||
auth = []amqp.Authentication{
|
||||
&amqp.PlainAuth{
|
||||
Username: a.Username,
|
||||
Password: a.Password,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
config := amqp.Config{
|
||||
TLSClientConfig: tls,
|
||||
SASL: sasl, // if nil, it will be PLAIN
|
||||
SASL: auth, // if nil, it will be PLAIN
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
@@ -163,28 +209,55 @@ func (a *AMQPConsumer) Start(acc telegraf.Accumulator) error {
|
||||
}
|
||||
|
||||
func (a *AMQPConsumer) connect(amqpConf *amqp.Config) (<-chan amqp.Delivery, error) {
|
||||
conn, err := amqp.DialConfig(a.URL, *amqpConf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
brokers := a.Brokers
|
||||
if len(brokers) == 0 {
|
||||
brokers = []string{a.URL}
|
||||
}
|
||||
a.conn = conn
|
||||
|
||||
ch, err := conn.Channel()
|
||||
p := rand.Perm(len(brokers))
|
||||
for _, n := range p {
|
||||
broker := brokers[n]
|
||||
log.Printf("D! [amqp_consumer] connecting to %q", broker)
|
||||
conn, err := amqp.DialConfig(broker, *amqpConf)
|
||||
if err == nil {
|
||||
a.conn = conn
|
||||
log.Printf("D! [amqp_consumer] connected to %q", broker)
|
||||
break
|
||||
}
|
||||
log.Printf("D! [amqp_consumer] error connecting to %q", broker)
|
||||
}
|
||||
|
||||
if a.conn == nil {
|
||||
return nil, errors.New("could not connect to any broker")
|
||||
}
|
||||
|
||||
ch, err := a.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
|
||||
)
|
||||
var exchangeDurable = true
|
||||
switch a.ExchangeDurability {
|
||||
case "transient":
|
||||
exchangeDurable = false
|
||||
default:
|
||||
exchangeDurable = true
|
||||
}
|
||||
|
||||
exchangeArgs := make(amqp.Table, len(a.ExchangeArguments))
|
||||
for k, v := range a.ExchangeArguments {
|
||||
exchangeArgs[k] = v
|
||||
}
|
||||
|
||||
err = declareExchange(
|
||||
ch,
|
||||
a.Exchange,
|
||||
a.ExchangeType,
|
||||
a.ExchangePassive,
|
||||
exchangeDurable,
|
||||
exchangeArgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to declare an exchange: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q, err := ch.QueueDeclare(
|
||||
@@ -236,6 +309,42 @@ func (a *AMQPConsumer) connect(amqpConf *amqp.Config) (<-chan amqp.Delivery, err
|
||||
return msgs, err
|
||||
}
|
||||
|
||||
func declareExchange(
|
||||
channel *amqp.Channel,
|
||||
exchangeName string,
|
||||
exchangeType string,
|
||||
exchangePassive bool,
|
||||
exchangeDurable bool,
|
||||
exchangeArguments amqp.Table,
|
||||
) error {
|
||||
var err error
|
||||
if exchangePassive {
|
||||
err = channel.ExchangeDeclarePassive(
|
||||
exchangeName,
|
||||
exchangeType,
|
||||
exchangeDurable,
|
||||
false, // delete when unused
|
||||
false, // internal
|
||||
false, // no-wait
|
||||
exchangeArguments,
|
||||
)
|
||||
} else {
|
||||
err = channel.ExchangeDeclare(
|
||||
exchangeName,
|
||||
exchangeType,
|
||||
exchangeDurable,
|
||||
false, // delete when unused
|
||||
false, // internal
|
||||
false, // no-wait
|
||||
exchangeArguments,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error declaring exchange: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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()
|
||||
@@ -267,8 +376,11 @@ func (a *AMQPConsumer) Stop() {
|
||||
func init() {
|
||||
inputs.Add("amqp_consumer", func() telegraf.Input {
|
||||
return &AMQPConsumer{
|
||||
AuthMethod: DefaultAuthMethod,
|
||||
PrefetchCount: DefaultPrefetchCount,
|
||||
URL: DefaultBroker,
|
||||
AuthMethod: DefaultAuthMethod,
|
||||
ExchangeType: DefaultExchangeType,
|
||||
ExchangeDurability: DefaultExchangeDurability,
|
||||
PrefetchCount: DefaultPrefetchCount,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ Supported Burrow version: `1.x`
|
||||
### Configuration
|
||||
|
||||
```
|
||||
[[inputs.burrow]]
|
||||
## Burrow API endpoints in format "schema://host:port".
|
||||
## Default is "http://localhost:8000".
|
||||
servers = ["http://localhost:8000"]
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
package conntrack
|
||||
|
||||
import (
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func restoreDflts(savedFiles, savedDirs []string) {
|
||||
@@ -39,6 +40,7 @@ func TestDefaultsUsed(t *testing.T) {
|
||||
|
||||
tmpFile, err := ioutil.TempFile(tmpdir, "ip_conntrack_count")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
dfltDirs = []string{tmpdir}
|
||||
fname := path.Base(tmpFile.Name())
|
||||
@@ -63,6 +65,8 @@ func TestConfigsUsed(t *testing.T) {
|
||||
cntFile, err := ioutil.TempFile(tmpdir, "nf_conntrack_count")
|
||||
maxFile, err := ioutil.TempFile(tmpdir, "nf_conntrack_max")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(cntFile.Name())
|
||||
defer os.Remove(maxFile.Name())
|
||||
|
||||
dfltDirs = []string{tmpdir}
|
||||
cntFname := path.Base(cntFile.Name())
|
||||
|
||||
@@ -172,8 +172,14 @@ Both `jolokia2_agent` and `jolokia2_proxy` plugins support default configuration
|
||||
|
||||
### Example Configurations:
|
||||
|
||||
- [Java JVM](/plugins/inputs/jolokia2/examples/java.conf)
|
||||
- [Kafka](/plugins/inputs/jolokia2/examples/kafka.conf)
|
||||
- [ActiveMQ](/plugins/inputs/jolokia2/examples/activemq.conf)
|
||||
- [BitBucket](/plugins/inputs/jolokia2/examples/bitbucket.conf)
|
||||
- [Cassandra](/plugins/inputs/jolokia2/examples/cassandra.conf)
|
||||
- [Hadoop-HDFS](/plugins/inputs/jolokia2/examples/hadoop-hdfs.conf)
|
||||
- [Java JVM](/plugins/inputs/jolokia2/examples/java.conf)
|
||||
- [JBoss](/plugins/inputs/jolokia2/examples/jboss.conf)
|
||||
- [Kafka](/plugins/inputs/jolokia2/examples/kafka.conf)
|
||||
- [Tomcat](/plugins/inputs/jolokia2/examples/tomcat.conf)
|
||||
- [Weblogic](/plugins/inputs/jolokia2/examples/weblogic.conf)
|
||||
|
||||
Please help improve this list and contribute new configuration files by opening an issue or pull request.
|
||||
|
||||
57
plugins/inputs/jolokia2/examples/activemq.conf
Normal file
57
plugins/inputs/jolokia2/examples/activemq.conf
Normal file
@@ -0,0 +1,57 @@
|
||||
## Jolokia is bundled with ActiveMQ
|
||||
|
||||
[[inputs.jolokia2_agent]]
|
||||
urls = ["http://localhost:8161/api/jolokia"]
|
||||
name_prefix = "activemq."
|
||||
username = "admin"
|
||||
password = "admin"
|
||||
|
||||
### JVM Generic
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "OperatingSystem"
|
||||
mbean = "java.lang:type=OperatingSystem"
|
||||
paths = ["ProcessCpuLoad","SystemLoadAverage","SystemCpuLoad"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_runtime"
|
||||
mbean = "java.lang:type=Runtime"
|
||||
paths = ["Uptime"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory"
|
||||
mbean = "java.lang:type=Memory"
|
||||
paths = ["HeapMemoryUsage", "NonHeapMemoryUsage", "ObjectPendingFinalizationCount"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_garbage_collector"
|
||||
mbean = "java.lang:name=*,type=GarbageCollector"
|
||||
paths = ["CollectionTime", "CollectionCount"]
|
||||
tag_keys = ["name"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory_pool"
|
||||
mbean = "java.lang:name=*,type=MemoryPool"
|
||||
paths = ["Usage", "PeakUsage", "CollectionUsage"]
|
||||
tag_keys = ["name"]
|
||||
tag_prefix = "pool_"
|
||||
|
||||
### ACTIVEMQ
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "queue"
|
||||
mbean = "org.apache.activemq:brokerName=*,destinationName=*,destinationType=Queue,type=Broker"
|
||||
paths = ["QueueSize","EnqueueCount","ConsumerCount","DispatchCount","DequeueCount","ProducerCount","InFlightCount"]
|
||||
tag_keys = ["brokerName","destinationName"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "topic"
|
||||
mbean = "org.apache.activemq:brokerName=*,destinationName=*,destinationType=Topic,type=Broker"
|
||||
paths = ["ProducerCount","DequeueCount","ConsumerCount","QueueSize","EnqueueCount"]
|
||||
tag_keys = ["brokerName","destinationName"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "broker"
|
||||
mbean = "org.apache.activemq:brokerName=*,type=Broker"
|
||||
paths = ["TotalConsumerCount","TotalMessageCount","TotalEnqueueCount","TotalDequeueCount","MemoryLimit","MemoryPercentUsage","StoreLimit","StorePercentUsage","TempPercentUsage","TempLimit"]
|
||||
tag_keys = ["brokerName"]
|
||||
39
plugins/inputs/jolokia2/examples/bitbucket.conf
Normal file
39
plugins/inputs/jolokia2/examples/bitbucket.conf
Normal file
@@ -0,0 +1,39 @@
|
||||
[[inputs.jolokia2_agent]]
|
||||
urls = ["http://localhost:8778/jolokia"]
|
||||
name_prefix = "bitbucket."
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_operatingsystem"
|
||||
mbean = "java.lang:type=OperatingSystem"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_runtime"
|
||||
mbean = "java.lang:type=Runtime"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_thread"
|
||||
mbean = "java.lang:type=Threading"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory"
|
||||
mbean = "java.lang:type=Memory"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_class_loading"
|
||||
mbean = "java.lang:type=ClassLoading"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory_pool"
|
||||
mbean = "java.lang:type=MemoryPool,name=*"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "webhooks"
|
||||
mbean = "com.atlassian.webhooks:name=*"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "atlassian"
|
||||
mbean = "com.atlassian.bitbucket:name=*"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "thread_pools"
|
||||
mbean = "com.atlassian.bitbucket.thread-pools:name=*"
|
||||
85
plugins/inputs/jolokia2/examples/hadooop-hdfs.conf
Normal file
85
plugins/inputs/jolokia2/examples/hadooop-hdfs.conf
Normal file
@@ -0,0 +1,85 @@
|
||||
################
|
||||
# NAMENODE #
|
||||
################
|
||||
[[inputs.jolokia2_agent]]
|
||||
urls = ["http://localhost:8778/jolokia"]
|
||||
name_prefix = "hadoop.hdfs.namenode."
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "FSNamesystem"
|
||||
mbean = "Hadoop:name=FSNamesystem,service=NameNode"
|
||||
paths = ["CapacityTotal", "CapacityRemaining", "CapacityUsedNonDFS", "NumLiveDataNodes", "NumDeadDataNodes", "NumInMaintenanceDeadDataNodes", "NumDecomDeadDataNodes"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "FSNamesystemState"
|
||||
mbean = "Hadoop:name=FSNamesystemState,service=NameNode"
|
||||
paths = ["VolumeFailuresTotal", "UnderReplicatedBlocks", "BlocksTotal"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "OperatingSystem"
|
||||
mbean = "java.lang:type=OperatingSystem"
|
||||
paths = ["ProcessCpuLoad", "SystemLoadAverage", "SystemCpuLoad"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_runtime"
|
||||
mbean = "java.lang:type=Runtime"
|
||||
paths = ["Uptime"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory"
|
||||
mbean = "java.lang:type=Memory"
|
||||
paths = ["HeapMemoryUsage", "NonHeapMemoryUsage", "ObjectPendingFinalizationCount"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_garbage_collector"
|
||||
mbean = "java.lang:name=*,type=GarbageCollector"
|
||||
paths = ["CollectionTime", "CollectionCount"]
|
||||
tag_keys = ["name"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory_pool"
|
||||
mbean = "java.lang:name=*,type=MemoryPool"
|
||||
paths = ["Usage", "PeakUsage", "CollectionUsage"]
|
||||
tag_keys = ["name"]
|
||||
tag_prefix = "pool_"
|
||||
|
||||
|
||||
################
|
||||
# DATANODE #
|
||||
################
|
||||
[[inputs.jolokia2_agent]]
|
||||
urls = ["http://localhost:7778/jolokia"]
|
||||
name_prefix = "hadoop.hdfs.datanode."
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "FSDatasetState"
|
||||
mbean = "Hadoop:name=FSDatasetState,service=DataNode"
|
||||
paths = ["Capacity", "DfsUsed", "Remaining", "NumBlocksFailedToUnCache", "NumBlocksFailedToCache", "NumBlocksCached"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "OperatingSystem"
|
||||
mbean = "java.lang:type=OperatingSystem"
|
||||
paths = ["ProcessCpuLoad", "SystemLoadAverage", "SystemCpuLoad"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_runtime"
|
||||
mbean = "java.lang:type=Runtime"
|
||||
paths = ["Uptime"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory"
|
||||
mbean = "java.lang:type=Memory"
|
||||
paths = ["HeapMemoryUsage", "NonHeapMemoryUsage", "ObjectPendingFinalizationCount"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_garbage_collector"
|
||||
mbean = "java.lang:name=*,type=GarbageCollector"
|
||||
paths = ["CollectionTime", "CollectionCount"]
|
||||
tag_keys = ["name"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory_pool"
|
||||
mbean = "java.lang:name=*,type=MemoryPool"
|
||||
paths = ["Usage", "PeakUsage", "CollectionUsage"]
|
||||
tag_keys = ["name"]
|
||||
tag_prefix = "pool_"
|
||||
59
plugins/inputs/jolokia2/examples/jboss.conf
Normal file
59
plugins/inputs/jolokia2/examples/jboss.conf
Normal file
@@ -0,0 +1,59 @@
|
||||
[[inputs.jolokia2_agent]]
|
||||
urls = ["http://localhost:8080/jolokia"]
|
||||
name_prefix = "jboss."
|
||||
|
||||
### JVM Generic
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "OperatingSystem"
|
||||
mbean = "java.lang:type=OperatingSystem"
|
||||
paths = ["ProcessCpuLoad","SystemLoadAverage","SystemCpuLoad"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_runtime"
|
||||
mbean = "java.lang:type=Runtime"
|
||||
paths = ["Uptime"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory"
|
||||
mbean = "java.lang:type=Memory"
|
||||
paths = ["HeapMemoryUsage", "NonHeapMemoryUsage", "ObjectPendingFinalizationCount"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_garbage_collector"
|
||||
mbean = "java.lang:name=*,type=GarbageCollector"
|
||||
paths = ["CollectionTime", "CollectionCount"]
|
||||
tag_keys = ["name"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory_pool"
|
||||
mbean = "java.lang:name=*,type=MemoryPool"
|
||||
paths = ["Usage", "PeakUsage", "CollectionUsage"]
|
||||
tag_keys = ["name"]
|
||||
tag_prefix = "pool_"
|
||||
|
||||
### JBOSS
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "connectors.http"
|
||||
mbean = "jboss.as:https-listener=*,server=*,subsystem=undertow"
|
||||
paths = ["bytesReceived","bytesSent","errorCount","requestCount"]
|
||||
tag_keys = ["server","https-listener"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "connectors.http"
|
||||
mbean = "jboss.as:http-listener=*,server=*,subsystem=undertow"
|
||||
paths = ["bytesReceived","bytesSent","errorCount","requestCount"]
|
||||
tag_keys = ["server","http-listener"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "datasource.jdbc"
|
||||
mbean = "jboss.as:data-source=*,statistics=jdbc,subsystem=datasources"
|
||||
paths = ["PreparedStatementCacheAccessCount","PreparedStatementCacheHitCount","PreparedStatementCacheMissCount"]
|
||||
tag_keys = ["data-source"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "datasource.pool"
|
||||
mbean = "jboss.as:data-source=*,statistics=pool,subsystem=datasources"
|
||||
paths = ["AvailableCount","ActiveCount","MaxUsedCount"]
|
||||
tag_keys = ["data-source"]
|
||||
65
plugins/inputs/jolokia2/examples/tomcat.conf
Normal file
65
plugins/inputs/jolokia2/examples/tomcat.conf
Normal file
@@ -0,0 +1,65 @@
|
||||
[[inputs.jolokia2_agent]]
|
||||
urls = ["http://localhost:8080/jolokia"]
|
||||
name_prefix = "tomcat."
|
||||
|
||||
### JVM Generic
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "OperatingSystem"
|
||||
mbean = "java.lang:type=OperatingSystem"
|
||||
paths = ["ProcessCpuLoad","SystemLoadAverage","SystemCpuLoad"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_runtime"
|
||||
mbean = "java.lang:type=Runtime"
|
||||
paths = ["Uptime"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory"
|
||||
mbean = "java.lang:type=Memory"
|
||||
paths = ["HeapMemoryUsage", "NonHeapMemoryUsage", "ObjectPendingFinalizationCount"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_garbage_collector"
|
||||
mbean = "java.lang:name=*,type=GarbageCollector"
|
||||
paths = ["CollectionTime", "CollectionCount"]
|
||||
tag_keys = ["name"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory_pool"
|
||||
mbean = "java.lang:name=*,type=MemoryPool"
|
||||
paths = ["Usage", "PeakUsage", "CollectionUsage"]
|
||||
tag_keys = ["name"]
|
||||
tag_prefix = "pool_"
|
||||
|
||||
### TOMCAT
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "GlobalRequestProcessor"
|
||||
mbean = "Catalina:name=*,type=GlobalRequestProcessor"
|
||||
paths = ["requestCount","bytesReceived","bytesSent","processingTime","errorCount"]
|
||||
tag_keys = ["name"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "JspMonitor"
|
||||
mbean = "Catalina:J2EEApplication=*,J2EEServer=*,WebModule=*,name=jsp,type=JspMonitor"
|
||||
paths = ["jspReloadCount","jspCount","jspUnloadCount"]
|
||||
tag_keys = ["J2EEApplication","J2EEServer","WebModule"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "ThreadPool"
|
||||
mbean = "Catalina:name=*,type=ThreadPool"
|
||||
paths = ["maxThreads","currentThreadCount","currentThreadsBusy"]
|
||||
tag_keys = ["name"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "Servlet"
|
||||
mbean = "Catalina:J2EEApplication=*,J2EEServer=*,WebModule=*,j2eeType=Servlet,name=*"
|
||||
paths = ["processingTime","errorCount","requestCount"]
|
||||
tag_keys = ["name","J2EEApplication","J2EEServer","WebModule"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "Cache"
|
||||
mbean = "Catalina:context=*,host=*,name=Cache,type=WebResourceRoot"
|
||||
paths = ["hitCount","lookupCount"]
|
||||
tag_keys = ["context","host"]
|
||||
56
plugins/inputs/jolokia2/examples/weblogic.conf
Normal file
56
plugins/inputs/jolokia2/examples/weblogic.conf
Normal file
@@ -0,0 +1,56 @@
|
||||
[[inputs.jolokia2_agent]]
|
||||
urls = ["http://localhost:8080/jolokia"]
|
||||
name_prefix = "weblogic."
|
||||
|
||||
### JVM Generic
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "OperatingSystem"
|
||||
mbean = "java.lang:type=OperatingSystem"
|
||||
paths = ["ProcessCpuLoad","SystemLoadAverage","SystemCpuLoad"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_runtime"
|
||||
mbean = "java.lang:type=Runtime"
|
||||
paths = ["Uptime"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory"
|
||||
mbean = "java.lang:type=Memory"
|
||||
paths = ["HeapMemoryUsage", "NonHeapMemoryUsage", "ObjectPendingFinalizationCount"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_garbage_collector"
|
||||
mbean = "java.lang:name=*,type=GarbageCollector"
|
||||
paths = ["CollectionTime", "CollectionCount"]
|
||||
tag_keys = ["name"]
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "jvm_memory_pool"
|
||||
mbean = "java.lang:name=*,type=MemoryPool"
|
||||
paths = ["Usage", "PeakUsage", "CollectionUsage"]
|
||||
tag_keys = ["name"]
|
||||
tag_prefix = "pool_"
|
||||
|
||||
### WLS
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "JTARuntime"
|
||||
mbean = "com.bea:Name=JTARuntime,ServerRuntime=*,Type=JTARuntime"
|
||||
paths = ["SecondsActiveTotalCount","TransactionRolledBackTotalCount","TransactionRolledBackSystemTotalCount","TransactionRolledBackAppTotalCount","TransactionRolledBackResourceTotalCount","TransactionHeuristicsTotalCount","TransactionAbandonedTotalCount","TransactionTotalCount","TransactionRolledBackTimeoutTotalCount","ActiveTransactionsTotalCount","TransactionCommittedTotalCount"]
|
||||
tag_keys = ["ServerRuntime"]
|
||||
tag_prefix = "wls_"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "ThreadPoolRuntime"
|
||||
mbean = "com.bea:Name=ThreadPoolRuntime,ServerRuntime=*,Type=ThreadPoolRuntime"
|
||||
paths = ["StuckThreadCount","CompletedRequestCount","ExecuteThreadTotalCount","ExecuteThreadIdleCount","StandbyThreadCount","Throughput","HoggingThreadCount","PendingUserRequestCount"]
|
||||
tag_keys = ["ServerRuntime"]
|
||||
tag_prefix = "wls_"
|
||||
|
||||
[[inputs.jolokia2_agent.metric]]
|
||||
name = "JMSRuntime"
|
||||
mbean = "com.bea:Name=*.jms,ServerRuntime=*,Type=JMSRuntime"
|
||||
paths = ["ConnectionsCurrentCount","ConnectionsHighCount","ConnectionsTotalCount","JMSServersCurrentCount","JMSServersHighCount","JMSServersTotalCount"]
|
||||
tag_keys = ["name","ServerRuntime"]
|
||||
tag_prefix = "wls_"
|
||||
@@ -14,7 +14,7 @@ To use this plugin you must enable the [monitoring](https://www.openldap.org/dev
|
||||
# ldaps, starttls, or no encryption. default is an empty string, disabling all encryption.
|
||||
# note that port will likely need to be changed to 636 for ldaps
|
||||
# valid options: "" | "starttls" | "ldaps"
|
||||
ssl = ""
|
||||
tls = ""
|
||||
|
||||
# skip peer certificate verification. Default is false.
|
||||
insecure_skip_verify = false
|
||||
|
||||
@@ -15,9 +15,11 @@ import (
|
||||
type Openldap struct {
|
||||
Host string
|
||||
Port int
|
||||
Ssl string
|
||||
SSL string `toml:"ssl"` // Deprecated in 1.7; use TLS
|
||||
TLS string `toml:"tls"`
|
||||
InsecureSkipVerify bool
|
||||
SslCa string
|
||||
SSLCA string `toml:"ssl_ca"` // Deprecated in 1.7; use TLSCA
|
||||
TLSCA string `toml:"tls_ca"`
|
||||
BindDn string
|
||||
BindPassword string
|
||||
ReverseMetricNames bool
|
||||
@@ -30,7 +32,7 @@ const sampleConfig string = `
|
||||
# ldaps, starttls, or no encryption. default is an empty string, disabling all encryption.
|
||||
# note that port will likely need to be changed to 636 for ldaps
|
||||
# valid options: "" | "starttls" | "ldaps"
|
||||
ssl = ""
|
||||
tls = ""
|
||||
|
||||
# skip peer certificate verification. Default is false.
|
||||
insecure_skip_verify = false
|
||||
@@ -70,9 +72,11 @@ func NewOpenldap() *Openldap {
|
||||
return &Openldap{
|
||||
Host: "localhost",
|
||||
Port: 389,
|
||||
Ssl: "",
|
||||
SSL: "",
|
||||
TLS: "",
|
||||
InsecureSkipVerify: false,
|
||||
SslCa: "",
|
||||
SSLCA: "",
|
||||
TLSCA: "",
|
||||
BindDn: "",
|
||||
BindPassword: "",
|
||||
ReverseMetricNames: false,
|
||||
@@ -81,12 +85,19 @@ func NewOpenldap() *Openldap {
|
||||
|
||||
// gather metrics
|
||||
func (o *Openldap) Gather(acc telegraf.Accumulator) error {
|
||||
if o.TLS == "" {
|
||||
o.TLS = o.SSL
|
||||
}
|
||||
if o.TLSCA == "" {
|
||||
o.TLSCA = o.SSLCA
|
||||
}
|
||||
|
||||
var err error
|
||||
var l *ldap.Conn
|
||||
if o.Ssl != "" {
|
||||
if o.TLS != "" {
|
||||
// build tls config
|
||||
clientTLSConfig := tls.ClientConfig{
|
||||
SSLCA: o.SslCa,
|
||||
TLSCA: o.TLSCA,
|
||||
InsecureSkipVerify: o.InsecureSkipVerify,
|
||||
}
|
||||
tlsConfig, err := clientTLSConfig.TLSConfig()
|
||||
@@ -94,13 +105,13 @@ func (o *Openldap) Gather(acc telegraf.Accumulator) error {
|
||||
acc.AddError(err)
|
||||
return nil
|
||||
}
|
||||
if o.Ssl == "ldaps" {
|
||||
if o.TLS == "ldaps" {
|
||||
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", o.Host, o.Port), tlsConfig)
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
return nil
|
||||
}
|
||||
} else if o.Ssl == "starttls" {
|
||||
} else if o.TLS == "starttls" {
|
||||
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", o.Host, o.Port))
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
@@ -108,7 +119,7 @@ func (o *Openldap) Gather(acc telegraf.Accumulator) error {
|
||||
}
|
||||
err = l.StartTLS(tlsConfig)
|
||||
} else {
|
||||
acc.AddError(fmt.Errorf("Invalid setting for ssl: %s", o.Ssl))
|
||||
acc.AddError(fmt.Errorf("Invalid setting for ssl: %s", o.TLS))
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package openldap
|
||||
|
||||
import (
|
||||
"gopkg.in/ldap.v2"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/ldap.v2"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -74,7 +75,7 @@ func TestOpenldapStartTLS(t *testing.T) {
|
||||
o := &Openldap{
|
||||
Host: testutil.GetLocalHost(),
|
||||
Port: 389,
|
||||
Ssl: "starttls",
|
||||
SSL: "starttls",
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
@@ -92,7 +93,7 @@ func TestOpenldapLDAPS(t *testing.T) {
|
||||
o := &Openldap{
|
||||
Host: testutil.GetLocalHost(),
|
||||
Port: 636,
|
||||
Ssl: "ldaps",
|
||||
SSL: "ldaps",
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ func TestOpenldapInvalidSSL(t *testing.T) {
|
||||
o := &Openldap{
|
||||
Host: testutil.GetLocalHost(),
|
||||
Port: 636,
|
||||
Ssl: "invalid",
|
||||
SSL: "invalid",
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ func TestOpenldapBind(t *testing.T) {
|
||||
o := &Openldap{
|
||||
Host: testutil.GetLocalHost(),
|
||||
Port: 389,
|
||||
Ssl: "",
|
||||
SSL: "",
|
||||
InsecureSkipVerify: true,
|
||||
BindDn: "cn=manager,cn=config",
|
||||
BindPassword: "secret",
|
||||
@@ -157,7 +158,7 @@ func TestOpenldapReverseMetrics(t *testing.T) {
|
||||
o := &Openldap{
|
||||
Host: testutil.GetLocalHost(),
|
||||
Port: 389,
|
||||
Ssl: "",
|
||||
SSL: "",
|
||||
InsecureSkipVerify: true,
|
||||
BindDn: "cn=manager,cn=config",
|
||||
BindPassword: "secret",
|
||||
|
||||
@@ -31,6 +31,21 @@ telegraf ALL=(root) NOPASSWD: /sbin/pfctl -s info
|
||||
- searches (integer, count)
|
||||
- inserts (integer, count)
|
||||
- removals (integer, count)
|
||||
- match (integer, count)
|
||||
- bad-offset (integer, count)
|
||||
- fragment (integer, count)
|
||||
- short (integer, count)
|
||||
- normalize (integer, count)
|
||||
- memory (integer, count)
|
||||
- bad-timestamp (integer, count)
|
||||
- congestion (integer, count)
|
||||
- ip-option (integer, count)
|
||||
- proto-cksum (integer, count)
|
||||
- state-mismatch (integer, count)
|
||||
- state-insert (integer, count)
|
||||
- state-limit (integer, count)
|
||||
- src-limit (integer, count)
|
||||
- synproxy (integer, count)
|
||||
|
||||
### Example Output:
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ func errMissingData(tag string) error {
|
||||
|
||||
type pfctlOutputStanza struct {
|
||||
HeaderRE *regexp.Regexp
|
||||
ParseFunc func([]string, telegraf.Accumulator) error
|
||||
ParseFunc func([]string, map[string]interface{}) error
|
||||
Found bool
|
||||
}
|
||||
|
||||
@@ -76,11 +76,16 @@ var pfctlOutputStanzas = []*pfctlOutputStanza{
|
||||
HeaderRE: regexp.MustCompile("^State Table"),
|
||||
ParseFunc: parseStateTable,
|
||||
},
|
||||
&pfctlOutputStanza{
|
||||
HeaderRE: regexp.MustCompile("^Counters"),
|
||||
ParseFunc: parseCounterTable,
|
||||
},
|
||||
}
|
||||
|
||||
var anyTableHeaderRE = regexp.MustCompile("^[A-Z]")
|
||||
|
||||
func (pf *PF) parsePfctlOutput(pfoutput string, acc telegraf.Accumulator) error {
|
||||
fields := make(map[string]interface{})
|
||||
scanner := bufio.NewScanner(strings.NewReader(pfoutput))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
@@ -91,10 +96,14 @@ func (pf *PF) parsePfctlOutput(pfoutput string, acc telegraf.Accumulator) error
|
||||
line = scanner.Text()
|
||||
for !anyTableHeaderRE.MatchString(line) {
|
||||
stanzaLines = append(stanzaLines, line)
|
||||
scanner.Scan()
|
||||
line = scanner.Text()
|
||||
more := scanner.Scan()
|
||||
if more {
|
||||
line = scanner.Text()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if perr := s.ParseFunc(stanzaLines, acc); perr != nil {
|
||||
if perr := s.ParseFunc(stanzaLines, fields); perr != nil {
|
||||
return perr
|
||||
}
|
||||
s.Found = true
|
||||
@@ -106,6 +115,8 @@ func (pf *PF) parsePfctlOutput(pfoutput string, acc telegraf.Accumulator) error
|
||||
return errParseHeader
|
||||
}
|
||||
}
|
||||
|
||||
acc.AddFields(measurement, fields, make(map[string]string))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -124,11 +135,40 @@ var StateTable = []*Entry{
|
||||
|
||||
var stateTableRE = regexp.MustCompile(`^ (.*?)\s+(\d+)`)
|
||||
|
||||
func parseStateTable(lines []string, acc telegraf.Accumulator) error {
|
||||
func parseStateTable(lines []string, fields map[string]interface{}) error {
|
||||
return storeFieldValues(lines, stateTableRE, fields, StateTable)
|
||||
}
|
||||
|
||||
var CounterTable = []*Entry{
|
||||
&Entry{"match", "match", -1},
|
||||
&Entry{"bad-offset", "bad-offset", -1},
|
||||
&Entry{"fragment", "fragment", -1},
|
||||
&Entry{"short", "short", -1},
|
||||
&Entry{"normalize", "normalize", -1},
|
||||
&Entry{"memory", "memory", -1},
|
||||
&Entry{"bad-timestamp", "bad-timestamp", -1},
|
||||
&Entry{"congestion", "congestion", -1},
|
||||
&Entry{"ip-option", "ip-option", -1},
|
||||
&Entry{"proto-cksum", "proto-cksum", -1},
|
||||
&Entry{"state-mismatch", "state-mismatch", -1},
|
||||
&Entry{"state-insert", "state-insert", -1},
|
||||
&Entry{"state-limit", "state-limit", -1},
|
||||
&Entry{"src-limit", "src-limit", -1},
|
||||
&Entry{"synproxy", "synproxy", -1},
|
||||
}
|
||||
|
||||
var counterTableRE = regexp.MustCompile(`^ (.*?)\s+(\d+)`)
|
||||
|
||||
func parseCounterTable(lines []string, fields map[string]interface{}) error {
|
||||
return storeFieldValues(lines, counterTableRE, fields, CounterTable)
|
||||
}
|
||||
|
||||
func storeFieldValues(lines []string, regex *regexp.Regexp, fields map[string]interface{}, entryTable []*Entry) error {
|
||||
|
||||
for _, v := range lines {
|
||||
entries := stateTableRE.FindStringSubmatch(v)
|
||||
entries := regex.FindStringSubmatch(v)
|
||||
if entries != nil {
|
||||
for _, f := range StateTable {
|
||||
for _, f := range entryTable {
|
||||
if f.PfctlTitle == entries[1] {
|
||||
var err error
|
||||
if f.Value, err = strconv.ParseInt(entries[2], 10, 64); err != nil {
|
||||
@@ -139,15 +179,13 @@ func parseStateTable(lines []string, acc telegraf.Accumulator) error {
|
||||
}
|
||||
}
|
||||
|
||||
fields := make(map[string]interface{})
|
||||
for _, v := range StateTable {
|
||||
for _, v := range entryTable {
|
||||
if v.Value == -1 {
|
||||
return errMissingData(v.PfctlTitle)
|
||||
}
|
||||
fields[v.Field] = v.Value
|
||||
}
|
||||
|
||||
acc.AddFields(measurement, fields, make(map[string]string))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -152,10 +152,25 @@ Counters
|
||||
measurements: []measurementResult{
|
||||
measurementResult{
|
||||
fields: map[string]interface{}{
|
||||
"entries": int64(2),
|
||||
"searches": int64(11325),
|
||||
"inserts": int64(5),
|
||||
"removals": int64(3)},
|
||||
"entries": int64(2),
|
||||
"searches": int64(11325),
|
||||
"inserts": int64(5),
|
||||
"removals": int64(3),
|
||||
"match": int64(11226),
|
||||
"bad-offset": int64(0),
|
||||
"fragment": int64(0),
|
||||
"short": int64(0),
|
||||
"normalize": int64(0),
|
||||
"memory": int64(0),
|
||||
"bad-timestamp": int64(0),
|
||||
"congestion": int64(0),
|
||||
"ip-option": int64(0),
|
||||
"proto-cksum": int64(0),
|
||||
"state-mismatch": int64(0),
|
||||
"state-insert": int64(0),
|
||||
"state-limit": int64(0),
|
||||
"src-limit": int64(0),
|
||||
"synproxy": int64(0)},
|
||||
tags: map[string]string{},
|
||||
},
|
||||
},
|
||||
@@ -197,10 +212,25 @@ Counters
|
||||
measurements: []measurementResult{
|
||||
measurementResult{
|
||||
fields: map[string]interface{}{
|
||||
"entries": int64(649),
|
||||
"searches": int64(18421725761),
|
||||
"inserts": int64(156762508),
|
||||
"removals": int64(156761859)},
|
||||
"entries": int64(649),
|
||||
"searches": int64(18421725761),
|
||||
"inserts": int64(156762508),
|
||||
"removals": int64(156761859),
|
||||
"match": int64(473002784),
|
||||
"bad-offset": int64(0),
|
||||
"fragment": int64(2729),
|
||||
"short": int64(107),
|
||||
"normalize": int64(1685),
|
||||
"memory": int64(101),
|
||||
"bad-timestamp": int64(0),
|
||||
"congestion": int64(0),
|
||||
"ip-option": int64(152301),
|
||||
"proto-cksum": int64(108),
|
||||
"state-mismatch": int64(24393),
|
||||
"state-insert": int64(92),
|
||||
"state-limit": int64(0),
|
||||
"src-limit": int64(0),
|
||||
"synproxy": int64(0)},
|
||||
tags: map[string]string{},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -175,7 +175,7 @@ func (p *Ping) args(url string) []string {
|
||||
}
|
||||
if p.Timeout > 0 {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
case "darwin", "freebsd", "netbsd", "openbsd":
|
||||
args = append(args, "-W", strconv.FormatFloat(p.Timeout*1000, 'f', -1, 64))
|
||||
case "linux":
|
||||
args = append(args, "-W", strconv.FormatFloat(p.Timeout, 'f', -1, 64))
|
||||
@@ -186,7 +186,7 @@ func (p *Ping) args(url string) []string {
|
||||
}
|
||||
if p.Deadline > 0 {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
case "darwin", "freebsd", "netbsd", "openbsd":
|
||||
args = append(args, "-t", strconv.Itoa(p.Deadline))
|
||||
case "linux":
|
||||
args = append(args, "-w", strconv.Itoa(p.Deadline))
|
||||
@@ -197,10 +197,10 @@ func (p *Ping) args(url string) []string {
|
||||
}
|
||||
if p.Interface != "" {
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "freebsd", "netbsd", "openbsd":
|
||||
args = append(args, "-S", p.Interface)
|
||||
case "linux":
|
||||
args = append(args, "-I", p.Interface)
|
||||
case "freebsd", "darwin":
|
||||
args = append(args, "-S", p.Interface)
|
||||
default:
|
||||
// Not sure the best option here, just assume GNU ping?
|
||||
args = append(args, "-I", p.Interface)
|
||||
|
||||
@@ -218,10 +218,20 @@ func (t *Table) initBuild() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.Name == "" {
|
||||
t.Name = oidText
|
||||
}
|
||||
t.Fields = append(t.Fields, fields...)
|
||||
|
||||
knownOIDs := map[string]bool{}
|
||||
for _, f := range t.Fields {
|
||||
knownOIDs[f.Oid] = true
|
||||
}
|
||||
for _, f := range fields {
|
||||
if !knownOIDs[f.Oid] {
|
||||
t.Fields = append(t.Fields, f)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ var mockedCommands = [][]string{
|
||||
{"snmptranslate", "-Td", "-Ob", "-m", "all", "1.0.0.1.1"},
|
||||
{"snmptranslate", "-Td", "-Ob", "-m", "all", ".1.0.0.0.1.1"},
|
||||
{"snmptranslate", "-Td", "-Ob", "-m", "all", ".1.0.0.0.1.1.0"},
|
||||
{"snmptranslate", "-Td", "-Ob", "-m", "all", ".1.0.0.0.1.4"},
|
||||
{"snmptranslate", "-Td", "-Ob", "-m", "all", ".1.0.0.0.1.5"},
|
||||
{"snmptranslate", "-Td", "-Ob", "-m", "all", ".1.2.3"},
|
||||
{"snmptranslate", "-Td", "-Ob", ".iso.2.3"},
|
||||
{"snmptranslate", "-Td", "-Ob", "-m", "all", ".999"},
|
||||
@@ -30,6 +30,7 @@ var mockedCommands = [][]string{
|
||||
{"snmptranslate", "-Td", "-Ob", "TEST::testTable"},
|
||||
{"snmptranslate", "-Td", "-Ob", "TEST::connections"},
|
||||
{"snmptranslate", "-Td", "-Ob", "TEST::latency"},
|
||||
{"snmptranslate", "-Td", "-Ob", "TEST::description"},
|
||||
{"snmptranslate", "-Td", "-Ob", "TEST::hostname"},
|
||||
{"snmptranslate", "-Td", "-Ob", "IF-MIB::ifPhysAddress.1"},
|
||||
{"snmptranslate", "-Td", "-Ob", "BRIDGE-MIB::dot1dTpFdbAddress.1"},
|
||||
|
||||
@@ -67,7 +67,7 @@ var mockedCommandResults = map[string]mockedCommandResult{
|
||||
"snmptranslate\x00-Td\x00-Ob\x00-m\x00all\x001.0.0.1.1": mockedCommandResult{stdout: "TEST::hostname\nhostname OBJECT-TYPE\n -- FROM\tTEST\n SYNTAX\tOCTET STRING\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n::= { iso(1) 0 testOID(0) 1 1 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00-m\x00all\x00.1.0.0.0.1.1": mockedCommandResult{stdout: "TEST::server\nserver OBJECT-TYPE\n -- FROM\tTEST\n SYNTAX\tOCTET STRING\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n::= { iso(1) 0 testOID(0) testTable(0) testTableEntry(1) 1 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00-m\x00all\x00.1.0.0.0.1.1.0": mockedCommandResult{stdout: "TEST::server.0\nserver OBJECT-TYPE\n -- FROM\tTEST\n SYNTAX\tOCTET STRING\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n::= { iso(1) 0 testOID(0) testTable(0) testTableEntry(1) server(1) 0 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00-m\x00all\x00.1.0.0.0.1.4": mockedCommandResult{stdout: "TEST::testTableEntry.4\ntestTableEntry OBJECT-TYPE\n -- FROM\tTEST\n MAX-ACCESS\tnot-accessible\n STATUS\tcurrent\n INDEX\t\t{ server }\n::= { iso(1) 0 testOID(0) testTable(0) testTableEntry(1) 4 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00-m\x00all\x00.1.0.0.0.1.5": mockedCommandResult{stdout: "TEST::testTableEntry.5\ntestTableEntry OBJECT-TYPE\n -- FROM\tTEST\n MAX-ACCESS\tnot-accessible\n STATUS\tcurrent\n INDEX\t\t{ server }\n::= { iso(1) 0 testOID(0) testTable(0) testTableEntry(1) 5 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00-m\x00all\x00.1.2.3": mockedCommandResult{stdout: "iso.2.3\niso OBJECT-TYPE\n -- FROM\t#-1\n::= { iso(1) 2 3 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00.iso.2.3": mockedCommandResult{stdout: "iso.2.3\niso OBJECT-TYPE\n -- FROM\t#-1\n::= { iso(1) 2 3 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00-m\x00all\x00.999": mockedCommandResult{stdout: ".999\n [TRUNCATED]\n", stderr: "", exitError: false},
|
||||
@@ -76,10 +76,11 @@ var mockedCommandResults = map[string]mockedCommandResult{
|
||||
"snmptranslate\x00-Td\x00-Ob\x00TEST::testTable": mockedCommandResult{stdout: "TEST::testTable\ntestTable OBJECT-TYPE\n -- FROM\tTEST\n MAX-ACCESS\tnot-accessible\n STATUS\tcurrent\n::= { iso(1) 0 testOID(0) 0 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00TEST::connections": mockedCommandResult{stdout: "TEST::connections\nconnections OBJECT-TYPE\n -- FROM\tTEST\n SYNTAX\tINTEGER\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n::= { iso(1) 0 testOID(0) testTable(0) testTableEntry(1) 2 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00TEST::latency": mockedCommandResult{stdout: "TEST::latency\nlatency OBJECT-TYPE\n -- FROM\tTEST\n SYNTAX\tOCTET STRING\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n::= { iso(1) 0 testOID(0) testTable(0) testTableEntry(1) 3 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00TEST::description": mockedCommandResult{stdout: "TEST::description\ndescription OBJECT-TYPE\n -- FROM\tTEST\n SYNTAX\tOCTET STRING\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n::= { iso(1) 0 testOID(0) testTable(0) testTableEntry(1) 4 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00TEST::hostname": mockedCommandResult{stdout: "TEST::hostname\nhostname OBJECT-TYPE\n -- FROM\tTEST\n SYNTAX\tOCTET STRING\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n::= { iso(1) 0 testOID(0) 1 1 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00IF-MIB::ifPhysAddress.1": mockedCommandResult{stdout: "IF-MIB::ifPhysAddress.1\nifPhysAddress OBJECT-TYPE\n -- FROM\tIF-MIB\n -- TEXTUAL CONVENTION PhysAddress\n SYNTAX\tOCTET STRING\n DISPLAY-HINT\t\"1x:\"\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n DESCRIPTION\t\"The interface's address at its protocol sub-layer. For\n example, for an 802.x interface, this object normally\n contains a MAC address. The interface's media-specific MIB\n must define the bit and byte ordering and the format of the\n value of this object. For interfaces which do not have such\n an address (e.g., a serial line), this object should contain\n an octet string of zero length.\"\n::= { iso(1) org(3) dod(6) internet(1) mgmt(2) mib-2(1) interfaces(2) ifTable(2) ifEntry(1) ifPhysAddress(6) 1 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00BRIDGE-MIB::dot1dTpFdbAddress.1": mockedCommandResult{stdout: "BRIDGE-MIB::dot1dTpFdbAddress.1\ndot1dTpFdbAddress OBJECT-TYPE\n -- FROM\tBRIDGE-MIB\n -- TEXTUAL CONVENTION MacAddress\n SYNTAX\tOCTET STRING (6) \n DISPLAY-HINT\t\"1x:\"\n MAX-ACCESS\tread-only\n STATUS\tcurrent\n DESCRIPTION\t\"A unicast MAC address for which the bridge has\n forwarding and/or filtering information.\"\n::= { iso(1) org(3) dod(6) internet(1) mgmt(2) mib-2(1) dot1dBridge(17) dot1dTp(4) dot1dTpFdbTable(3) dot1dTpFdbEntry(1) dot1dTpFdbAddress(1) 1 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00-Ob\x00TCP-MIB::tcpConnectionLocalAddress.1": mockedCommandResult{stdout: "TCP-MIB::tcpConnectionLocalAddress.1\ntcpConnectionLocalAddress OBJECT-TYPE\n -- FROM\tTCP-MIB\n -- TEXTUAL CONVENTION InetAddress\n SYNTAX\tOCTET STRING (0..255) \n MAX-ACCESS\tnot-accessible\n STATUS\tcurrent\n DESCRIPTION\t\"The local IP address for this TCP connection. The type\n of this address is determined by the value of\n tcpConnectionLocalAddressType.\n\n As this object is used in the index for the\n tcpConnectionTable, implementors should be\n careful not to create entries that would result in OIDs\n with more than 128 subidentifiers; otherwise the information\n cannot be accessed by using SNMPv1, SNMPv2c, or SNMPv3.\"\n::= { iso(1) org(3) dod(6) internet(1) mgmt(2) mib-2(1) tcp(6) tcpConnectionTable(19) tcpConnectionEntry(1) tcpConnectionLocalAddress(2) 1 }\n", stderr: "", exitError: false},
|
||||
"snmptranslate\x00-Td\x00TEST::testTable.1": mockedCommandResult{stdout: "TEST::testTableEntry\ntestTableEntry OBJECT-TYPE\n -- FROM\tTEST\n MAX-ACCESS\tnot-accessible\n STATUS\tcurrent\n INDEX\t\t{ server }\n::= { iso(1) 0 testOID(0) testTable(0) 1 }\n", stderr: "", exitError: false},
|
||||
"snmptable\x00-Ch\x00-Cl\x00-c\x00public\x00127.0.0.1\x00TEST::testTable": mockedCommandResult{stdout: "server connections latency \nTEST::testTable: No entries\n", stderr: "", exitError: false},
|
||||
"snmptable\x00-Ch\x00-Cl\x00-c\x00public\x00127.0.0.1\x00TEST::testTable": mockedCommandResult{stdout: "server connections latency description \nTEST::testTable: No entries\n", stderr: "", exitError: false},
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ var tsc = &testSNMPConnection{
|
||||
".1.0.0.0.1.3.1": "0.456",
|
||||
".1.0.0.0.1.3.2": "0.000",
|
||||
".1.0.0.0.1.3.3": "9.999",
|
||||
".1.0.0.0.1.4.0": 123456,
|
||||
".1.0.0.0.1.5.0": 123456,
|
||||
".1.0.0.1.1": "baz",
|
||||
".1.0.0.1.2": 234,
|
||||
".1.0.0.1.3": []byte("byte slice"),
|
||||
@@ -159,19 +159,23 @@ func TestFieldInit(t *testing.T) {
|
||||
|
||||
func TestTableInit(t *testing.T) {
|
||||
tbl := Table{
|
||||
Oid: ".1.0.0.0",
|
||||
Fields: []Field{{Oid: ".999", Name: "foo"}},
|
||||
Oid: ".1.0.0.0",
|
||||
Fields: []Field{
|
||||
{Oid: ".999", Name: "foo"},
|
||||
{Oid: "TEST::description", Name: "description", IsTag: true},
|
||||
},
|
||||
}
|
||||
err := tbl.init()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "testTable", tbl.Name)
|
||||
|
||||
assert.Len(t, tbl.Fields, 4)
|
||||
assert.Len(t, tbl.Fields, 5)
|
||||
assert.Contains(t, tbl.Fields, Field{Oid: ".999", Name: "foo", initialized: true})
|
||||
assert.Contains(t, tbl.Fields, Field{Oid: ".1.0.0.0.1.1", Name: "server", IsTag: true, initialized: true})
|
||||
assert.Contains(t, tbl.Fields, Field{Oid: ".1.0.0.0.1.2", Name: "connections", initialized: true})
|
||||
assert.Contains(t, tbl.Fields, Field{Oid: ".1.0.0.0.1.3", Name: "latency", initialized: true})
|
||||
assert.Contains(t, tbl.Fields, Field{Oid: ".1.0.0.0.1.4", Name: "description", IsTag: true, initialized: true})
|
||||
}
|
||||
|
||||
func TestSnmpInit(t *testing.T) {
|
||||
@@ -187,10 +191,11 @@ func TestSnmpInit(t *testing.T) {
|
||||
err := s.init()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, s.Tables[0].Fields, 3)
|
||||
assert.Len(t, s.Tables[0].Fields, 4)
|
||||
assert.Contains(t, s.Tables[0].Fields, Field{Oid: ".1.0.0.0.1.1", Name: "server", IsTag: true, initialized: true})
|
||||
assert.Contains(t, s.Tables[0].Fields, Field{Oid: ".1.0.0.0.1.2", Name: "connections", initialized: true})
|
||||
assert.Contains(t, s.Tables[0].Fields, Field{Oid: ".1.0.0.0.1.3", Name: "latency", initialized: true})
|
||||
assert.Contains(t, s.Tables[0].Fields, Field{Oid: ".1.0.0.0.1.4", Name: "description", initialized: true})
|
||||
|
||||
assert.Equal(t, Field{
|
||||
Oid: ".1.0.0.1.1",
|
||||
@@ -579,7 +584,7 @@ func TestGather(t *testing.T) {
|
||||
Fields: []Field{
|
||||
{
|
||||
Name: "myOtherField",
|
||||
Oid: ".1.0.0.0.1.4",
|
||||
Oid: ".1.0.0.0.1.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
7
plugins/inputs/snmp/testdata/test.mib
vendored
7
plugins/inputs/snmp/testdata/test.mib
vendored
@@ -22,6 +22,7 @@ TestTableEntry ::=
|
||||
server OCTET STRING,
|
||||
connections INTEGER,
|
||||
latency OCTET STRING,
|
||||
description OCTET STRING,
|
||||
}
|
||||
|
||||
server OBJECT-TYPE
|
||||
@@ -42,6 +43,12 @@ latency OBJECT-TYPE
|
||||
STATUS current
|
||||
::= { testTableEntry 3 }
|
||||
|
||||
description OBJECT-TYPE
|
||||
SYNTAX OCTET STRING
|
||||
MAX-ACCESS read-only
|
||||
STATUS current
|
||||
::= { testTableEntry 4 }
|
||||
|
||||
hostname OBJECT-TYPE
|
||||
SYNTAX OCTET STRING
|
||||
MAX-ACCESS read-only
|
||||
|
||||
@@ -3,9 +3,11 @@ package socket_listener
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -50,14 +52,17 @@ func TestSocketListener_tcp_tls(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSocketListener_unix_tls(t *testing.T) {
|
||||
defer testEmptyLog(t)()
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "socket_listener.TestSocketListener_unix_tls.sock")
|
||||
|
||||
sl := newSocketListener()
|
||||
sl.ServiceAddress = "unix:///tmp/telegraf_test.sock"
|
||||
sl.ServiceAddress = "unix://" + sock
|
||||
sl.ServerConfig = *pki.TLSServerConfig()
|
||||
|
||||
acc := &testutil.Accumulator{}
|
||||
err := sl.Start(acc)
|
||||
err = sl.Start(acc)
|
||||
require.NoError(t, err)
|
||||
defer sl.Stop()
|
||||
|
||||
@@ -65,7 +70,7 @@ func TestSocketListener_unix_tls(t *testing.T) {
|
||||
tlsCfg.InsecureSkipVerify = true
|
||||
require.NoError(t, err)
|
||||
|
||||
secureClient, err := tls.Dial("unix", "/tmp/telegraf_test.sock", tlsCfg)
|
||||
secureClient, err := tls.Dial("unix", sock, tlsCfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
testSocketListener(t, sl, secureClient)
|
||||
@@ -108,38 +113,48 @@ func TestSocketListener_udp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSocketListener_unix(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "socket_listener.TestSocketListener_unix.sock")
|
||||
|
||||
defer testEmptyLog(t)()
|
||||
|
||||
os.Create("/tmp/telegraf_test.sock")
|
||||
os.Create(sock)
|
||||
sl := newSocketListener()
|
||||
sl.ServiceAddress = "unix:///tmp/telegraf_test.sock"
|
||||
sl.ServiceAddress = "unix://" + sock
|
||||
sl.ReadBufferSize = 1024
|
||||
|
||||
acc := &testutil.Accumulator{}
|
||||
err := sl.Start(acc)
|
||||
err = sl.Start(acc)
|
||||
require.NoError(t, err)
|
||||
defer sl.Stop()
|
||||
|
||||
client, err := net.Dial("unix", "/tmp/telegraf_test.sock")
|
||||
client, err := net.Dial("unix", sock)
|
||||
require.NoError(t, err)
|
||||
|
||||
testSocketListener(t, sl, client)
|
||||
}
|
||||
|
||||
func TestSocketListener_unixgram(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "socket_listener.TestSocketListener_unixgram.sock")
|
||||
|
||||
defer testEmptyLog(t)()
|
||||
|
||||
os.Create("/tmp/telegraf_test.sock")
|
||||
os.Create(sock)
|
||||
sl := newSocketListener()
|
||||
sl.ServiceAddress = "unixgram:///tmp/telegraf_test.sock"
|
||||
sl.ServiceAddress = "unixgram://" + sock
|
||||
sl.ReadBufferSize = 1024
|
||||
|
||||
acc := &testutil.Accumulator{}
|
||||
err := sl.Start(acc)
|
||||
err = sl.Start(acc)
|
||||
require.NoError(t, err)
|
||||
defer sl.Stop()
|
||||
|
||||
client, err := net.Dial("unixgram", "/tmp/telegraf_test.sock")
|
||||
client, err := net.Dial("unixgram", sock)
|
||||
require.NoError(t, err)
|
||||
|
||||
testSocketListener(t, sl, client)
|
||||
|
||||
98
plugins/inputs/syslog/README.md
Normal file
98
plugins/inputs/syslog/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Syslog Input Plugin
|
||||
|
||||
The syslog plugin listens for syslog messages transmitted over
|
||||
[UDP](https://tools.ietf.org/html/rfc5426) or
|
||||
[TCP](https://tools.ietf.org/html/rfc5425).
|
||||
|
||||
Syslog messages should be formatted according to
|
||||
[RFC 5424](https://tools.ietf.org/html/rfc5424).
|
||||
|
||||
### Configuration
|
||||
|
||||
```toml
|
||||
[[inputs.syslog]]
|
||||
## Specify an ip or hostname with port - eg., tcp://localhost:6514, tcp://10.0.0.1:6514
|
||||
## Protocol, address and port to host the syslog receiver.
|
||||
## If no host is specified, then localhost is used.
|
||||
## If no port is specified, 6514 is used (RFC5425#section-4.1).
|
||||
server = "tcp://:6514"
|
||||
|
||||
## TLS Config
|
||||
# tls_allowed_cacerts = ["/etc/telegraf/ca.pem"]
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
# tls_key = "/etc/telegraf/key.pem"
|
||||
|
||||
## Period between keep alive probes.
|
||||
## 0 disables keep alive probes.
|
||||
## Defaults to the OS configuration.
|
||||
## Only applies to stream sockets (e.g. TCP).
|
||||
# keep_alive_period = "5m"
|
||||
|
||||
## Maximum number of concurrent connections (default = 0).
|
||||
## 0 means unlimited.
|
||||
## Only applies to stream sockets (e.g. TCP).
|
||||
# max_connections = 1024
|
||||
|
||||
## Read timeout (default = 500ms).
|
||||
## 0 means unlimited.
|
||||
# read_timeout = 500ms
|
||||
|
||||
## Whether to parse in best effort mode or not (default = false).
|
||||
## By default best effort parsing is off.
|
||||
# best_effort = false
|
||||
|
||||
## Character to prepend to SD-PARAMs (default = "_").
|
||||
## A syslog message can contain multiple parameters and multiple identifiers within structured data section.
|
||||
## Eg., [id1 name1="val1" name2="val2"][id2 name1="val1" nameA="valA"]
|
||||
## For each combination a field is created.
|
||||
## Its name is created concatenating identifier, sdparam_separator, and parameter name.
|
||||
# sdparam_separator = "_"
|
||||
```
|
||||
|
||||
#### Best Effort
|
||||
|
||||
The [`best_effort`](https://github.com/influxdata/go-syslog#best-effort-mode)
|
||||
option instructs the parser to extract partial but valid info from syslog
|
||||
messages. If unset only full messages will be collected.
|
||||
|
||||
### Metrics
|
||||
|
||||
- syslog
|
||||
- tags
|
||||
- severity (string)
|
||||
- facility (string)
|
||||
- hostname (string)
|
||||
- appname (string)
|
||||
- fields
|
||||
- version (integer)
|
||||
- severity_code (integer)
|
||||
- facility_code (integer)
|
||||
- timestamp (integer)
|
||||
- procid (string)
|
||||
- msgid (string)
|
||||
- sdid (bool)
|
||||
- *Structured Data* (string)
|
||||
|
||||
### Rsyslog Integration
|
||||
|
||||
Rsyslog can be configured to forward logging messages to Telegraf by configuring
|
||||
[remote logging](https://www.rsyslog.com/doc/v8-stable/configuration/actions.html#remote-machine).
|
||||
|
||||
Most system are setup with a configuration split between `/etc/rsyslog.conf`
|
||||
and the files in the `/etc/rsyslog.d/` directory, it is recommended to add the
|
||||
new configuration into the config directory to simplify updates to the main
|
||||
config file.
|
||||
|
||||
Add the following lines to `/etc/rsyslog.d/50-telegraf.conf` making
|
||||
adjustments to the target address as needed:
|
||||
```
|
||||
$ActionQueueType LinkedList # use asynchronous processing
|
||||
$ActionQueueFileName srvrfwd # set file name, also enables disk mode
|
||||
$ActionResumeRetryCount -1 # infinite retries on insert failure
|
||||
$ActionQueueSaveOnShutdown on # save in-memory data if rsyslog shuts down
|
||||
|
||||
# forward over tcp with octet framing according to RFC 5425
|
||||
*.* @@(o)127.0.0.1:6514;RSYSLOG_SyslogProtocol23Format
|
||||
```
|
||||
|
||||
To complete TLS setup please refer to [rsyslog docs](https://www.rsyslog.com/doc/v8-stable/tutorials/tls.html).
|
||||
539
plugins/inputs/syslog/rfc5425_test.go
Normal file
539
plugins/inputs/syslog/rfc5425_test.go
Normal file
@@ -0,0 +1,539 @@
|
||||
package syslog
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
pki = testutil.NewPKI("../../../testutil/pki")
|
||||
)
|
||||
|
||||
type testCase5425 struct {
|
||||
name string
|
||||
data []byte
|
||||
wantBestEffort []testutil.Metric
|
||||
wantStrict []testutil.Metric
|
||||
werr int // how many errors we expect in the strict mode?
|
||||
}
|
||||
|
||||
func getTestCasesForRFC5425() []testCase5425 {
|
||||
testCases := []testCase5425{
|
||||
{
|
||||
name: "1st/avg/ok",
|
||||
data: []byte(`188 <29>1 2016-02-21T04:32:57+00:00 web1 someservice 2341 2 [origin][meta sequence="14125553" service="someservice"] "GET /v1/ok HTTP/1.1" 200 145 "-" "hacheck 0.9.0" 24306 127.0.0.1:40124 575`),
|
||||
wantStrict: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"timestamp": time.Unix(1456029177, 0).UnixNano(),
|
||||
"procid": "2341",
|
||||
"msgid": "2",
|
||||
"message": `"GET /v1/ok HTTP/1.1" 200 145 "-" "hacheck 0.9.0" 24306 127.0.0.1:40124 575`,
|
||||
"origin": true,
|
||||
"meta_sequence": "14125553",
|
||||
"meta_service": "someservice",
|
||||
"severity_code": 5,
|
||||
"facility_code": 3,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "notice",
|
||||
"facility": "daemon",
|
||||
"hostname": "web1",
|
||||
"appname": "someservice",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
wantBestEffort: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"timestamp": time.Unix(1456029177, 0).UnixNano(),
|
||||
"procid": "2341",
|
||||
"msgid": "2",
|
||||
"message": `"GET /v1/ok HTTP/1.1" 200 145 "-" "hacheck 0.9.0" 24306 127.0.0.1:40124 575`,
|
||||
"origin": true,
|
||||
"meta_sequence": "14125553",
|
||||
"meta_service": "someservice",
|
||||
"severity_code": 5,
|
||||
"facility_code": 3,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "notice",
|
||||
"facility": "daemon",
|
||||
"hostname": "web1",
|
||||
"appname": "someservice",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1st/min/ok//2nd/min/ok",
|
||||
data: []byte("16 <1>2 - - - - - -17 <4>11 - - - - - -"),
|
||||
wantStrict: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(2),
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(11),
|
||||
"severity_code": 4,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "warning",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime.Add(time.Nanosecond),
|
||||
},
|
||||
},
|
||||
wantBestEffort: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(2),
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(11),
|
||||
"severity_code": 4,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "warning",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime.Add(time.Nanosecond),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1st/utf8/ok",
|
||||
data: []byte("23 <1>1 - - - - - - hellø"),
|
||||
wantStrict: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"message": "hellø",
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
wantBestEffort: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"message": "hellø",
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1st/nl/ok", // newline
|
||||
data: []byte("28 <1>3 - - - - - - hello\nworld"),
|
||||
wantStrict: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(3),
|
||||
"message": "hello\nworld",
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
wantBestEffort: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(3),
|
||||
"message": "hello\nworld",
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1st/uf/ko", // underflow (msglen less than provided octets)
|
||||
data: []byte("16 <1>2"),
|
||||
wantStrict: nil,
|
||||
wantBestEffort: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(2),
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
werr: 1,
|
||||
},
|
||||
{
|
||||
name: "1st/min/ok",
|
||||
data: []byte("16 <1>1 - - - - - -"),
|
||||
wantStrict: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
wantBestEffort: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1st/uf/mf", // The first "underflow" message breaks also the second one
|
||||
data: []byte("16 <1>217 <11>1 - - - - - -"),
|
||||
wantStrict: nil,
|
||||
wantBestEffort: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(217),
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
werr: 1,
|
||||
},
|
||||
// {
|
||||
// name: "1st/of/ko", // overflow (msglen greather then max allowed octets)
|
||||
// data: []byte(fmt.Sprintf("8193 <%d>%d %s %s %s %s %s 12 %s", maxP, maxV, maxTS, maxH, maxA, maxPID, maxMID, message7681)),
|
||||
// want: []testutil.Metric{},
|
||||
// },
|
||||
{
|
||||
name: "1st/max/ok",
|
||||
data: []byte(fmt.Sprintf("8192 <%d>%d %s %s %s %s %s - %s", maxP, maxV, maxTS, maxH, maxA, maxPID, maxMID, message7681)),
|
||||
wantStrict: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": maxV,
|
||||
"timestamp": time.Unix(1514764799, 999999000).UnixNano(),
|
||||
"message": message7681,
|
||||
"procid": maxPID,
|
||||
"msgid": maxMID,
|
||||
"facility_code": 23,
|
||||
"severity_code": 7,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "debug",
|
||||
"facility": "local7",
|
||||
"hostname": maxH,
|
||||
"appname": maxA,
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
wantBestEffort: []testutil.Metric{
|
||||
testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": maxV,
|
||||
"timestamp": time.Unix(1514764799, 999999000).UnixNano(),
|
||||
"message": message7681,
|
||||
"procid": maxPID,
|
||||
"msgid": maxMID,
|
||||
"facility_code": 23,
|
||||
"severity_code": 7,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "debug",
|
||||
"facility": "local7",
|
||||
"hostname": maxH,
|
||||
"appname": maxA,
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return testCases
|
||||
}
|
||||
|
||||
func newTCPSyslogReceiver(address string, keepAlive *internal.Duration, maxConn int, bestEffort bool) *Syslog {
|
||||
d := &internal.Duration{
|
||||
Duration: defaultReadTimeout,
|
||||
}
|
||||
s := &Syslog{
|
||||
Address: address,
|
||||
now: func() time.Time {
|
||||
return defaultTime
|
||||
},
|
||||
ReadTimeout: d,
|
||||
BestEffort: bestEffort,
|
||||
Separator: "_",
|
||||
}
|
||||
if keepAlive != nil {
|
||||
s.KeepAlivePeriod = keepAlive
|
||||
}
|
||||
if maxConn > 0 {
|
||||
s.MaxConnections = maxConn
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func testStrictRFC5425(t *testing.T, protocol string, address string, wantTLS bool, keepAlive *internal.Duration) {
|
||||
for _, tc := range getTestCasesForRFC5425() {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Creation of a strict mode receiver
|
||||
receiver := newTCPSyslogReceiver(protocol+"://"+address, keepAlive, 0, false)
|
||||
require.NotNil(t, receiver)
|
||||
if wantTLS {
|
||||
receiver.ServerConfig = *pki.TLSServerConfig()
|
||||
}
|
||||
require.Equal(t, receiver.KeepAlivePeriod, keepAlive)
|
||||
acc := &testutil.Accumulator{}
|
||||
require.NoError(t, receiver.Start(acc))
|
||||
defer receiver.Stop()
|
||||
|
||||
// Connect
|
||||
var conn net.Conn
|
||||
var err error
|
||||
if wantTLS {
|
||||
config, e := pki.TLSClientConfig().TLSConfig()
|
||||
require.NoError(t, e)
|
||||
config.ServerName = "localhost"
|
||||
conn, err = tls.Dial(protocol, address, config)
|
||||
} else {
|
||||
conn, err = net.Dial(protocol, address)
|
||||
defer conn.Close()
|
||||
}
|
||||
require.NotNil(t, conn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Clear
|
||||
acc.ClearMetrics()
|
||||
acc.Errors = make([]error, 0)
|
||||
|
||||
// Write
|
||||
conn.Write(tc.data)
|
||||
|
||||
// Wait that the the number of data points is accumulated
|
||||
// Since the receiver is running concurrently
|
||||
if tc.wantStrict != nil {
|
||||
acc.Wait(len(tc.wantStrict))
|
||||
}
|
||||
// Wait the parsing error
|
||||
acc.WaitError(tc.werr)
|
||||
|
||||
// Verify
|
||||
if len(acc.Errors) != tc.werr {
|
||||
t.Fatalf("Got unexpected errors. want error = %v, errors = %v\n", tc.werr, acc.Errors)
|
||||
}
|
||||
var got []testutil.Metric
|
||||
for _, metric := range acc.Metrics {
|
||||
got = append(got, *metric)
|
||||
}
|
||||
if !cmp.Equal(tc.wantStrict, got) {
|
||||
t.Fatalf("Got (+) / Want (-)\n %s", cmp.Diff(tc.wantStrict, got))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testBestEffortRFC5425(t *testing.T, protocol string, address string, wantTLS bool, keepAlive *internal.Duration) {
|
||||
for _, tc := range getTestCasesForRFC5425() {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Creation of a best effort mode receiver
|
||||
receiver := newTCPSyslogReceiver(protocol+"://"+address, keepAlive, 0, true)
|
||||
require.NotNil(t, receiver)
|
||||
if wantTLS {
|
||||
receiver.ServerConfig = *pki.TLSServerConfig()
|
||||
}
|
||||
require.Equal(t, receiver.KeepAlivePeriod, keepAlive)
|
||||
acc := &testutil.Accumulator{}
|
||||
require.NoError(t, receiver.Start(acc))
|
||||
defer receiver.Stop()
|
||||
|
||||
// Connect
|
||||
var conn net.Conn
|
||||
var err error
|
||||
if wantTLS {
|
||||
config, e := pki.TLSClientConfig().TLSConfig()
|
||||
require.NoError(t, e)
|
||||
config.ServerName = "localhost"
|
||||
conn, err = tls.Dial(protocol, address, config)
|
||||
} else {
|
||||
conn, err = net.Dial(protocol, address)
|
||||
defer conn.Close()
|
||||
}
|
||||
require.NotNil(t, conn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Clear
|
||||
acc.ClearMetrics()
|
||||
acc.Errors = make([]error, 0)
|
||||
|
||||
// Write
|
||||
conn.Write(tc.data)
|
||||
|
||||
// Wait that the the number of data points is accumulated
|
||||
// Since the receiver is running concurrently
|
||||
if tc.wantBestEffort != nil {
|
||||
acc.Wait(len(tc.wantBestEffort))
|
||||
}
|
||||
|
||||
// Verify
|
||||
var got []testutil.Metric
|
||||
for _, metric := range acc.Metrics {
|
||||
got = append(got, *metric)
|
||||
}
|
||||
if !cmp.Equal(tc.wantBestEffort, got) {
|
||||
t.Fatalf("Got (+) / Want (-)\n %s", cmp.Diff(tc.wantBestEffort, got))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrict_tcp(t *testing.T) {
|
||||
testStrictRFC5425(t, "tcp", address, false, nil)
|
||||
}
|
||||
|
||||
func TestBestEffort_tcp(t *testing.T) {
|
||||
testBestEffortRFC5425(t, "tcp", address, false, nil)
|
||||
}
|
||||
|
||||
func TestStrict_tcp_tls(t *testing.T) {
|
||||
testStrictRFC5425(t, "tcp", address, true, nil)
|
||||
}
|
||||
|
||||
func TestBestEffort_tcp_tls(t *testing.T) {
|
||||
testBestEffortRFC5425(t, "tcp", address, true, nil)
|
||||
}
|
||||
|
||||
func TestStrictWithKeepAlive_tcp_tls(t *testing.T) {
|
||||
testStrictRFC5425(t, "tcp", address, true, &internal.Duration{Duration: time.Minute})
|
||||
}
|
||||
|
||||
func TestStrictWithZeroKeepAlive_tcp_tls(t *testing.T) {
|
||||
testStrictRFC5425(t, "tcp", address, true, &internal.Duration{Duration: 0})
|
||||
}
|
||||
|
||||
func TestStrict_unix(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "syslog.TestStrict_unix.sock")
|
||||
testStrictRFC5425(t, "unix", sock, false, nil)
|
||||
}
|
||||
|
||||
func TestBestEffort_unix(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "syslog.TestBestEffort_unix.sock")
|
||||
testBestEffortRFC5425(t, "unix", sock, false, nil)
|
||||
}
|
||||
|
||||
func TestStrict_unix_tls(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "syslog.TestStrict_unix_tls.sock")
|
||||
testStrictRFC5425(t, "unix", sock, true, nil)
|
||||
}
|
||||
|
||||
func TestBestEffort_unix_tls(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "syslog.TestBestEffort_unix_tls.sock")
|
||||
testBestEffortRFC5425(t, "unix", sock, true, nil)
|
||||
}
|
||||
408
plugins/inputs/syslog/rfc5426_test.go
Normal file
408
plugins/inputs/syslog/rfc5426_test.go
Normal file
@@ -0,0 +1,408 @@
|
||||
package syslog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testCase5426 struct {
|
||||
name string
|
||||
data []byte
|
||||
wantBestEffort *testutil.Metric
|
||||
wantStrict *testutil.Metric
|
||||
werr bool
|
||||
}
|
||||
|
||||
func getTestCasesForRFC5426() []testCase5426 {
|
||||
testCases := []testCase5426{
|
||||
{
|
||||
name: "empty",
|
||||
data: []byte(""),
|
||||
werr: true,
|
||||
},
|
||||
{
|
||||
name: "complete",
|
||||
data: []byte("<1>1 - - - - - - A"),
|
||||
wantBestEffort: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"message": "A",
|
||||
"facility_code": 0,
|
||||
"severity_code": 1,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
wantStrict: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"message": "A",
|
||||
"facility_code": 0,
|
||||
"severity_code": 1,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one/per/packet",
|
||||
data: []byte("<1>3 - - - - - - A<1>4 - - - - - - B"),
|
||||
wantBestEffort: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(3),
|
||||
"message": "A<1>4 - - - - - - B",
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
wantStrict: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(3),
|
||||
"message": "A<1>4 - - - - - - B",
|
||||
"severity_code": 1,
|
||||
"facility_code": 0,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "average",
|
||||
data: []byte(`<29>1 2016-02-21T04:32:57+00:00 web1 someservice 2341 2 [origin][meta sequence="14125553" service="someservice"] "GET /v1/ok HTTP/1.1" 200 145 "-" "hacheck 0.9.0" 24306 127.0.0.1:40124 575`),
|
||||
wantBestEffort: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"timestamp": time.Unix(1456029177, 0).UnixNano(),
|
||||
"procid": "2341",
|
||||
"msgid": "2",
|
||||
"message": `"GET /v1/ok HTTP/1.1" 200 145 "-" "hacheck 0.9.0" 24306 127.0.0.1:40124 575`,
|
||||
"origin": true,
|
||||
"meta_sequence": "14125553",
|
||||
"meta_service": "someservice",
|
||||
"severity_code": 5,
|
||||
"facility_code": 3,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "notice",
|
||||
"facility": "daemon",
|
||||
"hostname": "web1",
|
||||
"appname": "someservice",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
wantStrict: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"timestamp": time.Unix(1456029177, 0).UnixNano(),
|
||||
"procid": "2341",
|
||||
"msgid": "2",
|
||||
"message": `"GET /v1/ok HTTP/1.1" 200 145 "-" "hacheck 0.9.0" 24306 127.0.0.1:40124 575`,
|
||||
"origin": true,
|
||||
"meta_sequence": "14125553",
|
||||
"meta_service": "someservice",
|
||||
"severity_code": 5,
|
||||
"facility_code": 3,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "notice",
|
||||
"facility": "daemon",
|
||||
"hostname": "web1",
|
||||
"appname": "someservice",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "max",
|
||||
data: []byte(fmt.Sprintf("<%d>%d %s %s %s %s %s - %s", maxP, maxV, maxTS, maxH, maxA, maxPID, maxMID, message7681)),
|
||||
wantBestEffort: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": maxV,
|
||||
"timestamp": time.Unix(1514764799, 999999000).UnixNano(),
|
||||
"message": message7681,
|
||||
"procid": maxPID,
|
||||
"msgid": maxMID,
|
||||
"severity_code": 7,
|
||||
"facility_code": 23,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "debug",
|
||||
"facility": "local7",
|
||||
"hostname": maxH,
|
||||
"appname": maxA,
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
wantStrict: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": maxV,
|
||||
"timestamp": time.Unix(1514764799, 999999000).UnixNano(),
|
||||
"message": message7681,
|
||||
"procid": maxPID,
|
||||
"msgid": maxMID,
|
||||
"severity_code": 7,
|
||||
"facility_code": 23,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "debug",
|
||||
"facility": "local7",
|
||||
"hostname": maxH,
|
||||
"appname": maxA,
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minimal/incomplete",
|
||||
data: []byte("<1>2"),
|
||||
wantBestEffort: &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(2),
|
||||
"facility_code": 0,
|
||||
"severity_code": 1,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: defaultTime,
|
||||
},
|
||||
werr: true,
|
||||
},
|
||||
}
|
||||
|
||||
return testCases
|
||||
}
|
||||
|
||||
func newUDPSyslogReceiver(address string, bestEffort bool) *Syslog {
|
||||
return &Syslog{
|
||||
Address: address,
|
||||
now: func() time.Time {
|
||||
return defaultTime
|
||||
},
|
||||
BestEffort: bestEffort,
|
||||
Separator: "_",
|
||||
}
|
||||
}
|
||||
|
||||
func testRFC5426(t *testing.T, protocol string, address string, bestEffort bool) {
|
||||
for _, tc := range getTestCasesForRFC5426() {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create receiver
|
||||
receiver := newUDPSyslogReceiver(protocol+"://"+address, bestEffort)
|
||||
acc := &testutil.Accumulator{}
|
||||
require.NoError(t, receiver.Start(acc))
|
||||
defer receiver.Stop()
|
||||
|
||||
// Clear
|
||||
acc.ClearMetrics()
|
||||
acc.Errors = make([]error, 0)
|
||||
|
||||
// Connect
|
||||
conn, err := net.Dial(protocol, address)
|
||||
require.NotNil(t, conn)
|
||||
defer conn.Close()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Write
|
||||
_, e := conn.Write(tc.data)
|
||||
require.Nil(t, e)
|
||||
|
||||
// Waiting ...
|
||||
if tc.wantStrict == nil && tc.werr || bestEffort && tc.werr {
|
||||
acc.WaitError(1)
|
||||
}
|
||||
if tc.wantBestEffort != nil && bestEffort || tc.wantStrict != nil && !bestEffort {
|
||||
acc.Wait(1) // RFC5426 mandates a syslog message per UDP packet
|
||||
}
|
||||
|
||||
// Compare
|
||||
var got *testutil.Metric
|
||||
var want *testutil.Metric
|
||||
if len(acc.Metrics) > 0 {
|
||||
got = acc.Metrics[0]
|
||||
}
|
||||
if bestEffort {
|
||||
want = tc.wantBestEffort
|
||||
} else {
|
||||
want = tc.wantStrict
|
||||
}
|
||||
if !cmp.Equal(want, got) {
|
||||
t.Fatalf("Got (+) / Want (-)\n %s", cmp.Diff(want, got))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestEffort_udp(t *testing.T) {
|
||||
testRFC5426(t, "udp", address, true)
|
||||
}
|
||||
|
||||
func TestStrict_udp(t *testing.T) {
|
||||
testRFC5426(t, "udp", address, false)
|
||||
}
|
||||
|
||||
func TestBestEffort_unixgram(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "syslog.TestBestEffort_unixgram.sock")
|
||||
os.Create(sock)
|
||||
testRFC5426(t, "unixgram", sock, true)
|
||||
}
|
||||
|
||||
func TestStrict_unixgram(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "syslog.TestStrict_unixgram.sock")
|
||||
os.Create(sock)
|
||||
testRFC5426(t, "unixgram", sock, false)
|
||||
}
|
||||
|
||||
func TestTimeIncrement_udp(t *testing.T) {
|
||||
var i int64
|
||||
atomic.StoreInt64(&i, 0)
|
||||
getNow := func() time.Time {
|
||||
if atomic.LoadInt64(&i)%2 == 0 {
|
||||
return time.Unix(1, 0)
|
||||
}
|
||||
return time.Unix(1, 1)
|
||||
}
|
||||
|
||||
// Create receiver
|
||||
receiver := &Syslog{
|
||||
Address: "udp://" + address,
|
||||
now: getNow,
|
||||
BestEffort: false,
|
||||
Separator: "_",
|
||||
}
|
||||
acc := &testutil.Accumulator{}
|
||||
require.NoError(t, receiver.Start(acc))
|
||||
defer receiver.Stop()
|
||||
|
||||
// Connect
|
||||
conn, err := net.Dial("udp", address)
|
||||
require.NotNil(t, conn)
|
||||
defer conn.Close()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Write
|
||||
_, e := conn.Write([]byte("<1>1 - - - - - -"))
|
||||
require.Nil(t, e)
|
||||
|
||||
// Wait
|
||||
acc.Wait(1)
|
||||
|
||||
want := &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"facility_code": 0,
|
||||
"severity_code": 1,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: getNow(),
|
||||
}
|
||||
|
||||
if !cmp.Equal(want, acc.Metrics[0]) {
|
||||
t.Fatalf("Got (+) / Want (-)\n %s", cmp.Diff(want, acc.Metrics[0]))
|
||||
}
|
||||
|
||||
// New one with different time
|
||||
atomic.StoreInt64(&i, atomic.LoadInt64(&i)+1)
|
||||
|
||||
// Clear
|
||||
acc.ClearMetrics()
|
||||
|
||||
// Write
|
||||
_, e = conn.Write([]byte("<1>1 - - - - - -"))
|
||||
require.Nil(t, e)
|
||||
|
||||
// Wait
|
||||
acc.Wait(1)
|
||||
|
||||
want = &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"facility_code": 0,
|
||||
"severity_code": 1,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: getNow(),
|
||||
}
|
||||
|
||||
if !cmp.Equal(want, acc.Metrics[0]) {
|
||||
t.Fatalf("Got (+) / Want (-)\n %s", cmp.Diff(want, acc.Metrics[0]))
|
||||
}
|
||||
|
||||
// New one with same time as previous one
|
||||
|
||||
// Clear
|
||||
acc.ClearMetrics()
|
||||
|
||||
// Write
|
||||
_, e = conn.Write([]byte("<1>1 - - - - - -"))
|
||||
require.Nil(t, e)
|
||||
|
||||
// Wait
|
||||
acc.Wait(1)
|
||||
|
||||
want = &testutil.Metric{
|
||||
Measurement: "syslog",
|
||||
Fields: map[string]interface{}{
|
||||
"version": uint16(1),
|
||||
"facility_code": 0,
|
||||
"severity_code": 1,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"severity": "alert",
|
||||
"facility": "kern",
|
||||
},
|
||||
Time: getNow().Add(time.Nanosecond),
|
||||
}
|
||||
|
||||
if !cmp.Equal(want, acc.Metrics[0]) {
|
||||
t.Fatalf("Got (+) / Want (-)\n %s", cmp.Diff(want, acc.Metrics[0]))
|
||||
}
|
||||
}
|
||||
419
plugins/inputs/syslog/syslog.go
Normal file
419
plugins/inputs/syslog/syslog.go
Normal file
@@ -0,0 +1,419 @@
|
||||
package syslog
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/go-syslog/rfc5424"
|
||||
"github.com/influxdata/go-syslog/rfc5425"
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
tlsConfig "github.com/influxdata/telegraf/internal/tls"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
const defaultReadTimeout = time.Millisecond * 500
|
||||
const ipMaxPacketSize = 64 * 1024
|
||||
|
||||
// Syslog is a syslog plugin
|
||||
type Syslog struct {
|
||||
tlsConfig.ServerConfig
|
||||
Address string `toml:"server"`
|
||||
KeepAlivePeriod *internal.Duration
|
||||
ReadTimeout *internal.Duration
|
||||
MaxConnections int
|
||||
BestEffort bool
|
||||
Separator string `toml:"sdparam_separator"`
|
||||
|
||||
now func() time.Time
|
||||
lastTime time.Time
|
||||
|
||||
mu sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
io.Closer
|
||||
|
||||
isStream bool
|
||||
tcpListener net.Listener
|
||||
tlsConfig *tls.Config
|
||||
connections map[string]net.Conn
|
||||
connectionsMu sync.Mutex
|
||||
|
||||
udpListener net.PacketConn
|
||||
}
|
||||
|
||||
var sampleConfig = `
|
||||
## Specify an ip or hostname with port - eg., tcp://localhost:6514, tcp://10.0.0.1:6514
|
||||
## Protocol, address and port to host the syslog receiver.
|
||||
## If no host is specified, then localhost is used.
|
||||
## If no port is specified, 6514 is used (RFC5425#section-4.1).
|
||||
server = "tcp://:6514"
|
||||
|
||||
## TLS Config
|
||||
# tls_allowed_cacerts = ["/etc/telegraf/ca.pem"]
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
# tls_key = "/etc/telegraf/key.pem"
|
||||
|
||||
## Period between keep alive probes.
|
||||
## 0 disables keep alive probes.
|
||||
## Defaults to the OS configuration.
|
||||
## Only applies to stream sockets (e.g. TCP).
|
||||
# keep_alive_period = "5m"
|
||||
|
||||
## Maximum number of concurrent connections (default = 0).
|
||||
## 0 means unlimited.
|
||||
## Only applies to stream sockets (e.g. TCP).
|
||||
# max_connections = 1024
|
||||
|
||||
## Read timeout (default = 500ms).
|
||||
## 0 means unlimited.
|
||||
# read_timeout = 500ms
|
||||
|
||||
## Whether to parse in best effort mode or not (default = false).
|
||||
## By default best effort parsing is off.
|
||||
# best_effort = false
|
||||
|
||||
## Character to prepend to SD-PARAMs (default = "_").
|
||||
## A syslog message can contain multiple parameters and multiple identifiers within structured data section.
|
||||
## Eg., [id1 name1="val1" name2="val2"][id2 name1="val1" nameA="valA"]
|
||||
## For each combination a field is created.
|
||||
## Its name is created concatenating identifier, sdparam_separator, and parameter name.
|
||||
# sdparam_separator = "_"
|
||||
`
|
||||
|
||||
// SampleConfig returns sample configuration message
|
||||
func (s *Syslog) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
// Description returns the plugin description
|
||||
func (s *Syslog) Description() string {
|
||||
return "Accepts syslog messages per RFC5425"
|
||||
}
|
||||
|
||||
// Gather ...
|
||||
func (s *Syslog) Gather(_ telegraf.Accumulator) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the service.
|
||||
func (s *Syslog) Start(acc telegraf.Accumulator) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
scheme, host, err := getAddressParts(s.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Address = host
|
||||
|
||||
switch scheme {
|
||||
case "tcp", "tcp4", "tcp6", "unix", "unixpacket":
|
||||
s.isStream = true
|
||||
case "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unixgram":
|
||||
s.isStream = false
|
||||
default:
|
||||
return fmt.Errorf("unknown protocol '%s' in '%s'", scheme, s.Address)
|
||||
}
|
||||
|
||||
if scheme == "unix" || scheme == "unixpacket" || scheme == "unixgram" {
|
||||
os.Remove(s.Address)
|
||||
}
|
||||
|
||||
if s.isStream {
|
||||
l, err := net.Listen(scheme, s.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Closer = l
|
||||
s.tcpListener = l
|
||||
s.tlsConfig, err = s.TLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.wg.Add(1)
|
||||
go s.listenStream(acc)
|
||||
} else {
|
||||
l, err := net.ListenPacket(scheme, s.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Closer = l
|
||||
s.udpListener = l
|
||||
|
||||
s.wg.Add(1)
|
||||
go s.listenPacket(acc)
|
||||
}
|
||||
|
||||
if scheme == "unix" || scheme == "unixpacket" || scheme == "unixgram" {
|
||||
s.Closer = unixCloser{path: s.Address, closer: s.Closer}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop cleans up all resources
|
||||
func (s *Syslog) Stop() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.Closer != nil {
|
||||
s.Close()
|
||||
}
|
||||
s.wg.Wait()
|
||||
}
|
||||
|
||||
// getAddressParts returns the address scheme and host
|
||||
// it also sets defaults for them when missing
|
||||
// when the input address does not specify the protocol it returns an error
|
||||
func getAddressParts(a string) (string, string, error) {
|
||||
parts := strings.SplitN(a, "://", 2)
|
||||
if len(parts) != 2 {
|
||||
return "", "", fmt.Errorf("missing protocol within address '%s'", a)
|
||||
}
|
||||
|
||||
u, _ := url.Parse(a)
|
||||
switch u.Scheme {
|
||||
case "unix", "unixpacket", "unixgram":
|
||||
return parts[0], parts[1], nil
|
||||
}
|
||||
|
||||
var host string
|
||||
if u.Hostname() != "" {
|
||||
host = u.Hostname()
|
||||
}
|
||||
host += ":"
|
||||
if u.Port() == "" {
|
||||
host += "6514"
|
||||
} else {
|
||||
host += u.Port()
|
||||
}
|
||||
|
||||
return u.Scheme, host, nil
|
||||
}
|
||||
|
||||
func (s *Syslog) listenPacket(acc telegraf.Accumulator) {
|
||||
defer s.wg.Done()
|
||||
b := make([]byte, ipMaxPacketSize)
|
||||
p := rfc5424.NewParser()
|
||||
for {
|
||||
n, _, err := s.udpListener.ReadFrom(b)
|
||||
if err != nil {
|
||||
if !strings.HasSuffix(err.Error(), ": use of closed network connection") {
|
||||
acc.AddError(err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if s.ReadTimeout != nil && s.ReadTimeout.Duration > 0 {
|
||||
s.udpListener.SetReadDeadline(time.Now().Add(s.ReadTimeout.Duration))
|
||||
}
|
||||
|
||||
message, err := p.Parse(b[:n], &s.BestEffort)
|
||||
if message != nil {
|
||||
acc.AddFields("syslog", fields(*message, s), tags(*message), s.time())
|
||||
}
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Syslog) listenStream(acc telegraf.Accumulator) {
|
||||
defer s.wg.Done()
|
||||
|
||||
s.connections = map[string]net.Conn{}
|
||||
|
||||
for {
|
||||
conn, err := s.tcpListener.Accept()
|
||||
if err != nil {
|
||||
if !strings.HasSuffix(err.Error(), ": use of closed network connection") {
|
||||
acc.AddError(err)
|
||||
}
|
||||
break
|
||||
}
|
||||
var tcpConn, _ = conn.(*net.TCPConn)
|
||||
if s.tlsConfig != nil {
|
||||
conn = tls.Server(conn, s.tlsConfig)
|
||||
}
|
||||
|
||||
s.connectionsMu.Lock()
|
||||
if s.MaxConnections > 0 && len(s.connections) >= s.MaxConnections {
|
||||
s.connectionsMu.Unlock()
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
s.connections[conn.RemoteAddr().String()] = conn
|
||||
s.connectionsMu.Unlock()
|
||||
|
||||
if err := s.setKeepAlive(tcpConn); err != nil {
|
||||
acc.AddError(fmt.Errorf("unable to configure keep alive (%s): %s", s.Address, err))
|
||||
}
|
||||
|
||||
go s.handle(conn, acc)
|
||||
}
|
||||
|
||||
s.connectionsMu.Lock()
|
||||
for _, c := range s.connections {
|
||||
c.Close()
|
||||
}
|
||||
s.connectionsMu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Syslog) removeConnection(c net.Conn) {
|
||||
s.connectionsMu.Lock()
|
||||
delete(s.connections, c.RemoteAddr().String())
|
||||
s.connectionsMu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Syslog) handle(conn net.Conn, acc telegraf.Accumulator) {
|
||||
defer func() {
|
||||
s.removeConnection(conn)
|
||||
conn.Close()
|
||||
}()
|
||||
|
||||
if s.ReadTimeout != nil && s.ReadTimeout.Duration > 0 {
|
||||
conn.SetReadDeadline(time.Now().Add(s.ReadTimeout.Duration))
|
||||
}
|
||||
|
||||
var p *rfc5425.Parser
|
||||
if s.BestEffort {
|
||||
p = rfc5425.NewParser(conn, rfc5425.WithBestEffort())
|
||||
} else {
|
||||
p = rfc5425.NewParser(conn)
|
||||
}
|
||||
|
||||
p.ParseExecuting(func(r *rfc5425.Result) {
|
||||
s.store(*r, acc)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Syslog) setKeepAlive(c *net.TCPConn) error {
|
||||
if s.KeepAlivePeriod == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.KeepAlivePeriod.Duration == 0 {
|
||||
return c.SetKeepAlive(false)
|
||||
}
|
||||
if err := c.SetKeepAlive(true); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SetKeepAlivePeriod(s.KeepAlivePeriod.Duration)
|
||||
}
|
||||
|
||||
func (s *Syslog) store(res rfc5425.Result, acc telegraf.Accumulator) {
|
||||
if res.Error != nil {
|
||||
acc.AddError(res.Error)
|
||||
}
|
||||
if res.MessageError != nil {
|
||||
acc.AddError(res.MessageError)
|
||||
}
|
||||
if res.Message != nil {
|
||||
msg := *res.Message
|
||||
acc.AddFields("syslog", fields(msg, s), tags(msg), s.time())
|
||||
}
|
||||
}
|
||||
|
||||
func tags(msg rfc5424.SyslogMessage) map[string]string {
|
||||
ts := map[string]string{}
|
||||
|
||||
// Not checking assuming a minimally valid message
|
||||
ts["severity"] = *msg.SeverityShortLevel()
|
||||
ts["facility"] = *msg.FacilityLevel()
|
||||
|
||||
if msg.Hostname() != nil {
|
||||
ts["hostname"] = *msg.Hostname()
|
||||
}
|
||||
|
||||
if msg.Appname() != nil {
|
||||
ts["appname"] = *msg.Appname()
|
||||
}
|
||||
|
||||
return ts
|
||||
}
|
||||
|
||||
func fields(msg rfc5424.SyslogMessage, s *Syslog) map[string]interface{} {
|
||||
// Not checking assuming a minimally valid message
|
||||
flds := map[string]interface{}{
|
||||
"version": msg.Version(),
|
||||
}
|
||||
flds["severity_code"] = int(*msg.Severity())
|
||||
flds["facility_code"] = int(*msg.Facility())
|
||||
|
||||
if msg.Timestamp() != nil {
|
||||
flds["timestamp"] = (*msg.Timestamp()).UnixNano()
|
||||
}
|
||||
|
||||
if msg.ProcID() != nil {
|
||||
flds["procid"] = *msg.ProcID()
|
||||
}
|
||||
|
||||
if msg.MsgID() != nil {
|
||||
flds["msgid"] = *msg.MsgID()
|
||||
}
|
||||
|
||||
if msg.Message() != nil {
|
||||
flds["message"] = *msg.Message()
|
||||
}
|
||||
|
||||
if msg.StructuredData() != nil {
|
||||
for sdid, sdparams := range *msg.StructuredData() {
|
||||
if len(sdparams) == 0 {
|
||||
// When SD-ID does not have params we indicate its presence with a bool
|
||||
flds[sdid] = true
|
||||
continue
|
||||
}
|
||||
for name, value := range sdparams {
|
||||
// Using whitespace as separator since it is not allowed by the grammar within SDID
|
||||
flds[sdid+s.Separator+name] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flds
|
||||
}
|
||||
|
||||
type unixCloser struct {
|
||||
path string
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
func (uc unixCloser) Close() error {
|
||||
err := uc.closer.Close()
|
||||
os.Remove(uc.path) // ignore error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Syslog) time() time.Time {
|
||||
t := s.now()
|
||||
if t == s.lastTime {
|
||||
t = t.Add(time.Nanosecond)
|
||||
}
|
||||
s.lastTime = t
|
||||
return t
|
||||
}
|
||||
|
||||
func getNanoNow() time.Time {
|
||||
return time.Unix(0, time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func init() {
|
||||
receiver := &Syslog{
|
||||
Address: ":6514",
|
||||
now: getNanoNow,
|
||||
ReadTimeout: &internal.Duration{
|
||||
Duration: defaultReadTimeout,
|
||||
},
|
||||
Separator: "_",
|
||||
}
|
||||
|
||||
inputs.Add("syslog", func() telegraf.Input { return receiver })
|
||||
}
|
||||
68
plugins/inputs/syslog/syslog_test.go
Normal file
68
plugins/inputs/syslog/syslog_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package syslog
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
address = ":6514"
|
||||
)
|
||||
|
||||
var defaultTime = time.Unix(0, 0)
|
||||
var maxP = uint8(191)
|
||||
var maxV = uint16(999)
|
||||
var maxTS = "2017-12-31T23:59:59.999999+00:00"
|
||||
var maxH = "abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabc"
|
||||
var maxA = "abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdef"
|
||||
var maxPID = "abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzab"
|
||||
var maxMID = "abcdefghilmnopqrstuvzabcdefghilm"
|
||||
var message7681 = strings.Repeat("l", 7681)
|
||||
|
||||
func TestAddress(t *testing.T) {
|
||||
var err error
|
||||
var rec *Syslog
|
||||
|
||||
rec = &Syslog{
|
||||
Address: "localhost:6514",
|
||||
}
|
||||
err = rec.Start(&testutil.Accumulator{})
|
||||
require.EqualError(t, err, "missing protocol within address 'localhost:6514'")
|
||||
require.Error(t, err)
|
||||
|
||||
rec = &Syslog{
|
||||
Address: "unsupported://example.com:6514",
|
||||
}
|
||||
err = rec.Start(&testutil.Accumulator{})
|
||||
require.EqualError(t, err, "unknown protocol 'unsupported' in 'example.com:6514'")
|
||||
require.Error(t, err)
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
require.NoError(t, err)
|
||||
sock := filepath.Join(tmpdir, "syslog.TestAddress.sock")
|
||||
|
||||
rec = &Syslog{
|
||||
Address: "unixgram://" + sock,
|
||||
}
|
||||
err = rec.Start(&testutil.Accumulator{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, sock, rec.Address)
|
||||
rec.Stop()
|
||||
|
||||
// Default port is 6514
|
||||
rec = &Syslog{
|
||||
Address: "tcp://localhost",
|
||||
}
|
||||
err = rec.Start(&testutil.Accumulator{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "localhost:6514", rec.Address)
|
||||
rec.Stop()
|
||||
}
|
||||
@@ -20,7 +20,7 @@ This plugin gathers stats from [Varnish HTTP Cache](https://varnish-cache.org/)
|
||||
|
||||
## Optional name for the varnish instance (or working directory) to query
|
||||
## Usually appened after -n in varnish cli
|
||||
#name = instanceName
|
||||
# instance_name = instanceName
|
||||
```
|
||||
|
||||
### Measurements & Fields:
|
||||
|
||||
@@ -48,7 +48,7 @@ var sampleConfig = `
|
||||
|
||||
## Optional name for the varnish instance (or working directory) to query
|
||||
## Usually appened after -n in varnish cli
|
||||
#name = instanceName
|
||||
# instance_name = instanceName
|
||||
`
|
||||
|
||||
func (s *Varnish) Description() string {
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
# win_perf_counters readme
|
||||
|
||||
The way this plugin works is that on load of Telegraf,
|
||||
the plugin will be handed configuration from Telegraf.
|
||||
This configuration is parsed and then tested for validity such as
|
||||
if the Object, Instance and Counter existing.
|
||||
If it does not match at startup, it will not be fetched.
|
||||
Exceptions to this are in cases where you query for all instances "*".
|
||||
By default the plugin does not return _Total
|
||||
when it is querying for all (*) as this is redundant.
|
||||
Input plugin to read Performance Counters on Windows operating systems.
|
||||
|
||||
Configuration is parsed and then tested for validity such as
|
||||
whether the Object, Instance and Counter exist on Telegraf startup.
|
||||
|
||||
Counter paths are refreshed periodically, see [CountersRefreshInterval](#countersrefreshinterval)
|
||||
configuration parameter for more info.
|
||||
|
||||
In case of query for all instances `["*"]`, the plugin does not return the instance `_Total`
|
||||
by default. See [IncludeTotal](#includetotal) for more info.
|
||||
|
||||
## Basics
|
||||
|
||||
The examples contained in this file have been found on the internet
|
||||
as counters used when performance monitoring
|
||||
Active Directory and IIS in perticular.
|
||||
Active Directory and IIS in particular.
|
||||
There are a lot other good objects to monitor, if you know what to look for.
|
||||
This file is likely to be updated in the future with more examples for
|
||||
useful configurations for separate scenarios.
|
||||
@@ -29,9 +31,41 @@ Bool, if set to `true` will print out all matching performance objects.
|
||||
Example:
|
||||
`PrintValid=true`
|
||||
|
||||
#### UseWildcardsExpansion
|
||||
|
||||
If `UseWildcardsExpansion` is set to true, wildcards can be used in the
|
||||
instance name and the counter name. When using localized Windows, counters
|
||||
will be also be localized. Instance indexes will also be returned in the
|
||||
instance name.
|
||||
|
||||
Partial wildcards (e.g. `chrome*`) are supported only in the instance name on Windows Vista and newer.
|
||||
|
||||
If disabled, wildcards (not partial) in instance names can still be used, but
|
||||
instance indexes will not be returned in the instance names.
|
||||
|
||||
Example:
|
||||
`UseWildcardsExpansion=true`
|
||||
|
||||
#### CountersRefreshInterval
|
||||
|
||||
Configured counters are matched against available counters at the interval
|
||||
specified by the `CountersRefreshInterval` parameter. Default value is `1m` (1 minute).
|
||||
|
||||
If wildcards are used in instance or counter names, they are expanded at this point, if the `UseWildcardsExpansion` param is set to `true`.
|
||||
|
||||
Setting `CountersRefreshInterval` too low (order of seconds) can cause Telegraf to create
|
||||
a high CPU load.
|
||||
|
||||
Set to `0s` to disable periodic refreshing.
|
||||
|
||||
Example:
|
||||
`CountersRefreshInterval=1m`
|
||||
|
||||
#### PreVistaSupport
|
||||
|
||||
Bool, if set to `true` will use the localized PerfCounter interface that is present before Vista for backwards compatability.
|
||||
_Deprecated. Necessary features on Windows Vista and newer are checked dynamically_
|
||||
|
||||
Bool, if set to `true` will use the localized PerfCounter interface that has been present since before Vista for backwards compatability.
|
||||
|
||||
It is recommended NOT to use this on OSes starting with Vista and newer because it requires more configuration to use this than the newer interface present since Vista.
|
||||
|
||||
@@ -45,10 +79,10 @@ See Entry below.
|
||||
### Entry
|
||||
A new configuration entry consists of the TOML header to start with,
|
||||
`[[inputs.win_perf_counters.object]]`.
|
||||
This must follow before other plugins configuration,
|
||||
This must follow before other plugin configurations,
|
||||
beneath the main win_perf_counters entry, `[[inputs.win_perf_counters]]`.
|
||||
|
||||
Following this is 3 required key/value pairs and the three optional parameters and their usage.
|
||||
Following this are 3 required key/value pairs and the three optional parameters and their usage.
|
||||
|
||||
#### ObjectName
|
||||
**Required**
|
||||
@@ -60,37 +94,41 @@ Example: `ObjectName = "LogicalDisk"`
|
||||
#### Instances
|
||||
**Required**
|
||||
|
||||
Instances (this is an array) is the instances of a counter you would like returned,
|
||||
Instances key (this is an array) is the instances of a counter you would like returned,
|
||||
it can be one or more values.
|
||||
|
||||
Example, `Instances = ["C:","D:","E:"]` will return only for the instances
|
||||
C:, D: and E: where relevant. To get all instances of a Counter, use ["*"] only.
|
||||
By default any results containing _Total are stripped,
|
||||
C:, D: and E: where relevant. To get all instances of a Counter, use `["*"]` only.
|
||||
By default any results containing `_Total` are stripped,
|
||||
unless this is specified as the wanted instance.
|
||||
Alternatively see the option IncludeTotal below.
|
||||
Alternatively see the option `IncludeTotal` below.
|
||||
|
||||
Some Objects does not have instances to select from at all,
|
||||
here only one option is valid if you want data back,
|
||||
It is also possible to set partial wildcards, eg. `["chrome*"]`, if the `UseWildcardsExpansion` param is set to `true`
|
||||
|
||||
Some Objects do not have instances to select from at all.
|
||||
Here only one option is valid if you want data back,
|
||||
and that is to specify `Instances = ["------"]`.
|
||||
|
||||
#### Counters
|
||||
**Required**
|
||||
|
||||
Counters (this is an array) is the counters of the ObjectName
|
||||
Counters key (this is an array) is the counters of the ObjectName
|
||||
you would like returned, it can also be one or more values.
|
||||
|
||||
Example: `Counters = ["% Idle Time", "% Disk Read Time", "% Disk Write Time"]`
|
||||
This must be specified for every counter you want the results of,
|
||||
it is not possible to ask for all counters in the ObjectName.
|
||||
|
||||
This must be specified for every counter you want the results of, or use
|
||||
`["*"]` for all the counters for object, if the `UseWildcardsExpansion` param
|
||||
is set to `true`
|
||||
|
||||
#### Measurement
|
||||
*Optional*
|
||||
|
||||
This key is optional, if it is not set it will be win_perf_counters.
|
||||
This key is optional, if it is not set it will be `win_perf_counters`.
|
||||
In InfluxDB this is the key by which the returned data is stored underneath,
|
||||
so for ordering your data in a good manner,
|
||||
this is a good key to set with where you want your IIS and Disk results stored,
|
||||
separate from Processor results.
|
||||
this is a good key to set with a value when you want your IIS and Disk results stored
|
||||
separately from Processor results.
|
||||
|
||||
Example: `Measurement = "win_disk"
|
||||
|
||||
@@ -99,9 +137,9 @@ Example: `Measurement = "win_disk"
|
||||
|
||||
This key is optional, it is a simple bool.
|
||||
If it is not set to true or included it is treated as false.
|
||||
This key only has an effect if Instances is set to "*"
|
||||
and you would also like all instances containg _Total returned,
|
||||
like "_Total", "0,_Total" and so on where applicable
|
||||
This key only has an effect if the Instances key is set to `["*"]`
|
||||
and you would also like all instances containing `_Total` returned,
|
||||
like `_Total`, `0,_Total` and so on where applicable
|
||||
(Processor Information is one example).
|
||||
|
||||
#### WarnOnMissing
|
||||
@@ -111,13 +149,13 @@ This key is optional, it is a simple bool.
|
||||
If it is not set to true or included it is treated as false.
|
||||
This only has an effect on the first execution of the plugin,
|
||||
it will print out any ObjectName/Instance/Counter combinations
|
||||
asked for that does not match. Useful when debugging new configurations.
|
||||
asked for that do not match. Useful when debugging new configurations.
|
||||
|
||||
#### FailOnMissing
|
||||
*Internal*
|
||||
|
||||
This key should not be used, it is for testing purposes only.
|
||||
It is a simple bool, if it is not set to true or included this is treaded as false.
|
||||
This key should not be used. It is for testing purposes only.
|
||||
It is a simple bool. If it is not set to true or included this is treated as false.
|
||||
If this is set to true, the plugin will abort and end prematurely
|
||||
if any of the combinations of ObjectName/Instances/Counters are invalid.
|
||||
|
||||
@@ -337,10 +375,14 @@ if any of the combinations of ObjectName/Instances/Counters are invalid.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you are getting an error about an invalid counter, use the `typeperf` command to check the counter path
|
||||
on the command line.
|
||||
E.g. `typeperf "Process(chrome*)\% Processor Time"`
|
||||
|
||||
If no metrics are emitted even with the default config, you may need to repair
|
||||
your performance counters.
|
||||
|
||||
1. Launch Command Prompt as Administrator (right click Runs As Administrator).
|
||||
1. Launch the Command Prompt as Administrator (right click Runs As Administrator).
|
||||
1. Drop into the C:\WINDOWS\System32 directory by typing `C:` then `cd \Windows\System32`
|
||||
1. Rebuild your counter values, which may take a few moments so please be
|
||||
patient, by running:
|
||||
|
||||
@@ -161,43 +161,6 @@ type (
|
||||
PDH_HCOUNTER HANDLE // counter handle
|
||||
)
|
||||
|
||||
// Union specialization for double values
|
||||
type PDH_FMT_COUNTERVALUE_DOUBLE struct {
|
||||
CStatus uint32
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// Union specialization for 64 bit integer values
|
||||
type PDH_FMT_COUNTERVALUE_LARGE struct {
|
||||
CStatus uint32
|
||||
LargeValue int64
|
||||
}
|
||||
|
||||
// Union specialization for long values
|
||||
type PDH_FMT_COUNTERVALUE_LONG struct {
|
||||
CStatus uint32
|
||||
LongValue int32
|
||||
padding [4]byte
|
||||
}
|
||||
|
||||
// Union specialization for double values, used by PdhGetFormattedCounterArrayDouble()
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
}
|
||||
|
||||
// Union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge()
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_LARGE struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PDH_FMT_COUNTERVALUE_LARGE
|
||||
}
|
||||
|
||||
// Union specialization for long values, used by PdhGetFormattedCounterArrayLong()
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_LONG struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PDH_FMT_COUNTERVALUE_LONG
|
||||
}
|
||||
|
||||
var (
|
||||
// Library
|
||||
libpdhDll *syscall.DLL
|
||||
@@ -211,6 +174,8 @@ var (
|
||||
pdh_GetFormattedCounterArrayW *syscall.Proc
|
||||
pdh_OpenQuery *syscall.Proc
|
||||
pdh_ValidatePathW *syscall.Proc
|
||||
pdh_ExpandWildCardPathW *syscall.Proc
|
||||
pdh_GetCounterInfoW *syscall.Proc
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -226,9 +191,11 @@ func init() {
|
||||
pdh_GetFormattedCounterArrayW = libpdhDll.MustFindProc("PdhGetFormattedCounterArrayW")
|
||||
pdh_OpenQuery = libpdhDll.MustFindProc("PdhOpenQuery")
|
||||
pdh_ValidatePathW = libpdhDll.MustFindProc("PdhValidatePathW")
|
||||
pdh_ExpandWildCardPathW = libpdhDll.MustFindProc("PdhExpandWildCardPathW")
|
||||
pdh_GetCounterInfoW = libpdhDll.MustFindProc("PdhGetCounterInfoW")
|
||||
}
|
||||
|
||||
// Adds the specified counter to the query. This is the internationalized version. Preferably, use the
|
||||
// PdhAddCounter adds the specified counter to the query. This is the internationalized version. Preferably, use the
|
||||
// function PdhAddEnglishCounter instead. hQuery is the query handle, which has been fetched by PdhOpenQuery.
|
||||
// szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version).
|
||||
// dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value
|
||||
@@ -277,7 +244,14 @@ func PdhAddCounter(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintp
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// Adds the specified language-neutral counter to the query. See the PdhAddCounter function. This function only exists on
|
||||
// PdhAddEnglishCounterSupported returns true if PdhAddEnglishCounterW Win API function was found in pdh.dll.
|
||||
// PdhAddEnglishCounterW function is not supported on pre-Windows Vista systems
|
||||
|
||||
func PdhAddEnglishCounterSupported() bool {
|
||||
return pdh_AddEnglishCounterW != nil
|
||||
}
|
||||
|
||||
// PdhAddEnglishCounter adds the specified language-neutral counter to the query. See the PdhAddCounter function. This function only exists on
|
||||
// Windows versions higher than Vista.
|
||||
func PdhAddEnglishCounter(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 {
|
||||
if pdh_AddEnglishCounterW == nil {
|
||||
@@ -294,7 +268,7 @@ func PdhAddEnglishCounter(hQuery PDH_HQUERY, szFullCounterPath string, dwUserDat
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// Closes all counters contained in the specified query, closes all handles related to the query,
|
||||
// PdhCloseQuery closes all counters contained in the specified query, closes all handles related to the query,
|
||||
// and frees all memory associated with the query.
|
||||
func PdhCloseQuery(hQuery PDH_HQUERY) uint32 {
|
||||
ret, _, _ := pdh_CloseQuery.Call(uintptr(hQuery))
|
||||
@@ -329,7 +303,7 @@ func PdhCollectQueryData(hQuery PDH_HQUERY) uint32 {
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// Formats the given hCounter using a 'double'. The result is set into the specialized union struct pValue.
|
||||
// PdhGetFormattedCounterValueDouble formats the given hCounter using a 'double'. The result is set into the specialized union struct pValue.
|
||||
// This function does not directly translate to a Windows counterpart due to union specialization tricks.
|
||||
func PdhGetFormattedCounterValueDouble(hCounter PDH_HCOUNTER, lpdwType *uint32, pValue *PDH_FMT_COUNTERVALUE_DOUBLE) uint32 {
|
||||
ret, _, _ := pdh_GetFormattedCounterValue.Call(
|
||||
@@ -341,7 +315,7 @@ func PdhGetFormattedCounterValueDouble(hCounter PDH_HCOUNTER, lpdwType *uint32,
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// Returns an array of formatted counter values. Use this function when you want to format the counter values of a
|
||||
// PdhGetFormattedCounterArrayDouble returns an array of formatted counter values. Use this function when you want to format the counter values of a
|
||||
// counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE.
|
||||
// An example of how this function can be used:
|
||||
//
|
||||
@@ -378,7 +352,7 @@ func PdhGetFormattedCounterValueDouble(hCounter PDH_HCOUNTER, lpdwType *uint32,
|
||||
// time.Sleep(2000 * time.Millisecond)
|
||||
// }
|
||||
// }
|
||||
func PdhGetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *PDH_FMT_COUNTERVALUE_ITEM_DOUBLE) uint32 {
|
||||
func PdhGetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
|
||||
ret, _, _ := pdh_GetFormattedCounterArrayW.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(PDH_FMT_DOUBLE|PDH_FMT_NOCAP100),
|
||||
@@ -389,7 +363,7 @@ func PdhGetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER, lpdwBufferSize *ui
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// Creates a new query that is used to manage the collection of performance data.
|
||||
// PdhOpenQuery creates a new query that is used to manage the collection of performance data.
|
||||
// szDataSource is a null terminated string that specifies the name of the log file from which to
|
||||
// retrieve the performance data. If 0, performance data is collected from a real-time data source.
|
||||
// dwUserData is a user-defined value to associate with this query. To retrieve the user data later,
|
||||
@@ -405,7 +379,51 @@ func PdhOpenQuery(szDataSource uintptr, dwUserData uintptr, phQuery *PDH_HQUERY)
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// Validates a path. Will return ERROR_SUCCESS when ok, or PDH_CSTATUS_BAD_COUNTERNAME when the path is
|
||||
//PdhExpandWildCardPath examines the specified computer or log file and returns those counter paths that match the given counter path which contains wildcard characters.
|
||||
//The general counter path format is as follows:
|
||||
//
|
||||
//\\computer\object(parent/instance#index)\counter
|
||||
//
|
||||
//The parent, instance, index, and counter components of the counter path may contain either a valid name or a wildcard character. The computer, parent, instance,
|
||||
// and index components are not necessary for all counters.
|
||||
//
|
||||
//The following is a list of the possible formats:
|
||||
//
|
||||
//\\computer\object(parent/instance#index)\counter
|
||||
//\\computer\object(parent/instance)\counter
|
||||
//\\computer\object(instance#index)\counter
|
||||
//\\computer\object(instance)\counter
|
||||
//\\computer\object\counter
|
||||
//\object(parent/instance#index)\counter
|
||||
//\object(parent/instance)\counter
|
||||
//\object(instance#index)\counter
|
||||
//\object(instance)\counter
|
||||
//\object\counter
|
||||
//Use an asterisk (*) as the wildcard character, for example, \object(*)\counter.
|
||||
//
|
||||
//If a wildcard character is specified in the parent name, all instances of the specified object that match the specified instance and counter fields will be returned.
|
||||
// For example, \object(*/instance)\counter.
|
||||
//
|
||||
//If a wildcard character is specified in the instance name, all instances of the specified object and parent object will be returned if all instance names
|
||||
// corresponding to the specified index match the wildcard character. For example, \object(parent/*)\counter. If the object does not contain an instance, an error occurs.
|
||||
//
|
||||
//If a wildcard character is specified in the counter name, all counters of the specified object are returned.
|
||||
//
|
||||
//Partial counter path string matches (for example, "pro*") are supported.
|
||||
func PdhExpandWildCardPath(szWildCardPath string, mszExpandedPathList *uint16, pcchPathListLength *uint32) uint32 {
|
||||
ptxt, _ := syscall.UTF16PtrFromString(szWildCardPath)
|
||||
flags := uint32(0) // expand instances and counters
|
||||
ret, _, _ := pdh_ExpandWildCardPathW.Call(
|
||||
uintptr(unsafe.Pointer(nil)), // search counters on local computer
|
||||
uintptr(unsafe.Pointer(ptxt)),
|
||||
uintptr(unsafe.Pointer(mszExpandedPathList)),
|
||||
uintptr(unsafe.Pointer(pcchPathListLength)),
|
||||
uintptr(unsafe.Pointer(&flags)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhValidatePath validates a path. Will return ERROR_SUCCESS when ok, or PDH_CSTATUS_BAD_COUNTERNAME when the path is
|
||||
// erroneous.
|
||||
func PdhValidatePath(path string) uint32 {
|
||||
ptxt, _ := syscall.UTF16PtrFromString(path)
|
||||
@@ -414,13 +432,6 @@ func PdhValidatePath(path string) uint32 {
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
func UTF16PtrToString(s *uint16) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:])
|
||||
}
|
||||
|
||||
func PdhFormatError(msgId uint32) string {
|
||||
var flags uint32 = windows.FORMAT_MESSAGE_FROM_HMODULE | windows.FORMAT_MESSAGE_ARGUMENT_ARRAY | windows.FORMAT_MESSAGE_IGNORE_INSERTS
|
||||
buf := make([]uint16, 300)
|
||||
@@ -430,3 +441,25 @@ func PdhFormatError(msgId uint32) string {
|
||||
}
|
||||
return fmt.Sprintf("(pdhErr=%d) %s", msgId, err.Error())
|
||||
}
|
||||
|
||||
//Retrieves information about a counter, such as data size, counter type, path, and user-supplied data values
|
||||
//hCounter [in]
|
||||
//Handle of the counter from which you want to retrieve information. The PdhAddCounter function returns this handle.
|
||||
//
|
||||
//bRetrieveExplainText [in]
|
||||
//Determines whether explain text is retrieved. If you set this parameter to TRUE, the explain text for the counter is retrieved. If you set this parameter to FALSE, the field in the returned buffer is NULL.
|
||||
//
|
||||
//pdwBufferSize [in, out]
|
||||
//Size of the lpBuffer buffer, in bytes. If zero on input, the function returns PDH_MORE_DATA and sets this parameter to the required buffer size. If the buffer is larger than the required size, the function sets this parameter to the actual size of the buffer that was used. If the specified size on input is greater than zero but less than the required size, you should not rely on the returned size to reallocate the buffer.
|
||||
//
|
||||
//lpBuffer [out]
|
||||
//Caller-allocated buffer that receives a PDH_COUNTER_INFO structure. The structure is variable-length, because the string data is appended to the end of the fixed-format portion of the structure. This is done so that all data is returned in a single buffer allocated by the caller. Set to NULL if pdwBufferSize is zero.
|
||||
func PdhGetCounterInfo(hCounter PDH_HCOUNTER, bRetrieveExplainText int, pdwBufferSize *uint32, lpBuffer *byte) uint32 {
|
||||
ret, _, _ := pdh_GetCounterInfoW.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(bRetrieveExplainText),
|
||||
uintptr(unsafe.Pointer(pdwBufferSize)),
|
||||
uintptr(unsafe.Pointer(lpBuffer)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
121
plugins/inputs/win_perf_counters/pdh_386.go
Normal file
121
plugins/inputs/win_perf_counters/pdh_386.go
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2010 The win Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
// +build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
// Union specialization for double values
|
||||
type PDH_FMT_COUNTERVALUE_DOUBLE struct {
|
||||
CStatus uint32
|
||||
padding [4]byte
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// Union specialization for 64 bit integer values
|
||||
type PDH_FMT_COUNTERVALUE_LARGE struct {
|
||||
CStatus uint32
|
||||
padding [4]byte
|
||||
LargeValue int64
|
||||
}
|
||||
|
||||
// Union specialization for long values
|
||||
type PDH_FMT_COUNTERVALUE_LONG struct {
|
||||
CStatus uint32
|
||||
LongValue int32
|
||||
padding [4]byte
|
||||
}
|
||||
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct {
|
||||
SzName *uint16
|
||||
padding [4]byte
|
||||
FmtValue PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
}
|
||||
|
||||
// Union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge()
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_LARGE struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
padding [4]byte
|
||||
FmtValue PDH_FMT_COUNTERVALUE_LARGE
|
||||
}
|
||||
|
||||
// Union specialization for long values, used by PdhGetFormattedCounterArrayLong()
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_LONG struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
padding [4]byte
|
||||
FmtValue PDH_FMT_COUNTERVALUE_LONG
|
||||
}
|
||||
|
||||
//PDH_COUNTER_INFO structure contains information describing the properties of a counter. This information also includes the counter path.
|
||||
type PDH_COUNTER_INFO struct {
|
||||
//Size of the structure, including the appended strings, in bytes.
|
||||
DwLength uint32
|
||||
//Counter type. For a list of counter types, see the Counter Types section of the <a "href=http://go.microsoft.com/fwlink/p/?linkid=84422">Windows Server 2003 Deployment Kit</a>.
|
||||
//The counter type constants are defined in Winperf.h.
|
||||
DwType uint32
|
||||
//Counter version information. Not used.
|
||||
CVersion uint32
|
||||
//Counter status that indicates if the counter value is valid. For a list of possible values,
|
||||
//see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
|
||||
CStatus uint32
|
||||
//Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
|
||||
//The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to
|
||||
//PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned
|
||||
LScale int32
|
||||
//Default scale factor as suggested by the counter's provider.
|
||||
LDefaultScale int32
|
||||
//The value passed in the dwUserData parameter when calling PdhAddCounter.
|
||||
DwUserData *uint32
|
||||
//The value passed in the dwUserData parameter when calling PdhOpenQuery.
|
||||
DwQueryUserData *uint32
|
||||
//Null-terminated string that specifies the full counter path. The string follows this structure in memory.
|
||||
SzFullPath *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
|
||||
//The string follows this structure in memory.
|
||||
SzMachineName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
|
||||
SzObjectName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
|
||||
//The string follows this structure in memory.
|
||||
SzInstanceName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the parent instance specified in the counter path. Is NULL, if the path does not specify a parent instance.
|
||||
//The string follows this structure in memory.
|
||||
SzParentInstance *uint16 // pointer to a string
|
||||
//Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
|
||||
DwInstanceIndex uint32 // pointer to a string
|
||||
//Null-terminated string that contains the counter name. The string follows this structure in memory.
|
||||
SzCounterName *uint16 // pointer to a string
|
||||
//padding
|
||||
Padding [4]byte
|
||||
//Help text that describes the counter. Is NULL if the source is a log file.
|
||||
SzExplainText *uint16 // pointer to a string
|
||||
//Start of the string data that is appended to the structure.
|
||||
DataBuffer [1]uint32 // pointer to an extra space
|
||||
}
|
||||
114
plugins/inputs/win_perf_counters/pdh_amd64.go
Normal file
114
plugins/inputs/win_perf_counters/pdh_amd64.go
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2010 The win Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
// +build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
// Union specialization for double values
|
||||
type PDH_FMT_COUNTERVALUE_DOUBLE struct {
|
||||
CStatus uint32
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// Union specialization for 64 bit integer values
|
||||
type PDH_FMT_COUNTERVALUE_LARGE struct {
|
||||
CStatus uint32
|
||||
LargeValue int64
|
||||
}
|
||||
|
||||
// Union specialization for long values
|
||||
type PDH_FMT_COUNTERVALUE_LONG struct {
|
||||
CStatus uint32
|
||||
LongValue int32
|
||||
padding [4]byte
|
||||
}
|
||||
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct {
|
||||
SzName *uint16
|
||||
FmtValue PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
}
|
||||
|
||||
// Union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge()
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_LARGE struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PDH_FMT_COUNTERVALUE_LARGE
|
||||
}
|
||||
|
||||
// Union specialization for long values, used by PdhGetFormattedCounterArrayLong()
|
||||
type PDH_FMT_COUNTERVALUE_ITEM_LONG struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PDH_FMT_COUNTERVALUE_LONG
|
||||
}
|
||||
|
||||
//PDH_COUNTER_INFO structure contains information describing the properties of a counter. This information also includes the counter path.
|
||||
type PDH_COUNTER_INFO struct {
|
||||
//Size of the structure, including the appended strings, in bytes.
|
||||
DwLength uint32
|
||||
//Counter type. For a list of counter types, see the Counter Types section of the <a "href=http://go.microsoft.com/fwlink/p/?linkid=84422">Windows Server 2003 Deployment Kit</a>.
|
||||
//The counter type constants are defined in Winperf.h.
|
||||
DwType uint32
|
||||
//Counter version information. Not used.
|
||||
CVersion uint32
|
||||
//Counter status that indicates if the counter value is valid. For a list of possible values,
|
||||
//see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
|
||||
CStatus uint32
|
||||
//Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
|
||||
//The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to
|
||||
//PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned
|
||||
LScale int32
|
||||
//Default scale factor as suggested by the counter's provider.
|
||||
LDefaultScale int32
|
||||
//The value passed in the dwUserData parameter when calling PdhAddCounter.
|
||||
DwUserData *uint32
|
||||
//The value passed in the dwUserData parameter when calling PdhOpenQuery.
|
||||
DwQueryUserData *uint32
|
||||
//Null-terminated string that specifies the full counter path. The string follows this structure in memory.
|
||||
SzFullPath *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
|
||||
//The string follows this structure in memory.
|
||||
SzMachineName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
|
||||
SzObjectName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
|
||||
//The string follows this structure in memory.
|
||||
SzInstanceName *uint16 // pointer to a string
|
||||
//Null-terminated string that contains the name of the parent instance specified in the counter path. Is NULL, if the path does not specify a parent instance.
|
||||
//The string follows this structure in memory.
|
||||
SzParentInstance *uint16 // pointer to a string
|
||||
//Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
|
||||
DwInstanceIndex uint32 // pointer to a string
|
||||
//Null-terminated string that contains the counter name. The string follows this structure in memory.
|
||||
SzCounterName *uint16 // pointer to a string
|
||||
//Help text that describes the counter. Is NULL if the source is a log file.
|
||||
SzExplainText *uint16 // pointer to a string
|
||||
//Start of the string data that is appended to the structure.
|
||||
DataBuffer [1]uint32 // pointer to an extra space
|
||||
}
|
||||
218
plugins/inputs/win_perf_counters/performance_query.go
Normal file
218
plugins/inputs/win_perf_counters/performance_query.go
Normal file
@@ -0,0 +1,218 @@
|
||||
// Go API over pdh syscalls
|
||||
// +build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//PerformanceQuery is abstraction for PDH_FMT_COUNTERVALUE_ITEM_DOUBLE
|
||||
type CounterValue struct {
|
||||
InstanceName string
|
||||
Value float64
|
||||
}
|
||||
|
||||
//PerformanceQuery provides wrappers around Windows performance counters API for easy usage in GO
|
||||
type PerformanceQuery interface {
|
||||
Open() error
|
||||
Close() error
|
||||
AddCounterToQuery(counterPath string) (PDH_HCOUNTER, error)
|
||||
AddEnglishCounterToQuery(counterPath string) (PDH_HCOUNTER, error)
|
||||
GetCounterPath(counterHandle PDH_HCOUNTER) (string, error)
|
||||
ExpandWildCardPath(counterPath string) ([]string, error)
|
||||
GetFormattedCounterValueDouble(hCounter PDH_HCOUNTER) (float64, error)
|
||||
GetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER) ([]CounterValue, error)
|
||||
CollectData() error
|
||||
AddEnglishCounterSupported() bool
|
||||
}
|
||||
|
||||
//PdhError represents error returned from Performance Counters API
|
||||
type PdhError struct {
|
||||
ErrorCode uint32
|
||||
errorText string
|
||||
}
|
||||
|
||||
func (m *PdhError) Error() string {
|
||||
return m.errorText
|
||||
}
|
||||
|
||||
func NewPdhError(code uint32) error {
|
||||
return &PdhError{
|
||||
ErrorCode: code,
|
||||
errorText: PdhFormatError(code),
|
||||
}
|
||||
}
|
||||
|
||||
//PerformanceQueryImpl is implementation of PerformanceQuery interface, which calls phd.dll functions
|
||||
type PerformanceQueryImpl struct {
|
||||
query PDH_HQUERY
|
||||
}
|
||||
|
||||
// Open creates a new counterPath that is used to manage the collection of performance data.
|
||||
// It returns counterPath handle used for subsequent calls for adding counters and querying data
|
||||
func (m *PerformanceQueryImpl) Open() error {
|
||||
if m.query != 0 {
|
||||
err := m.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var handle PDH_HQUERY
|
||||
ret := PdhOpenQuery(0, 0, &handle)
|
||||
if ret != ERROR_SUCCESS {
|
||||
return NewPdhError(ret)
|
||||
}
|
||||
m.query = handle
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the counterPath, releases associated counter handles and frees resources
|
||||
func (m *PerformanceQueryImpl) Close() error {
|
||||
if m.query == 0 {
|
||||
return errors.New("uninitialised query")
|
||||
}
|
||||
ret := PdhCloseQuery(m.query)
|
||||
if ret != ERROR_SUCCESS {
|
||||
return NewPdhError(ret)
|
||||
}
|
||||
m.query = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PerformanceQueryImpl) AddCounterToQuery(counterPath string) (PDH_HCOUNTER, error) {
|
||||
var counterHandle PDH_HCOUNTER
|
||||
if m.query == 0 {
|
||||
return 0, errors.New("uninitialised query")
|
||||
}
|
||||
ret := PdhAddCounter(m.query, counterPath, 0, &counterHandle)
|
||||
if ret != ERROR_SUCCESS {
|
||||
return 0, NewPdhError(ret)
|
||||
}
|
||||
return counterHandle, nil
|
||||
}
|
||||
|
||||
func (m *PerformanceQueryImpl) AddEnglishCounterToQuery(counterPath string) (PDH_HCOUNTER, error) {
|
||||
var counterHandle PDH_HCOUNTER
|
||||
if m.query == 0 {
|
||||
return 0, errors.New("uninitialised query")
|
||||
}
|
||||
ret := PdhAddEnglishCounter(m.query, counterPath, 0, &counterHandle)
|
||||
if ret != ERROR_SUCCESS {
|
||||
return 0, NewPdhError(ret)
|
||||
}
|
||||
return counterHandle, nil
|
||||
}
|
||||
|
||||
//GetCounterPath return counter information for given handle
|
||||
func (m *PerformanceQueryImpl) GetCounterPath(counterHandle PDH_HCOUNTER) (string, error) {
|
||||
var bufSize uint32
|
||||
var buff []byte
|
||||
|
||||
ret := PdhGetCounterInfo(counterHandle, 0, &bufSize, nil)
|
||||
if ret == PDH_MORE_DATA {
|
||||
buff = make([]byte, bufSize)
|
||||
bufSize = uint32(len(buff))
|
||||
ret = PdhGetCounterInfo(counterHandle, 0, &bufSize, &buff[0])
|
||||
if ret == ERROR_SUCCESS {
|
||||
ci := (*PDH_COUNTER_INFO)(unsafe.Pointer(&buff[0]))
|
||||
return UTF16PtrToString(ci.SzFullPath), nil
|
||||
}
|
||||
}
|
||||
return "", NewPdhError(ret)
|
||||
}
|
||||
|
||||
// ExpandWildCardPath examines local computer and returns those counter paths that match the given counter path which contains wildcard characters.
|
||||
func (m *PerformanceQueryImpl) ExpandWildCardPath(counterPath string) ([]string, error) {
|
||||
var bufSize uint32
|
||||
var buff []uint16
|
||||
|
||||
ret := PdhExpandWildCardPath(counterPath, nil, &bufSize)
|
||||
if ret == PDH_MORE_DATA {
|
||||
buff = make([]uint16, bufSize)
|
||||
bufSize = uint32(len(buff))
|
||||
ret = PdhExpandWildCardPath(counterPath, &buff[0], &bufSize)
|
||||
if ret == ERROR_SUCCESS {
|
||||
list := UTF16ToStringArray(buff)
|
||||
return list, nil
|
||||
}
|
||||
}
|
||||
return nil, NewPdhError(ret)
|
||||
}
|
||||
|
||||
//GetFormattedCounterValueDouble computes a displayable value for the specified counter
|
||||
func (m *PerformanceQueryImpl) GetFormattedCounterValueDouble(hCounter PDH_HCOUNTER) (float64, error) {
|
||||
var counterType uint32
|
||||
var value PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
ret := PdhGetFormattedCounterValueDouble(hCounter, &counterType, &value)
|
||||
if ret == ERROR_SUCCESS {
|
||||
if value.CStatus == PDH_CSTATUS_VALID_DATA || value.CStatus == PDH_CSTATUS_NEW_DATA {
|
||||
return value.DoubleValue, nil
|
||||
} else {
|
||||
return 0, NewPdhError(value.CStatus)
|
||||
}
|
||||
} else {
|
||||
return 0, NewPdhError(ret)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *PerformanceQueryImpl) GetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER) ([]CounterValue, error) {
|
||||
var buffSize uint32
|
||||
var itemCount uint32
|
||||
ret := PdhGetFormattedCounterArrayDouble(hCounter, &buffSize, &itemCount, nil)
|
||||
if ret == PDH_MORE_DATA {
|
||||
buff := make([]byte, buffSize)
|
||||
ret = PdhGetFormattedCounterArrayDouble(hCounter, &buffSize, &itemCount, &buff[0])
|
||||
if ret == ERROR_SUCCESS {
|
||||
items := (*[1 << 20]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE)(unsafe.Pointer(&buff[0]))[:itemCount]
|
||||
values := make([]CounterValue, 0, itemCount)
|
||||
for _, item := range items {
|
||||
if item.FmtValue.CStatus == PDH_CSTATUS_VALID_DATA || item.FmtValue.CStatus == PDH_CSTATUS_NEW_DATA {
|
||||
val := CounterValue{UTF16PtrToString(item.SzName), item.FmtValue.DoubleValue}
|
||||
values = append(values, val)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
}
|
||||
return nil, NewPdhError(ret)
|
||||
}
|
||||
|
||||
func (m *PerformanceQueryImpl) CollectData() error {
|
||||
if m.query == 0 {
|
||||
return errors.New("uninitialised query")
|
||||
}
|
||||
ret := PdhCollectQueryData(m.query)
|
||||
if ret != ERROR_SUCCESS {
|
||||
return NewPdhError(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PerformanceQueryImpl) AddEnglishCounterSupported() bool {
|
||||
return PdhAddEnglishCounterSupported()
|
||||
}
|
||||
|
||||
// UTF16PtrToString converts Windows API LPTSTR (pointer to string) to go string
|
||||
func UTF16PtrToString(s *uint16) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:])
|
||||
}
|
||||
|
||||
// UTF16ToStringArray converts list of Windows API NULL terminated strings to go string array
|
||||
func UTF16ToStringArray(buf []uint16) []string {
|
||||
var strings []string
|
||||
nextLineStart := 0
|
||||
stringLine := UTF16PtrToString(&buf[0])
|
||||
for stringLine != "" {
|
||||
strings = append(strings, stringLine)
|
||||
nextLineStart += len([]rune(stringLine)) + 1
|
||||
remainingBuf := buf[nextLineStart:]
|
||||
stringLine = UTF16PtrToString(&remainingBuf[0])
|
||||
}
|
||||
return strings
|
||||
}
|
||||
@@ -5,11 +5,13 @@ package win_perf_counters
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var sampleConfig = `
|
||||
@@ -20,6 +22,12 @@ var sampleConfig = `
|
||||
## agent, it will not be gathered.
|
||||
## Settings:
|
||||
# PrintValid = false # Print All matching performance counters
|
||||
# If UseWildcardsExpansion params is set to true, wildcards (partial wildcards in instance names and wildcards in counters names) in configured counter paths will be expanded
|
||||
# and in case of localized Windows, counter paths will be also localized. It also returns instance indexes in instance names.
|
||||
# If false, wildcards (not partial) in instance names will be still expanded, but instance indexes will not be returned in instance names.
|
||||
#UseWildcardsExpansion = false
|
||||
# Period after which counters will be reread from configuration and wildcards in counter paths expanded
|
||||
CountersRefreshInterval="1m"
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# Processor usage, alternative to native, reports on a per core.
|
||||
@@ -53,7 +61,7 @@ var sampleConfig = `
|
||||
Measurement = "win_system"
|
||||
|
||||
[[inputs.win_perf_counters.object]]
|
||||
# Example query where the Instance portion must be removed to get data back,
|
||||
# Example counterPath where the Instance portion must be removed to get data back,
|
||||
# such as from the Memory object.
|
||||
ObjectName = "Memory"
|
||||
Counters = [
|
||||
@@ -61,17 +69,21 @@ var sampleConfig = `
|
||||
"Page Faults/sec", "Pages/sec", "Transition Faults/sec",
|
||||
"Pool Nonpaged Bytes", "Pool Paged Bytes"
|
||||
]
|
||||
Instances = ["------"] # Use 6 x - to remove the Instance bit from the query.
|
||||
Instances = ["------"] # Use 6 x - to remove the Instance bit from the counterPath.
|
||||
Measurement = "win_mem"
|
||||
`
|
||||
|
||||
type Win_PerfCounters struct {
|
||||
PrintValid bool
|
||||
PreVistaSupport bool
|
||||
Object []perfobject
|
||||
PrintValid bool
|
||||
//deprecated: determined dynamically
|
||||
PreVistaSupport bool
|
||||
Object []perfobject
|
||||
CountersRefreshInterval internal.Duration
|
||||
UseWildcardsExpansion bool
|
||||
|
||||
configParsed bool
|
||||
itemCache []*item
|
||||
lastRefreshed time.Time
|
||||
counters []*counter
|
||||
query PerformanceQuery
|
||||
}
|
||||
|
||||
type perfobject struct {
|
||||
@@ -84,56 +96,115 @@ type perfobject struct {
|
||||
IncludeTotal bool
|
||||
}
|
||||
|
||||
type item struct {
|
||||
query string
|
||||
type counter struct {
|
||||
counterPath string
|
||||
objectName string
|
||||
counter string
|
||||
instance string
|
||||
measurement string
|
||||
include_total bool
|
||||
handle PDH_HQUERY
|
||||
includeTotal bool
|
||||
counterHandle PDH_HCOUNTER
|
||||
}
|
||||
|
||||
var sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec",
|
||||
" ", "_", "%", "Percent", `\`, "")
|
||||
|
||||
func (m *Win_PerfCounters) AddItem(query string, objectName string, counter string, instance string,
|
||||
measurement string, include_total bool) error {
|
||||
//General Counter path pattern is: \\computer\object(parent/instance#index)\counter
|
||||
//parent/instance#index part is skipped in single instance objects (e.g. Memory): \\computer\object\counter
|
||||
|
||||
var handle PDH_HQUERY
|
||||
var counterHandle PDH_HCOUNTER
|
||||
ret := PdhOpenQuery(0, 0, &handle)
|
||||
if m.PreVistaSupport {
|
||||
ret = PdhAddCounter(handle, query, 0, &counterHandle)
|
||||
var counterPathRE = regexp.MustCompile(`.*\\(.*)\\(.*)`)
|
||||
var objectInstanceRE = regexp.MustCompile(`(.*)\((.*)\)`)
|
||||
|
||||
//extractObjectInstanceCounterFromQuery gets object name, instance name (if available) and counter name from counter path
|
||||
func extractObjectInstanceCounterFromQuery(query string) (object string, instance string, counter string, err error) {
|
||||
pathParts := counterPathRE.FindAllStringSubmatch(query, -1)
|
||||
if pathParts == nil || len(pathParts[0]) != 3 {
|
||||
err = errors.New("Could not extract counter info from: " + query)
|
||||
return
|
||||
}
|
||||
counter = pathParts[0][2]
|
||||
//try to get instance name
|
||||
objectInstanceParts := objectInstanceRE.FindAllStringSubmatch(pathParts[0][1], -1)
|
||||
if objectInstanceParts == nil || len(objectInstanceParts[0]) != 3 {
|
||||
object = pathParts[0][1]
|
||||
} else {
|
||||
ret = PdhAddEnglishCounter(handle, query, 0, &counterHandle)
|
||||
object = objectInstanceParts[0][1]
|
||||
instance = objectInstanceParts[0][2]
|
||||
}
|
||||
|
||||
// Call PdhCollectQueryData one time to check existence of the counter
|
||||
ret = PdhCollectQueryData(handle)
|
||||
if ret != ERROR_SUCCESS {
|
||||
PdhCloseQuery(handle)
|
||||
return errors.New(PdhFormatError(ret))
|
||||
}
|
||||
|
||||
newItem := &item{query, objectName, counter, instance, measurement,
|
||||
include_total, handle, counterHandle}
|
||||
m.itemCache = append(m.itemCache, newItem)
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Win_PerfCounters) Description() string {
|
||||
return "Input plugin to query Performance Counters on Windows operating systems"
|
||||
return "Input plugin to counterPath Performance Counters on Windows operating systems"
|
||||
}
|
||||
|
||||
func (m *Win_PerfCounters) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
//objectName string, counter string, instance string, measurement string, include_total bool
|
||||
func (m *Win_PerfCounters) AddItem(counterPath string, objectName string, instance string, counterName string, measurement string, includeTotal bool) error {
|
||||
var err error
|
||||
var counterHandle PDH_HCOUNTER
|
||||
if !m.query.AddEnglishCounterSupported() {
|
||||
counterHandle, err = m.query.AddCounterToQuery(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
counterHandle, err = m.query.AddEnglishCounterToQuery(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if m.UseWildcardsExpansion {
|
||||
origInstance := instance
|
||||
counterPath, err = m.query.GetCounterPath(counterHandle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
counters, err := m.query.ExpandWildCardPath(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, counterPath := range counters {
|
||||
var err error
|
||||
counterHandle, err := m.query.AddCounterToQuery(counterPath)
|
||||
|
||||
objectName, instance, counterName, err = extractObjectInstanceCounterFromQuery(counterPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if instance == "_Total" && origInstance == "*" && !includeTotal {
|
||||
continue
|
||||
}
|
||||
|
||||
newItem := &counter{counterPath, objectName, counterName, instance, measurement,
|
||||
includeTotal, counterHandle}
|
||||
m.counters = append(m.counters, newItem)
|
||||
|
||||
if m.PrintValid {
|
||||
log.Printf("Valid: %s\n", counterPath)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newItem := &counter{counterPath, objectName, counterName, instance, measurement,
|
||||
includeTotal, counterHandle}
|
||||
m.counters = append(m.counters, newItem)
|
||||
if m.PrintValid {
|
||||
log.Printf("Valid: %s\n", counterPath)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Win_PerfCounters) ParseConfig() error {
|
||||
var query string
|
||||
var counterPath string
|
||||
|
||||
if len(m.Object) > 0 {
|
||||
for _, PerfObject := range m.Object {
|
||||
@@ -142,21 +213,16 @@ func (m *Win_PerfCounters) ParseConfig() error {
|
||||
objectname := PerfObject.ObjectName
|
||||
|
||||
if instance == "------" {
|
||||
query = "\\" + objectname + "\\" + counter
|
||||
counterPath = "\\" + objectname + "\\" + counter
|
||||
} else {
|
||||
query = "\\" + objectname + "(" + instance + ")\\" + counter
|
||||
counterPath = "\\" + objectname + "(" + instance + ")\\" + counter
|
||||
}
|
||||
|
||||
err := m.AddItem(query, objectname, counter, instance,
|
||||
PerfObject.Measurement, PerfObject.IncludeTotal)
|
||||
err := m.AddItem(counterPath, objectname, instance, counter, PerfObject.Measurement, PerfObject.IncludeTotal)
|
||||
|
||||
if err == nil {
|
||||
if m.PrintValid {
|
||||
fmt.Printf("Valid: %s\n", query)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
if PerfObject.FailOnMissing || PerfObject.WarnOnMissing {
|
||||
fmt.Printf("Invalid query: '%s'. Error: %s", query, err.Error())
|
||||
log.Printf("Invalid counterPath: '%s'. Error: %s\n", counterPath, err.Error())
|
||||
}
|
||||
if PerfObject.FailOnMissing {
|
||||
return err
|
||||
@@ -165,32 +231,41 @@ func (m *Win_PerfCounters) ParseConfig() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
err := errors.New("No performance objects configured!")
|
||||
err := errors.New("no performance objects configured")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Win_PerfCounters) GetParsedItemsForTesting() []*item {
|
||||
return m.itemCache
|
||||
}
|
||||
|
||||
func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error {
|
||||
// Parse the config once
|
||||
if !m.configParsed {
|
||||
err := m.ParseConfig()
|
||||
m.configParsed = true
|
||||
var err error
|
||||
|
||||
if m.lastRefreshed.IsZero() || (m.CountersRefreshInterval.Duration.Nanoseconds() > 0 && m.lastRefreshed.Add(m.CountersRefreshInterval.Duration).Before(time.Now())) {
|
||||
if m.counters != nil {
|
||||
m.counters = m.counters[:0]
|
||||
}
|
||||
|
||||
err = m.query.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var bufSize uint32
|
||||
var bufCount uint32
|
||||
var size uint32 = uint32(unsafe.Sizeof(PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
|
||||
var emptyBuf [1]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
|
||||
err = m.ParseConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//some counters need two data samples before computing a value
|
||||
err = m.query.CollectData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.lastRefreshed = time.Now()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
type InstanceGrouping struct {
|
||||
name string
|
||||
@@ -200,78 +275,79 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error {
|
||||
|
||||
var collectFields = make(map[InstanceGrouping]map[string]interface{})
|
||||
|
||||
err = m.query.CollectData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// For iterate over the known metrics and get the samples.
|
||||
for _, metric := range m.itemCache {
|
||||
for _, metric := range m.counters {
|
||||
// collect
|
||||
ret := PdhCollectQueryData(metric.handle)
|
||||
if ret == ERROR_SUCCESS {
|
||||
ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize,
|
||||
&bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN.
|
||||
if ret == PDH_MORE_DATA {
|
||||
filledBuf := make([]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
|
||||
if len(filledBuf) == 0 {
|
||||
continue
|
||||
if m.UseWildcardsExpansion {
|
||||
value, err := m.query.GetFormattedCounterValueDouble(metric.counterHandle)
|
||||
if err == nil {
|
||||
measurement := sanitizedChars.Replace(metric.measurement)
|
||||
if measurement == "" {
|
||||
measurement = "win_perf_counters"
|
||||
}
|
||||
ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle,
|
||||
&bufSize, &bufCount, &filledBuf[0])
|
||||
for i := 0; i < int(bufCount); i++ {
|
||||
c := filledBuf[i]
|
||||
var s string = UTF16PtrToString(c.SzName)
|
||||
|
||||
var instance = InstanceGrouping{measurement, metric.instance, metric.objectName}
|
||||
if collectFields[instance] == nil {
|
||||
collectFields[instance] = make(map[string]interface{})
|
||||
}
|
||||
collectFields[instance][sanitizedChars.Replace(metric.counter)] = float32(value)
|
||||
} else {
|
||||
//ignore invalid data from as some counters from process instances returns this sometimes
|
||||
if phderr, ok := err.(*PdhError); ok && phderr.ErrorCode != PDH_INVALID_DATA && phderr.ErrorCode != PDH_CALC_NEGATIVE_VALUE {
|
||||
return fmt.Errorf("error while getting value for counter %s: %v", metric.counterPath, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
counterValues, err := m.query.GetFormattedCounterArrayDouble(metric.counterHandle)
|
||||
if err == nil {
|
||||
for _, cValue := range counterValues {
|
||||
var add bool
|
||||
|
||||
if metric.include_total {
|
||||
if metric.includeTotal {
|
||||
// If IncludeTotal is set, include all.
|
||||
add = true
|
||||
} else if metric.instance == "*" && !strings.Contains(s, "_Total") {
|
||||
} else if metric.instance == "*" && !strings.Contains(cValue.InstanceName, "_Total") {
|
||||
// Catch if set to * and that it is not a '*_Total*' instance.
|
||||
add = true
|
||||
} else if metric.instance == s {
|
||||
} else if metric.instance == cValue.InstanceName {
|
||||
// Catch if we set it to total or some form of it
|
||||
add = true
|
||||
} else if strings.Contains(metric.instance, "#") && strings.HasPrefix(metric.instance, s) {
|
||||
} else if strings.Contains(metric.instance, "#") && strings.HasPrefix(metric.instance, cValue.InstanceName) {
|
||||
// If you are using a multiple instance identifier such as "w3wp#1"
|
||||
// phd.dll returns only the first 2 characters of the identifier.
|
||||
add = true
|
||||
s = metric.instance
|
||||
cValue.InstanceName = metric.instance
|
||||
} else if metric.instance == "------" {
|
||||
add = true
|
||||
}
|
||||
|
||||
if add {
|
||||
tags := make(map[string]string)
|
||||
if s != "" {
|
||||
tags["instance"] = s
|
||||
}
|
||||
tags["objectname"] = metric.objectName
|
||||
|
||||
measurement := sanitizedChars.Replace(metric.measurement)
|
||||
if measurement == "" {
|
||||
measurement = "win_perf_counters"
|
||||
}
|
||||
var instance = InstanceGrouping{measurement, s, metric.objectName}
|
||||
var instance = InstanceGrouping{measurement, cValue.InstanceName, metric.objectName}
|
||||
|
||||
if collectFields[instance] == nil {
|
||||
collectFields[instance] = make(map[string]interface{})
|
||||
}
|
||||
collectFields[instance][sanitizedChars.Replace(metric.counter)] = float32(c.FmtValue.DoubleValue)
|
||||
collectFields[instance][sanitizedChars.Replace(metric.counter)] = float32(cValue.Value)
|
||||
}
|
||||
}
|
||||
|
||||
filledBuf = nil
|
||||
// Need to at least set bufSize to zero, because if not, the function will not
|
||||
// return PDH_MORE_DATA and will not set the bufSize.
|
||||
bufCount = 0
|
||||
bufSize = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for instance, fields := range collectFields {
|
||||
var tags = map[string]string{
|
||||
"instance": instance.instance,
|
||||
"objectname": instance.objectname,
|
||||
}
|
||||
if len(instance.instance) > 0 {
|
||||
tags["instance"] = instance.instance
|
||||
}
|
||||
acc.AddFields(instance.name, fields, tags)
|
||||
}
|
||||
|
||||
@@ -279,5 +355,7 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error {
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("win_perf_counters", func() telegraf.Input { return &Win_PerfCounters{} })
|
||||
inputs.Add("win_perf_counters", func() telegraf.Input {
|
||||
return &Win_PerfCounters{query: &PerformanceQueryImpl{}, CountersRefreshInterval: internal.Duration{Duration: time.Second * 60}}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,615 @@
|
||||
// +build windows
|
||||
|
||||
package win_perf_counters
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func TestWinPerformanceQueryImpl(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
var query PerformanceQuery
|
||||
var hCounter PDH_HCOUNTER
|
||||
var err error
|
||||
query = &PerformanceQueryImpl{}
|
||||
|
||||
err = query.Close()
|
||||
require.Error(t, err, "uninitialized query must return errors")
|
||||
|
||||
_, err = query.AddCounterToQuery("")
|
||||
require.Error(t, err, "uninitialized query must return errors")
|
||||
assert.True(t, strings.Contains(err.Error(), "uninitialised"))
|
||||
|
||||
_, err = query.AddEnglishCounterToQuery("")
|
||||
require.Error(t, err, "uninitialized query must return errors")
|
||||
assert.True(t, strings.Contains(err.Error(), "uninitialised"))
|
||||
|
||||
err = query.CollectData()
|
||||
require.Error(t, err, "uninitialized query must return errors")
|
||||
assert.True(t, strings.Contains(err.Error(), "uninitialised"))
|
||||
|
||||
err = query.Open()
|
||||
require.NoError(t, err)
|
||||
|
||||
counterPath := "\\Processor Information(_Total)\\% Processor Time"
|
||||
|
||||
hCounter, err = query.AddCounterToQuery(counterPath)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, 0, hCounter)
|
||||
|
||||
err = query.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = query.Open()
|
||||
require.NoError(t, err)
|
||||
|
||||
hCounter, err = query.AddEnglishCounterToQuery(counterPath)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, 0, hCounter)
|
||||
|
||||
cp, err := query.GetCounterPath(hCounter)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasSuffix(cp, counterPath))
|
||||
|
||||
err = query.CollectData()
|
||||
require.NoError(t, err)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
err = query.CollectData()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = query.GetFormattedCounterValueDouble(hCounter)
|
||||
require.NoError(t, err)
|
||||
|
||||
counterPath = "\\Process(*)\\% Processor Time"
|
||||
paths, err := query.ExpandWildCardPath(counterPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, paths)
|
||||
assert.True(t, len(paths) > 1)
|
||||
|
||||
counterPath = "\\Process(_Total)\\*"
|
||||
paths, err = query.ExpandWildCardPath(counterPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, paths)
|
||||
assert.True(t, len(paths) > 1)
|
||||
|
||||
err = query.Open()
|
||||
require.NoError(t, err)
|
||||
|
||||
counterPath = "\\Process(*)\\% Processor Time"
|
||||
hCounter, err = query.AddEnglishCounterToQuery(counterPath)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, 0, hCounter)
|
||||
|
||||
err = query.CollectData()
|
||||
require.NoError(t, err)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
err = query.CollectData()
|
||||
require.NoError(t, err)
|
||||
|
||||
arr, err := query.GetFormattedCounterArrayDouble(hCounter)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, len(arr) > 0, "Too")
|
||||
|
||||
err = query.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigGet1(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
counters[0] = "% Processor Time"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigGet2(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
counters[0] = "% Processor Time"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(m.counters) == 1 {
|
||||
require.NoError(t, nil)
|
||||
} else if len(m.counters) == 0 {
|
||||
var errorstring1 = "No results returned from the counterPath: " + string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
} else if len(m.counters) > 1 {
|
||||
var errorstring1 = "Too many results returned from the counterPath: " + string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigGet3(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 2)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
counters[0] = "% Processor Time"
|
||||
counters[1] = "% Idle Time"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(m.counters) == 2 {
|
||||
require.NoError(t, nil)
|
||||
} else if len(m.counters) < 2 {
|
||||
|
||||
var errorstring1 = "Too few results returned from the counterPath. " + string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
} else if len(m.counters) > 2 {
|
||||
|
||||
var errorstring1 = "Too many results returned from the counterPath: " + string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigGet4(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 2)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
instances[1] = "0,1"
|
||||
counters[0] = "% Processor Time"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(m.counters) == 2 {
|
||||
require.NoError(t, nil)
|
||||
} else if len(m.counters) < 2 {
|
||||
|
||||
var errorstring1 = "Too few results returned from the counterPath: " + string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
} else if len(m.counters) > 2 {
|
||||
|
||||
var errorstring1 = "Too many results returned from the counterPath: " + string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigGet5(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 2)
|
||||
var counters = make([]string, 2)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
instances[1] = "0,1"
|
||||
counters[0] = "% Processor Time"
|
||||
counters[1] = "% Idle Time"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(m.counters) == 4 {
|
||||
require.NoError(t, nil)
|
||||
} else if len(m.counters) < 4 {
|
||||
var errorstring1 = "Too few results returned from the counterPath: " +
|
||||
string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
} else if len(m.counters) > 4 {
|
||||
var errorstring1 = "Too many results returned from the counterPath: " +
|
||||
string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigGet6(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "System"
|
||||
instances[0] = "------"
|
||||
counters[0] = "Context Switches/sec"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigGet7(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 3)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
counters[0] = "% Processor Time"
|
||||
counters[1] = "% Processor TimeERROR"
|
||||
counters[2] = "% Idle Time"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
objectname,
|
||||
counters,
|
||||
instances,
|
||||
measurement,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(m.counters) == 2 {
|
||||
require.NoError(t, nil)
|
||||
} else if len(m.counters) < 2 {
|
||||
var errorstring1 = "Too few results returned from the counterPath: " +
|
||||
string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
} else if len(m.counters) > 2 {
|
||||
var errorstring1 = "Too many results returned from the counterPath: " +
|
||||
string(len(m.counters))
|
||||
err2 := errors.New(errorstring1)
|
||||
require.NoError(t, err2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigError1(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor InformationERROR"
|
||||
instances[0] = "_Total"
|
||||
counters[0] = "% Processor Time"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigError2(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor"
|
||||
instances[0] = "SuperERROR"
|
||||
counters[0] = "% C1 Time"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
var acc testutil.Accumulator
|
||||
err = m.Gather(&acc)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestWinPerfcountersConfigError3(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
counters[0] = "% Processor TimeERROR"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
m.query.Open()
|
||||
|
||||
err := m.ParseConfig()
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestWinPerfcountersCollect1(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
var instances = make([]string, 1)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
counters[0] = "Parking Status"
|
||||
|
||||
var expectedCounter = "Parking_Status"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}}
|
||||
var acc testutil.Accumulator
|
||||
err := m.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
err = m.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, acc.Metrics, 2)
|
||||
|
||||
for _, metric := range acc.Metrics {
|
||||
_, ok := metric.Fields[expectedCounter]
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
}
|
||||
func TestWinPerfcountersCollect2(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var instances = make([]string, 2)
|
||||
var counters = make([]string, 1)
|
||||
var perfobjects = make([]perfobject, 1)
|
||||
|
||||
objectname := "Processor Information"
|
||||
instances[0] = "_Total"
|
||||
instances[1] = "0,0"
|
||||
counters[0] = "Performance Limit Flags"
|
||||
|
||||
var expectedCounter = "Performance_Limit_Flags"
|
||||
|
||||
var measurement = "test"
|
||||
|
||||
PerfObject := perfobject{
|
||||
ObjectName: objectname,
|
||||
Instances: instances,
|
||||
Counters: counters,
|
||||
Measurement: measurement,
|
||||
WarnOnMissing: false,
|
||||
FailOnMissing: true,
|
||||
IncludeTotal: false,
|
||||
}
|
||||
|
||||
perfobjects[0] = PerfObject
|
||||
|
||||
m := Win_PerfCounters{PrintValid: false, Object: perfobjects, query: &PerformanceQueryImpl{}, UseWildcardsExpansion: true}
|
||||
var acc testutil.Accumulator
|
||||
err := m.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
err = m.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, acc.Metrics, 4)
|
||||
|
||||
for _, metric := range acc.Metrics {
|
||||
_, ok := metric.Fields[expectedCounter]
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,44 +2,82 @@
|
||||
|
||||
This plugin writes to a AMQP 0-9-1 Exchange, a promenent implementation of this protocol being [RabbitMQ](https://www.rabbitmq.com/).
|
||||
|
||||
Metrics are written to a topic exchange using tag, defined in configuration file as RoutingTag, as a routing key.
|
||||
|
||||
If RoutingTag is empty, then empty routing key will be used.
|
||||
Metrics are grouped in batches by RoutingTag.
|
||||
|
||||
This plugin doesn't bind exchange to a queue, so it should be done by consumer.
|
||||
This plugin does not bind the exchange to a queue.
|
||||
|
||||
For an introduction to AMQP see:
|
||||
- https://www.rabbitmq.com/tutorials/amqp-concepts.html
|
||||
- https://www.rabbitmq.com/getstarted.html
|
||||
|
||||
### Configuration:
|
||||
|
||||
```
|
||||
# Configuration for the AMQP server to send metrics to
|
||||
```toml
|
||||
# Publishes metrics to an AMQP broker
|
||||
[[outputs.amqp]]
|
||||
## AMQP url
|
||||
url = "amqp://localhost:5672/influxdb"
|
||||
## AMQP exchange
|
||||
## Broker to publish to.
|
||||
## deprecated in 1.7; use the brokers option
|
||||
# url = "amqp://localhost:5672/influxdb"
|
||||
|
||||
## Brokers to publish to. If multiple brokers are specified a random broker
|
||||
## will be selected anytime a connection is established. This can be
|
||||
## helpful for load balancing when not using a dedicated load balancer.
|
||||
brokers = ["amqp://localhost:5672/influxdb"]
|
||||
|
||||
## Maximum messages to send over a connection. Once this is reached, the
|
||||
## connection is closed and a new connection is made. This can be helpful for
|
||||
## load balancing when not using a dedicated load balancer.
|
||||
# max_messages = 0
|
||||
|
||||
## Exchange to declare and publish to.
|
||||
exchange = "telegraf"
|
||||
|
||||
## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
|
||||
# exchange_type = "topic"
|
||||
|
||||
## If true, exchange will be passively declared.
|
||||
# exchange_declare_passive = false
|
||||
|
||||
## If true, exchange will be created as a durable exchange.
|
||||
# exchange_durable = true
|
||||
|
||||
## Additional exchange arguments.
|
||||
# exchange_arguments = { }
|
||||
# exchange_arguments = {"hash_propery" = "timestamp"}
|
||||
|
||||
## Authentication credentials for the PLAIN auth_method.
|
||||
# username = ""
|
||||
# password = ""
|
||||
|
||||
## 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"
|
||||
## Telegraf tag to use as a routing key
|
||||
## ie, if this tag exists, its value will be used as the routing key
|
||||
routing_tag = "host"
|
||||
## Delivery Mode controls if a published message is persistent
|
||||
## Valid options are "transient" and "persistent". default: "transient"
|
||||
|
||||
## Metric tag to use as a routing key.
|
||||
## ie, if this tag exists, its value will be used as the routing key
|
||||
# routing_tag = "host"
|
||||
|
||||
## Static routing key. Used when no routing_tag is set or as a fallback
|
||||
## when the tag specified in routing tag is not found.
|
||||
# routing_key = ""
|
||||
# routing_key = "telegraf"
|
||||
|
||||
## Delivery Mode controls if a published message is persistent.
|
||||
## One of "transient" or "persistent".
|
||||
# delivery_mode = "transient"
|
||||
|
||||
## InfluxDB retention policy
|
||||
# retention_policy = "default"
|
||||
## InfluxDB database
|
||||
## InfluxDB database added as a message header.
|
||||
## deprecated in 1.7; use the headers option
|
||||
# database = "telegraf"
|
||||
|
||||
## Write timeout, formatted as a string. If not provided, will default
|
||||
## to 5s. 0s means no timeout (not recommended).
|
||||
## InfluxDB retention policy added as a message header
|
||||
## deprecated in 1.7; use the headers option
|
||||
# retention_policy = "default"
|
||||
|
||||
## Static headers added to each published message.
|
||||
# headers = { }
|
||||
# headers = {"database" = "telegraf", "retention_policy" = "default"}
|
||||
|
||||
## Connection timeout. If not provided, will default to 5s. 0s means no
|
||||
## timeout (not recommended).
|
||||
# timeout = "5s"
|
||||
|
||||
## Optional TLS Config
|
||||
@@ -49,9 +87,25 @@ For an introduction to AMQP see:
|
||||
## Use TLS but skip chain & host verification
|
||||
# insecure_skip_verify = false
|
||||
|
||||
## If true use batch serialization format instead of line based delimiting.
|
||||
## Only applies to data formats which are not line based such as JSON.
|
||||
## Recommended to set to true.
|
||||
# use_batch_format = 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"
|
||||
# data_format = "influx"
|
||||
```
|
||||
|
||||
#### Routing
|
||||
|
||||
If `routing_tag` is set, and the tag is defined on the metric, the value of
|
||||
the tag is used as the routing key. Otherwise the value of `routing_key` is
|
||||
used directly. If both are unset the empty string is used.
|
||||
|
||||
Exchange types that do not use a routing key, `direct` and `header`, always
|
||||
use the empty string as the routing key.
|
||||
|
||||
Metrics are published in batches based on the final routing key.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package amqp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
@@ -17,80 +16,126 @@ import (
|
||||
"github.com/streadway/amqp"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
conn *amqp.Connection
|
||||
channel *amqp.Channel
|
||||
headers amqp.Table
|
||||
}
|
||||
|
||||
type AMQP struct {
|
||||
// AMQP brokers to send metrics to
|
||||
URL string
|
||||
// AMQP exchange
|
||||
Exchange string
|
||||
// AMQP Auth method
|
||||
AuthMethod string
|
||||
// Routing Key Tag
|
||||
RoutingTag string `toml:"routing_tag"`
|
||||
// InfluxDB database
|
||||
Database string
|
||||
// InfluxDB retention policy
|
||||
RetentionPolicy string
|
||||
// InfluxDB precision (DEPRECATED)
|
||||
Precision string
|
||||
// Connection timeout
|
||||
Timeout internal.Duration
|
||||
// Delivery Mode controls if a published message is persistent
|
||||
// Valid options are "transient" and "persistent". default: "transient"
|
||||
DeliveryMode string
|
||||
|
||||
tls.ClientConfig
|
||||
|
||||
sync.Mutex
|
||||
c *client
|
||||
|
||||
deliveryMode uint8
|
||||
serializer serializers.Serializer
|
||||
}
|
||||
const (
|
||||
DefaultURL = "amqp://localhost:5672/influxdb"
|
||||
DefaultAuthMethod = "PLAIN"
|
||||
DefaultExchangeType = "topic"
|
||||
DefaultRetentionPolicy = "default"
|
||||
DefaultDatabase = "telegraf"
|
||||
)
|
||||
|
||||
type externalAuth struct{}
|
||||
|
||||
func (a *externalAuth) Mechanism() string {
|
||||
return "EXTERNAL"
|
||||
}
|
||||
|
||||
func (a *externalAuth) Response() string {
|
||||
return fmt.Sprintf("\000")
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultAuthMethod = "PLAIN"
|
||||
DefaultRetentionPolicy = "default"
|
||||
DefaultDatabase = "telegraf"
|
||||
)
|
||||
type AMQP struct {
|
||||
URL string `toml:"url"` // deprecated in 1.7; use brokers
|
||||
Brokers []string `toml:"brokers"`
|
||||
Exchange string `toml:"exchange"`
|
||||
ExchangeType string `toml:"exchange_type"`
|
||||
ExchangePassive bool `toml:"exchange_passive"`
|
||||
ExchangeDurability string `toml:"exchange_durability"`
|
||||
ExchangeArguments map[string]string `toml:"exchange_arguments"`
|
||||
Username string `toml:"username"`
|
||||
Password string `toml:"password"`
|
||||
MaxMessages int `toml:"max_messages"`
|
||||
AuthMethod string `toml:"auth_method"`
|
||||
RoutingTag string `toml:"routing_tag"`
|
||||
RoutingKey string `toml:"routing_key"`
|
||||
DeliveryMode string `toml:"delivery_mode"`
|
||||
Database string `toml:"database"` // deprecated in 1.7; use headers
|
||||
RetentionPolicy string `toml:"retention_policy"` // deprecated in 1.7; use headers
|
||||
Precision string `toml:"precision"` // deprecated; has no effect
|
||||
Headers map[string]string `toml:"headers"`
|
||||
Timeout internal.Duration `toml:"timeout"`
|
||||
UseBatchFormat bool `toml:"use_batch_format"`
|
||||
tls.ClientConfig
|
||||
|
||||
serializer serializers.Serializer
|
||||
connect func(*ClientConfig) (Client, error)
|
||||
client Client
|
||||
config *ClientConfig
|
||||
sentMessages int
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
Publish(key string, body []byte) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
var sampleConfig = `
|
||||
## AMQP url
|
||||
url = "amqp://localhost:5672/influxdb"
|
||||
## AMQP exchange
|
||||
## Broker to publish to.
|
||||
## deprecated in 1.7; use the brokers option
|
||||
# url = "amqp://localhost:5672/influxdb"
|
||||
|
||||
## Brokers to publish to. If multiple brokers are specified a random broker
|
||||
## will be selected anytime a connection is established. This can be
|
||||
## helpful for load balancing when not using a dedicated load balancer.
|
||||
brokers = ["amqp://localhost:5672/influxdb"]
|
||||
|
||||
## Maximum messages to send over a connection. Once this is reached, the
|
||||
## connection is closed and a new connection is made. This can be helpful for
|
||||
## load balancing when not using a dedicated load balancer.
|
||||
# max_messages = 0
|
||||
|
||||
## Exchange to declare and publish to.
|
||||
exchange = "telegraf"
|
||||
|
||||
## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
|
||||
# exchange_type = "topic"
|
||||
|
||||
## If true, exchange will be passively declared.
|
||||
# exchange_declare_passive = false
|
||||
|
||||
## If true, exchange will be created as a durable exchange.
|
||||
# exchange_durable = true
|
||||
|
||||
## Additional exchange arguments.
|
||||
# exchange_arguments = { }
|
||||
# exchange_arguments = {"hash_propery" = "timestamp"}
|
||||
|
||||
## Authentication credentials for the PLAIN auth_method.
|
||||
# username = ""
|
||||
# password = ""
|
||||
|
||||
## 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"
|
||||
## Telegraf tag to use as a routing key
|
||||
## ie, if this tag exists, its value will be used as the routing key
|
||||
routing_tag = "host"
|
||||
## Delivery Mode controls if a published message is persistent
|
||||
## Valid options are "transient" and "persistent". default: "transient"
|
||||
delivery_mode = "transient"
|
||||
|
||||
## InfluxDB retention policy
|
||||
# retention_policy = "default"
|
||||
## InfluxDB database
|
||||
## Metric tag to use as a routing key.
|
||||
## ie, if this tag exists, its value will be used as the routing key
|
||||
# routing_tag = "host"
|
||||
|
||||
## Static routing key. Used when no routing_tag is set or as a fallback
|
||||
## when the tag specified in routing tag is not found.
|
||||
# routing_key = ""
|
||||
# routing_key = "telegraf"
|
||||
|
||||
## Delivery Mode controls if a published message is persistent.
|
||||
## One of "transient" or "persistent".
|
||||
# delivery_mode = "transient"
|
||||
|
||||
## InfluxDB database added as a message header.
|
||||
## deprecated in 1.7; use the headers option
|
||||
# database = "telegraf"
|
||||
|
||||
## Write timeout, formatted as a string. If not provided, will default
|
||||
## to 5s. 0s means no timeout (not recommended).
|
||||
## InfluxDB retention policy added as a message header
|
||||
## deprecated in 1.7; use the headers option
|
||||
# retention_policy = "default"
|
||||
|
||||
## Static headers added to each published message.
|
||||
# headers = { }
|
||||
# headers = {"database" = "telegraf", "retention_policy" = "default"}
|
||||
|
||||
## Connection timeout. If not provided, will default to 5s. 0s means no
|
||||
## timeout (not recommended).
|
||||
# timeout = "5s"
|
||||
|
||||
## Optional TLS Config
|
||||
@@ -100,194 +145,239 @@ var sampleConfig = `
|
||||
## Use TLS but skip chain & host verification
|
||||
# insecure_skip_verify = false
|
||||
|
||||
## If true use batch serialization format instead of line based delimiting.
|
||||
## Only applies to data formats which are not line based such as JSON.
|
||||
## Recommended to set to true.
|
||||
# use_batch_format = 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"
|
||||
# data_format = "influx"
|
||||
`
|
||||
|
||||
func (a *AMQP) SetSerializer(serializer serializers.Serializer) {
|
||||
a.serializer = serializer
|
||||
}
|
||||
|
||||
func (q *AMQP) Connect() error {
|
||||
switch q.DeliveryMode {
|
||||
case "transient":
|
||||
q.deliveryMode = amqp.Transient
|
||||
break
|
||||
case "persistent":
|
||||
q.deliveryMode = amqp.Persistent
|
||||
break
|
||||
default:
|
||||
q.deliveryMode = amqp.Transient
|
||||
break
|
||||
}
|
||||
|
||||
headers := amqp.Table{
|
||||
"database": q.Database,
|
||||
"retention_policy": q.RetentionPolicy,
|
||||
}
|
||||
|
||||
var connection *amqp.Connection
|
||||
// make new tls config
|
||||
tls, err := q.ClientConfig.TLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse auth method
|
||||
var sasl []amqp.Authentication // nil by default
|
||||
|
||||
if strings.ToUpper(q.AuthMethod) == "EXTERNAL" {
|
||||
sasl = []amqp.Authentication{&externalAuth{}}
|
||||
}
|
||||
|
||||
amqpConf := amqp.Config{
|
||||
TLSClientConfig: tls,
|
||||
SASL: sasl, // if nil, it will be PLAIN
|
||||
Dial: func(network, addr string) (net.Conn, error) {
|
||||
return net.DialTimeout(network, addr, q.Timeout.Duration)
|
||||
},
|
||||
}
|
||||
|
||||
connection, err = amqp.DialConfig(q.URL, amqpConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channel, err := connection.Channel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to open a channel: %s", err)
|
||||
}
|
||||
|
||||
err = channel.ExchangeDeclare(
|
||||
q.Exchange, // name
|
||||
"topic", // type
|
||||
true, // durable
|
||||
false, // delete when unused
|
||||
false, // internal
|
||||
false, // no-wait
|
||||
nil, // arguments
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to declare an exchange: %s", err)
|
||||
}
|
||||
|
||||
q.setClient(&client{
|
||||
conn: connection,
|
||||
channel: channel,
|
||||
headers: headers,
|
||||
})
|
||||
|
||||
go func() {
|
||||
err := <-connection.NotifyClose(make(chan *amqp.Error))
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
q.setClient(nil)
|
||||
|
||||
log.Printf("I! Closing: %s", err)
|
||||
log.Printf("I! Trying to reconnect")
|
||||
for err := q.Connect(); err != nil; err = q.Connect() {
|
||||
log.Println("E! ", err.Error())
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *AMQP) Close() error {
|
||||
c := q.getClient()
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := c.conn.Close()
|
||||
if err != nil && err != amqp.ErrClosed {
|
||||
log.Printf("E! Error closing AMQP connection: %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *AMQP) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (q *AMQP) Description() string {
|
||||
return "Configuration for the AMQP server to send metrics to"
|
||||
return "Publishes metrics to an AMQP broker"
|
||||
}
|
||||
|
||||
func (q *AMQP) Write(metrics []telegraf.Metric) error {
|
||||
if len(metrics) == 0 {
|
||||
return nil
|
||||
}
|
||||
func (q *AMQP) SetSerializer(serializer serializers.Serializer) {
|
||||
q.serializer = serializer
|
||||
}
|
||||
|
||||
c := q.getClient()
|
||||
if c == nil {
|
||||
return fmt.Errorf("connection is not open")
|
||||
}
|
||||
|
||||
outbuf := make(map[string][]byte)
|
||||
|
||||
for _, metric := range metrics {
|
||||
var key string
|
||||
if q.RoutingTag != "" {
|
||||
if h, ok := metric.Tags()[q.RoutingTag]; ok {
|
||||
key = h
|
||||
}
|
||||
}
|
||||
|
||||
buf, err := q.serializer.Serialize(metric)
|
||||
func (q *AMQP) Connect() error {
|
||||
if q.config == nil {
|
||||
config, err := q.makeClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outbuf[key] = append(outbuf[key], buf...)
|
||||
q.config = config
|
||||
}
|
||||
|
||||
for key, buf := range outbuf {
|
||||
// Note that since the channel is not in confirm mode, the absence of
|
||||
// an error does not indicate successful delivery.
|
||||
err := c.channel.Publish(
|
||||
q.Exchange, // exchange
|
||||
key, // routing key
|
||||
false, // mandatory
|
||||
false, // immediate
|
||||
amqp.Publishing{
|
||||
Headers: c.headers,
|
||||
ContentType: "text/plain",
|
||||
Body: buf,
|
||||
DeliveryMode: q.deliveryMode,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to send AMQP message: %s", err)
|
||||
}
|
||||
client, err := q.connect(q.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q.client = client
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *AMQP) Close() error {
|
||||
if q.client != nil {
|
||||
return q.client.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *AMQP) getClient() *client {
|
||||
q.Lock()
|
||||
defer q.Unlock()
|
||||
return q.c
|
||||
func (q *AMQP) routingKey(metric telegraf.Metric) string {
|
||||
if q.RoutingTag != "" {
|
||||
key, ok := metric.GetTag(q.RoutingTag)
|
||||
if ok {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return q.RoutingKey
|
||||
}
|
||||
|
||||
func (q *AMQP) setClient(c *client) {
|
||||
q.Lock()
|
||||
q.c = c
|
||||
q.Unlock()
|
||||
func (q *AMQP) Write(metrics []telegraf.Metric) error {
|
||||
batches := make(map[string][]telegraf.Metric)
|
||||
if q.ExchangeType == "direct" || q.ExchangeType == "header" {
|
||||
// Since the routing_key is ignored for these exchange types send as a
|
||||
// single batch.
|
||||
batches[""] = metrics
|
||||
} else {
|
||||
for _, metric := range metrics {
|
||||
routingKey := q.routingKey(metric)
|
||||
if _, ok := batches[routingKey]; !ok {
|
||||
batches[routingKey] = make([]telegraf.Metric, 0)
|
||||
}
|
||||
|
||||
batches[routingKey] = append(batches[routingKey], metric)
|
||||
}
|
||||
}
|
||||
|
||||
first := true
|
||||
for key, metrics := range batches {
|
||||
body, err := q.serialize(metrics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = q.publish(key, body)
|
||||
if err != nil {
|
||||
// If this is the first attempt to publish and the connection is
|
||||
// closed, try to reconnect and retry once.
|
||||
if aerr, ok := err.(*amqp.Error); first && ok && aerr == amqp.ErrClosed {
|
||||
first = false
|
||||
q.client = nil
|
||||
err := q.publish(key, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
q.client = nil
|
||||
return err
|
||||
}
|
||||
}
|
||||
first = false
|
||||
}
|
||||
|
||||
if q.sentMessages >= q.MaxMessages && q.MaxMessages > 0 {
|
||||
log.Printf("D! Output [amqp] sent MaxMessages; closing connection")
|
||||
q.client = nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *AMQP) publish(key string, body []byte) error {
|
||||
if q.client == nil {
|
||||
client, err := q.connect(q.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q.sentMessages = 0
|
||||
q.client = client
|
||||
}
|
||||
|
||||
err := q.client.Publish(key, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q.sentMessages++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *AMQP) serialize(metrics []telegraf.Metric) ([]byte, error) {
|
||||
if q.UseBatchFormat {
|
||||
return q.serializer.SerializeBatch(metrics)
|
||||
} else {
|
||||
var buf bytes.Buffer
|
||||
for _, metric := range metrics {
|
||||
octets, err := q.serializer.Serialize(metric)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = buf.Write(octets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
body := buf.Bytes()
|
||||
return body, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (q *AMQP) makeClientConfig() (*ClientConfig, error) {
|
||||
config := &ClientConfig{
|
||||
exchange: q.Exchange,
|
||||
exchangeType: q.ExchangeType,
|
||||
exchangePassive: q.ExchangePassive,
|
||||
timeout: q.Timeout.Duration,
|
||||
}
|
||||
|
||||
switch q.ExchangeDurability {
|
||||
case "transient":
|
||||
config.exchangeDurable = false
|
||||
default:
|
||||
config.exchangeDurable = true
|
||||
}
|
||||
|
||||
config.brokers = q.Brokers
|
||||
if len(config.brokers) == 0 {
|
||||
config.brokers = []string{q.URL}
|
||||
}
|
||||
|
||||
switch q.DeliveryMode {
|
||||
case "transient":
|
||||
config.deliveryMode = amqp.Transient
|
||||
case "persistent":
|
||||
config.deliveryMode = amqp.Persistent
|
||||
default:
|
||||
config.deliveryMode = amqp.Transient
|
||||
}
|
||||
|
||||
if len(q.Headers) > 0 {
|
||||
config.headers = make(amqp.Table, len(q.Headers))
|
||||
for k, v := range q.Headers {
|
||||
config.headers[k] = v
|
||||
}
|
||||
} else {
|
||||
// Copy deprecated fields into message header
|
||||
config.headers = amqp.Table{
|
||||
"database": q.Database,
|
||||
"retention_policy": q.RetentionPolicy,
|
||||
}
|
||||
}
|
||||
|
||||
if len(q.ExchangeArguments) > 0 {
|
||||
config.exchangeArguments = make(amqp.Table, len(q.ExchangeArguments))
|
||||
for k, v := range q.ExchangeArguments {
|
||||
config.exchangeArguments[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
tlsConfig, err := q.ClientConfig.TLSConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.tlsConfig = tlsConfig
|
||||
|
||||
var auth []amqp.Authentication
|
||||
if strings.ToUpper(q.AuthMethod) == "EXTERNAL" {
|
||||
auth = []amqp.Authentication{&externalAuth{}}
|
||||
} else if q.Username != "" || q.Password != "" {
|
||||
auth = []amqp.Authentication{
|
||||
&amqp.PlainAuth{
|
||||
Username: q.Username,
|
||||
Password: q.Password,
|
||||
},
|
||||
}
|
||||
}
|
||||
config.auth = auth
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func connect(config *ClientConfig) (Client, error) {
|
||||
return Connect(config)
|
||||
}
|
||||
|
||||
func init() {
|
||||
outputs.Add("amqp", func() telegraf.Output {
|
||||
return &AMQP{
|
||||
URL: DefaultURL,
|
||||
ExchangeType: DefaultExchangeType,
|
||||
AuthMethod: DefaultAuthMethod,
|
||||
Database: DefaultDatabase,
|
||||
RetentionPolicy: DefaultRetentionPolicy,
|
||||
Timeout: internal.Duration{Duration: time.Second * 5},
|
||||
connect: connect,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,30 +2,161 @@ package amqp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf/plugins/serializers"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/streadway/amqp"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConnectAndWrite(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
type MockClient struct {
|
||||
PublishF func(key string, body []byte) error
|
||||
CloseF func() error
|
||||
|
||||
var url = "amqp://" + testutil.GetLocalHost() + ":5672/"
|
||||
s, _ := serializers.NewInfluxSerializer()
|
||||
q := &AMQP{
|
||||
URL: url,
|
||||
Exchange: "telegraf_test",
|
||||
serializer: s,
|
||||
}
|
||||
PublishCallCount int
|
||||
CloseCallCount int
|
||||
|
||||
// Verify that we can connect to the AMQP broker
|
||||
err := q.Connect()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify that we can successfully write data to the amqp broker
|
||||
err = q.Write(testutil.MockMetrics())
|
||||
require.NoError(t, err)
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (c *MockClient) Publish(key string, body []byte) error {
|
||||
c.PublishCallCount++
|
||||
return c.PublishF(key, body)
|
||||
}
|
||||
|
||||
func (c *MockClient) Close() error {
|
||||
c.CloseCallCount++
|
||||
return c.CloseF()
|
||||
}
|
||||
|
||||
func MockConnect(config *ClientConfig) (Client, error) {
|
||||
return &MockClient{}, nil
|
||||
}
|
||||
|
||||
func NewMockClient() Client {
|
||||
return &MockClient{
|
||||
PublishF: func(key string, body []byte) error {
|
||||
return nil
|
||||
},
|
||||
CloseF: func() error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnect(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
output *AMQP
|
||||
errFunc func(t *testing.T, output *AMQP, err error)
|
||||
}{
|
||||
{
|
||||
name: "defaults",
|
||||
output: &AMQP{
|
||||
Brokers: []string{DefaultURL},
|
||||
ExchangeType: DefaultExchangeType,
|
||||
ExchangeDurability: "durable",
|
||||
AuthMethod: DefaultAuthMethod,
|
||||
Database: DefaultDatabase,
|
||||
RetentionPolicy: DefaultRetentionPolicy,
|
||||
Timeout: internal.Duration{Duration: time.Second * 5},
|
||||
connect: func(config *ClientConfig) (Client, error) {
|
||||
return NewMockClient(), nil
|
||||
},
|
||||
},
|
||||
errFunc: func(t *testing.T, output *AMQP, err error) {
|
||||
config := output.config
|
||||
require.Equal(t, []string{DefaultURL}, config.brokers)
|
||||
require.Equal(t, "", config.exchange)
|
||||
require.Equal(t, "topic", config.exchangeType)
|
||||
require.Equal(t, false, config.exchangePassive)
|
||||
require.Equal(t, true, config.exchangeDurable)
|
||||
require.Equal(t, amqp.Table(nil), config.exchangeArguments)
|
||||
require.Equal(t, amqp.Table{
|
||||
"database": DefaultDatabase,
|
||||
"retention_policy": DefaultRetentionPolicy,
|
||||
}, config.headers)
|
||||
require.Equal(t, amqp.Transient, config.deliveryMode)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "headers overrides deprecated dbrp",
|
||||
output: &AMQP{
|
||||
Headers: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
connect: func(config *ClientConfig) (Client, error) {
|
||||
return NewMockClient(), nil
|
||||
},
|
||||
},
|
||||
errFunc: func(t *testing.T, output *AMQP, err error) {
|
||||
config := output.config
|
||||
require.Equal(t, amqp.Table{
|
||||
"foo": "bar",
|
||||
}, config.headers)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exchange args",
|
||||
output: &AMQP{
|
||||
ExchangeArguments: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
connect: func(config *ClientConfig) (Client, error) {
|
||||
return NewMockClient(), nil
|
||||
},
|
||||
},
|
||||
errFunc: func(t *testing.T, output *AMQP, err error) {
|
||||
config := output.config
|
||||
require.Equal(t, amqp.Table{
|
||||
"foo": "bar",
|
||||
}, config.exchangeArguments)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "username password",
|
||||
output: &AMQP{
|
||||
URL: "amqp://foo:bar@localhost",
|
||||
Username: "telegraf",
|
||||
Password: "pa$$word",
|
||||
connect: func(config *ClientConfig) (Client, error) {
|
||||
return NewMockClient(), nil
|
||||
},
|
||||
},
|
||||
errFunc: func(t *testing.T, output *AMQP, err error) {
|
||||
config := output.config
|
||||
require.Equal(t, []amqp.Authentication{
|
||||
&amqp.PlainAuth{
|
||||
Username: "telegraf",
|
||||
Password: "pa$$word",
|
||||
},
|
||||
}, config.auth)
|
||||
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "url support",
|
||||
output: &AMQP{
|
||||
URL: DefaultURL,
|
||||
connect: func(config *ClientConfig) (Client, error) {
|
||||
return NewMockClient(), nil
|
||||
},
|
||||
},
|
||||
errFunc: func(t *testing.T, output *AMQP, err error) {
|
||||
config := output.config
|
||||
require.Equal(t, []string{DefaultURL}, config.brokers)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.output.Connect()
|
||||
tt.errFunc(t, tt.output, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
134
plugins/outputs/amqp/client.go
Normal file
134
plugins/outputs/amqp/client.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package amqp
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/streadway/amqp"
|
||||
)
|
||||
|
||||
type ClientConfig struct {
|
||||
brokers []string
|
||||
exchange string
|
||||
exchangeType string
|
||||
exchangePassive bool
|
||||
exchangeDurable bool
|
||||
exchangeArguments amqp.Table
|
||||
headers amqp.Table
|
||||
deliveryMode uint8
|
||||
tlsConfig *tls.Config
|
||||
timeout time.Duration
|
||||
auth []amqp.Authentication
|
||||
}
|
||||
|
||||
type client struct {
|
||||
conn *amqp.Connection
|
||||
channel *amqp.Channel
|
||||
config *ClientConfig
|
||||
}
|
||||
|
||||
// Connect opens a connection to one of the brokers at random
|
||||
func Connect(config *ClientConfig) (*client, error) {
|
||||
client := &client{
|
||||
config: config,
|
||||
}
|
||||
|
||||
p := rand.Perm(len(config.brokers))
|
||||
for _, n := range p {
|
||||
broker := config.brokers[n]
|
||||
log.Printf("D! Output [amqp] connecting to %q", broker)
|
||||
conn, err := amqp.DialConfig(
|
||||
broker, amqp.Config{
|
||||
TLSClientConfig: config.tlsConfig,
|
||||
SASL: config.auth, // if nil, it will be PLAIN taken from url
|
||||
Dial: func(network, addr string) (net.Conn, error) {
|
||||
return net.DialTimeout(network, addr, config.timeout)
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
client.conn = conn
|
||||
log.Printf("D! Output [amqp] connected to %q", broker)
|
||||
break
|
||||
}
|
||||
log.Printf("D! Output [amqp] error connecting to %q", broker)
|
||||
}
|
||||
|
||||
if client.conn == nil {
|
||||
return nil, errors.New("could not connect to any broker")
|
||||
}
|
||||
|
||||
channel, err := client.conn.Channel()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error opening channel: %v", err)
|
||||
}
|
||||
client.channel = channel
|
||||
|
||||
err = client.DeclareExchange()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *client) DeclareExchange() error {
|
||||
var err error
|
||||
if c.config.exchangePassive {
|
||||
err = c.channel.ExchangeDeclarePassive(
|
||||
c.config.exchange,
|
||||
c.config.exchangeType,
|
||||
c.config.exchangeDurable,
|
||||
false, // delete when unused
|
||||
false, // internal
|
||||
false, // no-wait
|
||||
c.config.exchangeArguments,
|
||||
)
|
||||
} else {
|
||||
err = c.channel.ExchangeDeclare(
|
||||
c.config.exchange,
|
||||
c.config.exchangeType,
|
||||
c.config.exchangeDurable,
|
||||
false, // delete when unused
|
||||
false, // internal
|
||||
false, // no-wait
|
||||
c.config.exchangeArguments,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error declaring exchange: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) Publish(key string, body []byte) error {
|
||||
// Note that since the channel is not in confirm mode, the absence of
|
||||
// an error does not indicate successful delivery.
|
||||
return c.channel.Publish(
|
||||
c.config.exchange, // exchange
|
||||
key, // routing key
|
||||
false, // mandatory
|
||||
false, // immediate
|
||||
amqp.Publishing{
|
||||
Headers: c.config.headers,
|
||||
ContentType: "text/plain",
|
||||
Body: body,
|
||||
DeliveryMode: c.config.deliveryMode,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *client) Close() error {
|
||||
if c.conn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := c.conn.Close()
|
||||
if err != nil && err != amqp.ErrClosed {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -177,6 +177,8 @@ func BuildMetricDatum(point telegraf.Metric) []*cloudwatch.MetricDatum {
|
||||
value = float64(t)
|
||||
case int64:
|
||||
value = float64(t)
|
||||
case uint64:
|
||||
value = float64(t)
|
||||
case float64:
|
||||
value = t
|
||||
case bool:
|
||||
|
||||
@@ -83,10 +83,6 @@ func (f *File) Description() string {
|
||||
}
|
||||
|
||||
func (f *File) Write(metrics []telegraf.Metric) error {
|
||||
if len(metrics) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var writeErr error = nil
|
||||
for _, metric := range metrics {
|
||||
b, err := f.serializer.Serialize(metric)
|
||||
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
|
||||
func TestFileExistingFile(t *testing.T) {
|
||||
fh := createFile()
|
||||
defer os.Remove(fh.Name())
|
||||
s, _ := serializers.NewInfluxSerializer()
|
||||
f := File{
|
||||
Files: []string{fh.Name()},
|
||||
@@ -43,6 +44,7 @@ func TestFileExistingFile(t *testing.T) {
|
||||
func TestFileNewFile(t *testing.T) {
|
||||
s, _ := serializers.NewInfluxSerializer()
|
||||
fh := tmpFile()
|
||||
defer os.Remove(fh)
|
||||
f := File{
|
||||
Files: []string{fh},
|
||||
serializer: s,
|
||||
@@ -62,8 +64,11 @@ func TestFileNewFile(t *testing.T) {
|
||||
|
||||
func TestFileExistingFiles(t *testing.T) {
|
||||
fh1 := createFile()
|
||||
defer os.Remove(fh1.Name())
|
||||
fh2 := createFile()
|
||||
defer os.Remove(fh2.Name())
|
||||
fh3 := createFile()
|
||||
defer os.Remove(fh3.Name())
|
||||
|
||||
s, _ := serializers.NewInfluxSerializer()
|
||||
f := File{
|
||||
@@ -88,8 +93,11 @@ func TestFileExistingFiles(t *testing.T) {
|
||||
func TestFileNewFiles(t *testing.T) {
|
||||
s, _ := serializers.NewInfluxSerializer()
|
||||
fh1 := tmpFile()
|
||||
defer os.Remove(fh1)
|
||||
fh2 := tmpFile()
|
||||
defer os.Remove(fh2)
|
||||
fh3 := tmpFile()
|
||||
defer os.Remove(fh3)
|
||||
f := File{
|
||||
Files: []string{fh1, fh2, fh3},
|
||||
serializer: s,
|
||||
@@ -111,7 +119,9 @@ func TestFileNewFiles(t *testing.T) {
|
||||
|
||||
func TestFileBoth(t *testing.T) {
|
||||
fh1 := createFile()
|
||||
defer os.Remove(fh1.Name())
|
||||
fh2 := tmpFile()
|
||||
defer os.Remove(fh2)
|
||||
|
||||
s, _ := serializers.NewInfluxSerializer()
|
||||
f := File{
|
||||
|
||||
@@ -21,11 +21,6 @@ data formats. For data_formats that support batching, metrics are sent in batch
|
||||
# username = "username"
|
||||
# password = "pa$$word"
|
||||
|
||||
## Additional HTTP headers
|
||||
# [outputs.http.headers]
|
||||
# # Should be set manually to "application/json" for json data_format
|
||||
# Content-Type = "text/plain; charset=utf-8"
|
||||
|
||||
## Optional TLS Config
|
||||
# tls_ca = "/etc/telegraf/ca.pem"
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
@@ -38,4 +33,9 @@ data formats. For data_formats that support batching, metrics are sent in batch
|
||||
## more about them here:
|
||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||
# data_format = "influx"
|
||||
|
||||
## Additional HTTP headers
|
||||
# [outputs.http.headers]
|
||||
# # Should be set manually to "application/json" for json data_format
|
||||
# Content-Type = "text/plain; charset=utf-8"
|
||||
```
|
||||
|
||||
@@ -29,11 +29,6 @@ var sampleConfig = `
|
||||
# username = "username"
|
||||
# password = "pa$$word"
|
||||
|
||||
## Additional HTTP headers
|
||||
# [outputs.http.headers]
|
||||
# # Should be set to "application/json" for json data_format
|
||||
# Content-Type = "text/plain; charset=utf-8"
|
||||
|
||||
## Optional TLS Config
|
||||
# tls_ca = "/etc/telegraf/ca.pem"
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
@@ -46,6 +41,11 @@ var sampleConfig = `
|
||||
## more about them here:
|
||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||
# data_format = "influx"
|
||||
|
||||
## Additional HTTP headers
|
||||
# [outputs.http.headers]
|
||||
# # Should be set manually to "application/json" for json data_format
|
||||
# Content-Type = "text/plain; charset=utf-8"
|
||||
`
|
||||
|
||||
const (
|
||||
|
||||
@@ -137,6 +137,7 @@ func (sw *SocketWriter) Write(metrics []telegraf.Metric) error {
|
||||
// permanent error. close the connection
|
||||
sw.Close()
|
||||
sw.Conn = nil
|
||||
return fmt.Errorf("closing connection: %v", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package socket_writer
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
@@ -44,13 +46,16 @@ func TestSocketWriter_udp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSocketWriter_unix(t *testing.T) {
|
||||
os.Remove("/tmp/telegraf_test.sock")
|
||||
defer os.Remove("/tmp/telegraf_test.sock")
|
||||
listener, err := net.Listen("unix", "/tmp/telegraf_test.sock")
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "socket_writer.TestSocketWriter_unix.sock")
|
||||
|
||||
listener, err := net.Listen("unix", sock)
|
||||
require.NoError(t, err)
|
||||
|
||||
sw := newSocketWriter()
|
||||
sw.Address = "unix:///tmp/telegraf_test.sock"
|
||||
sw.Address = "unix://" + sock
|
||||
|
||||
err = sw.Connect()
|
||||
require.NoError(t, err)
|
||||
@@ -62,13 +67,16 @@ func TestSocketWriter_unix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSocketWriter_unixgram(t *testing.T) {
|
||||
os.Remove("/tmp/telegraf_test.sock")
|
||||
defer os.Remove("/tmp/telegraf_test.sock")
|
||||
listener, err := net.ListenPacket("unixgram", "/tmp/telegraf_test.sock")
|
||||
tmpdir, err := ioutil.TempDir("", "telegraf")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
sock := filepath.Join(tmpdir, "socket_writer.TestSocketWriter_unixgram.sock")
|
||||
|
||||
listener, err := net.ListenPacket("unixgram", sock)
|
||||
require.NoError(t, err)
|
||||
|
||||
sw := newSocketWriter()
|
||||
sw.Address = "unixgram:///tmp/telegraf_test.sock"
|
||||
sw.Address = "unixgram://" + sock
|
||||
|
||||
err = sw.Connect()
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user