diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index a589050279b..4cbb83a60fc 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -834,6 +834,7 @@ The issue will be fixed in the 6.4.1 release. - Release prometheus collector metricset as GA. {pull}7660[7660] - Add Elasticsearch `cluster_stats` metricset. {pull}7638[7638] - Added `basepath` setting for HTTP-based metricsets {pull}7700[7700] +- Add couchdb module. {pull}9406[9406] *Packetbeat* diff --git a/metricbeat/docker-compose.yml b/metricbeat/docker-compose.yml index dae60f2597e..36a1c938eef 100644 --- a/metricbeat/docker-compose.yml +++ b/metricbeat/docker-compose.yml @@ -15,6 +15,7 @@ services: - ./module/apache/_meta/env - ./module/ceph/_meta/env - ./module/couchbase/_meta/env + - ./module/couchdb/_meta/env - ./module/dropwizard/_meta/env - ./module/elasticsearch/_meta/env - ./module/envoyproxy/_meta/env @@ -59,6 +60,9 @@ services: couchbase: build: ./module/couchbase/_meta + couchdb: + build: ./module/couchdb/_meta + dropwizard: build: ./module/dropwizard/_meta diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 3bc23e8be09..25136833985 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -20,6 +20,7 @@ grouped in the following categories: * <> * <> * <> +* <> * <> * <> * <> @@ -2549,6 +2550,364 @@ type: long Number of items/documents that are replicas. +-- + +[[exported-fields-couchdb]] +== couchdb fields + +couchdb module + + + +[float] +== couchdb fields + + + + +[float] +== server fields + +Contains CouchDB server stats + + + +[float] +== httpd fields + +HTTP statistics + + + +*`couchdb.server.httpd.view_reads`*:: ++ +-- +type: long + +Number of view reads + + +-- + +*`couchdb.server.httpd.bulk_requests`*:: ++ +-- +type: long + +Number of bulk requests + + +-- + +*`couchdb.server.httpd.clients_requesting_changes`*:: ++ +-- +type: long + +Number of clients for continuous _changes + + +-- + +*`couchdb.server.httpd.temporary_view_reads`*:: ++ +-- +type: long + +Number of temporary view reads + + +-- + +*`couchdb.server.httpd.requests`*:: ++ +-- +type: long + +Number of HTTP requests + + +-- + +[float] +== httpd_request_methods fields + +HTTP request methods + + + +*`couchdb.server.httpd_request_methods.COPY`*:: ++ +-- +type: long + +Number of HTTP COPY requests + + +-- + +*`couchdb.server.httpd_request_methods.HEAD`*:: ++ +-- +type: long + +Number of HTTP HEAD requests + + +-- + +*`couchdb.server.httpd_request_methods.POST`*:: ++ +-- +type: long + +Number of HTTP POST requests + + +-- + +*`couchdb.server.httpd_request_methods.DELETE`*:: ++ +-- +type: long + +Number of HTTP DELETE requests + + +-- + +*`couchdb.server.httpd_request_methods.GET`*:: ++ +-- +type: long + +Number of HTTP GET requests + + +-- + +*`couchdb.server.httpd_request_methods.PUT`*:: ++ +-- +type: long + +Number of HTTP PUT requests + + +-- + +[float] +== httpd_status_codes fields + +HTTP status codes statistics + + + +*`couchdb.server.httpd_status_codes.200`*:: ++ +-- +type: long + +Number of HTTP 200 OK responses + + +-- + +*`couchdb.server.httpd_status_codes.201`*:: ++ +-- +type: long + +Number of HTTP 201 Created responses + + +-- + +*`couchdb.server.httpd_status_codes.202`*:: ++ +-- +type: long + +Number of HTTP 202 Accepted responses + + +-- + +*`couchdb.server.httpd_status_codes.301`*:: ++ +-- +type: long + +Number of HTTP 301 Moved Permanently responses + + +-- + +*`couchdb.server.httpd_status_codes.304`*:: ++ +-- +type: long + +Number of HTTP 304 Not Modified responses + + +-- + +*`couchdb.server.httpd_status_codes.400`*:: ++ +-- +type: long + +Number of HTTP 400 Bad Request responses + + +-- + +*`couchdb.server.httpd_status_codes.401`*:: ++ +-- +type: long + +Number of HTTP 401 Unauthorized responses + + +-- + +*`couchdb.server.httpd_status_codes.403`*:: ++ +-- +type: long + +Number of HTTP 403 Forbidden responses + + +-- + +*`couchdb.server.httpd_status_codes.404`*:: ++ +-- +type: long + +Number of HTTP 404 Not Found responses + + +-- + +*`couchdb.server.httpd_status_codes.405`*:: ++ +-- +type: long + +Number of HTTP 405 Method Not Allowed responses + + +-- + +*`couchdb.server.httpd_status_codes.409`*:: ++ +-- +type: long + +Number of HTTP 409 Conflict responses + + +-- + +*`couchdb.server.httpd_status_codes.412`*:: ++ +-- +type: long + +Number of HTTP 412 Precondition Failed responses + + +-- + +*`couchdb.server.httpd_status_codes.500`*:: ++ +-- +type: long + +Number of HTTP 500 Internal Server Error responses + + +-- + +[float] +== couchdb fields + +couchdb statistics + + + +*`couchdb.server.couchdb.database_writes`*:: ++ +-- +type: long + +Number of times a database was changed + + +-- + +*`couchdb.server.couchdb.open_databases`*:: ++ +-- +type: long + +Number of open databases + + +-- + +*`couchdb.server.couchdb.auth_cache_misses`*:: ++ +-- +type: long + +Number of authentication cache misses + + +-- + +*`couchdb.server.couchdb.request_time`*:: ++ +-- +type: long + +Length of a request inside CouchDB without MochiWeb + + +-- + +*`couchdb.server.couchdb.database_reads`*:: ++ +-- +type: long + +Number of times a document was read from a database + + +-- + +*`couchdb.server.couchdb.auth_cache_hits`*:: ++ +-- +type: long + +Number of authentication cache hits + + +-- + +*`couchdb.server.couchdb.open_os_files`*:: ++ +-- +type: long + +Number of file descriptors CouchDB has open + + -- [[exported-fields-docker-processor]] diff --git a/metricbeat/docs/modules/couchdb.asciidoc b/metricbeat/docs/modules/couchdb.asciidoc new file mode 100644 index 00000000000..37fc5c0a1a0 --- /dev/null +++ b/metricbeat/docs/modules/couchdb.asciidoc @@ -0,0 +1,43 @@ +//// +This file is generated! See scripts/docs_collector.py +//// + +[[metricbeat-module-couchdb]] +== couchdb module + +beta[] + +This is the couchdb module. + +The default metricset is `server`. + +[float] +=== Compatibility + +The Couchdb module is tested with Couchdb 1.7. + + +[float] +=== Example configuration + +The couchdb module supports the standard configuration options that are described +in <>. Here is an example configuration: + +[source,yaml] +---- +metricbeat.modules: +- module: couchdb + metricsets: ["server"] + period: 10s + hosts: ["localhost:5984"] +---- + +[float] +=== Metricsets + +The following metricsets are available: + +* <> + +include::couchdb/server.asciidoc[] + diff --git a/metricbeat/docs/modules/couchdb/server.asciidoc b/metricbeat/docs/modules/couchdb/server.asciidoc new file mode 100644 index 00000000000..f215fd95b2d --- /dev/null +++ b/metricbeat/docs/modules/couchdb/server.asciidoc @@ -0,0 +1,23 @@ +//// +This file is generated! See scripts/docs_collector.py +//// + +[[metricbeat-metricset-couchdb-server]] +=== couchdb server metricset + +beta[] + +include::../../../module/couchdb/server/_meta/docs.asciidoc[] + + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../module/couchdb/server/_meta/data.json[] +---- diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index 3fcca72584c..a3930db8c85 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -23,6 +23,8 @@ This file is generated! See scripts/docs_collector.py .3+| .3+| |<> |<> |<> +|<> beta[] |image:./images/icon-no.png[No prebuilt dashboards] | +.1+| .1+| |<> beta[] |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | .9+| .9+| |<> |<> @@ -169,6 +171,7 @@ include::modules/apache.asciidoc[] include::modules/aws.asciidoc[] include::modules/ceph.asciidoc[] include::modules/couchbase.asciidoc[] +include::modules/couchdb.asciidoc[] include::modules/docker.asciidoc[] include::modules/dropwizard.asciidoc[] include::modules/elasticsearch.asciidoc[] diff --git a/metricbeat/include/list.go b/metricbeat/include/list.go index 0a873f9080f..20b43a8fb59 100644 --- a/metricbeat/include/list.go +++ b/metricbeat/include/list.go @@ -42,6 +42,8 @@ import ( _ "github.com/elastic/beats/metricbeat/module/couchbase/bucket" _ "github.com/elastic/beats/metricbeat/module/couchbase/cluster" _ "github.com/elastic/beats/metricbeat/module/couchbase/node" + _ "github.com/elastic/beats/metricbeat/module/couchdb" + _ "github.com/elastic/beats/metricbeat/module/couchdb/server" _ "github.com/elastic/beats/metricbeat/module/docker" _ "github.com/elastic/beats/metricbeat/module/docker/container" _ "github.com/elastic/beats/metricbeat/module/docker/cpu" diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index 6c89c863f81..b78ba735bee 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -161,6 +161,12 @@ metricbeat.modules: hosts: ["localhost:8091"] enabled: true +#------------------------------- couchdb Module ------------------------------ +- module: couchdb + metricsets: ["server"] + period: 10s + hosts: ["localhost:5984"] + #------------------------------- Docker Module ------------------------------- - module: docker metricsets: diff --git a/metricbeat/module/couchdb/_meta/Dockerfile b/metricbeat/module/couchdb/_meta/Dockerfile new file mode 100644 index 00000000000..dc4561c62e1 --- /dev/null +++ b/metricbeat/module/couchdb/_meta/Dockerfile @@ -0,0 +1,4 @@ +FROM apache/couchdb:1.7 +COPY ./local.ini /etc/couchdb/local.ini +EXPOSE 5984 +HEALTHCHECK --interval=1s --retries=90 CMD curl -f http://localhost:5984/ | grep Welcome diff --git a/metricbeat/module/couchdb/_meta/config.yml b/metricbeat/module/couchdb/_meta/config.yml new file mode 100644 index 00000000000..353e9513159 --- /dev/null +++ b/metricbeat/module/couchdb/_meta/config.yml @@ -0,0 +1,4 @@ +- module: couchdb + metricsets: ["server"] + period: 10s + hosts: ["localhost:5984"] diff --git a/metricbeat/module/couchdb/_meta/docs.asciidoc b/metricbeat/module/couchdb/_meta/docs.asciidoc new file mode 100644 index 00000000000..76ff4b18062 --- /dev/null +++ b/metricbeat/module/couchdb/_meta/docs.asciidoc @@ -0,0 +1,8 @@ +This is the couchdb module. + +The default metricset is `server`. + +[float] +=== Compatibility + +The Couchdb module is tested with Couchdb 1.7. diff --git a/metricbeat/module/couchdb/_meta/env b/metricbeat/module/couchdb/_meta/env new file mode 100644 index 00000000000..d790ad1f99b --- /dev/null +++ b/metricbeat/module/couchdb/_meta/env @@ -0,0 +1,2 @@ +COUCHDB_HOST=couchdb +COUCHDB_PORT=5984 diff --git a/metricbeat/module/couchdb/_meta/fields.yml b/metricbeat/module/couchdb/_meta/fields.yml new file mode 100644 index 00000000000..2e1f6d44a75 --- /dev/null +++ b/metricbeat/module/couchdb/_meta/fields.yml @@ -0,0 +1,10 @@ +- key: couchdb + title: "couchdb" + description: > + couchdb module + release: beta + fields: + - name: couchdb + type: group + description: > + fields: diff --git a/metricbeat/module/couchdb/_meta/local.ini b/metricbeat/module/couchdb/_meta/local.ini new file mode 100644 index 00000000000..cfbbcd4a59a --- /dev/null +++ b/metricbeat/module/couchdb/_meta/local.ini @@ -0,0 +1,5 @@ +[chttpd] +bind_address = any + +[httpd] +bind_address = any diff --git a/metricbeat/module/couchdb/_meta/test/serverstats.json b/metricbeat/module/couchdb/_meta/test/serverstats.json new file mode 100644 index 00000000000..2b4bd27d25b --- /dev/null +++ b/metricbeat/module/couchdb/_meta/test/serverstats.json @@ -0,0 +1,45 @@ +{ + "couchdb": { + "server": { + "couchdb": { + "open_os_files": 0, + "database_writes": 0, + "open_databases": 0, + "auth_cache_misses": 0, + "request_time": 142, + "database_reads": 0, + "auth_cache_hits": 0 + }, + "httpd": { + "requests": 3, + "viewReads": 0, + "bulk_requests": 0, + "clients_requesting_changes": 0, + "temporary_view_reads": 0 + }, + "httpd_request_methods": { + "GET": 3, + "PUT": 0, + "COPY": 0, + "HEAD": 0, + "POST": 0, + "DELETE": 0 + }, + "httpd_status_codes": { + "401": 0, + "405": 0, + "304": 0, + "400": 0, + "404": 0, + "409": 0, + "201": 0, + "202": 0, + "403": 0, + "500": 0, + "200": 3, + "301": 0, + "412": 0 + } + } + } +} diff --git a/metricbeat/module/couchdb/doc.go b/metricbeat/module/couchdb/doc.go new file mode 100644 index 00000000000..abb206ef2c5 --- /dev/null +++ b/metricbeat/module/couchdb/doc.go @@ -0,0 +1,19 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package couchdb is a Metricbeat module that contains MetricSets. +package couchdb diff --git a/metricbeat/module/couchdb/fields.go b/metricbeat/module/couchdb/fields.go new file mode 100644 index 00000000000..8def4f75f5e --- /dev/null +++ b/metricbeat/module/couchdb/fields.go @@ -0,0 +1,36 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Code generated by beats/dev-tools/cmd/asset/asset.go - DO NOT EDIT. + +package couchdb + +import ( + "github.com/elastic/beats/libbeat/asset" +) + +func init() { + if err := asset.SetFields("metricbeat", "couchdb", asset.ModuleFieldsPri, AssetCouchdb); err != nil { + panic(err) + } +} + +// AssetCouchdb returns asset data. +// This is the base64 encoded gzipped contents of ../metricbeat/module/couchdb. +func AssetCouchdb() string { + return "eJy8mMlu2zwQx+9+ioHvAeTtEB8+IHGc5EOzGI2DoieBJscWEYmjklQM9+kLyvsWhQlEHUVx/j/NwmUu4A0XfeBU8ERMGgBW2hT70Fy9aTYABBquZW4lqT781wCA9feQkShSbABoTJEZ7MMELWsATCWmwvTLjy9AsQx3RdxjFzn2YaapyFdvTujsG9o1ZlC/o968PmXvrM3lMyBlmVQGBo7r5nplEoxl1ux8ufdrADsjh2y7fIm1udgbOYdYgeme+/F4VHJJYyU3B+OnMHZR3iXOY41MHE7cMqWkZicGK7DK56nIJqiBpqUO7OucZZoU6Vus8U+BxtaBtaVySnCkdBaMpxKVNWs2qWYxT5iaYb2UK1mYkgZOykpVUGHgSPsst8UsJ830Iq413FvijaBX3D8V8uPpnmxlvXwc8706XUc7ztAmdOS479Xtyjactl1VvIPn0e9a41gyOhWPIrkfXt3UD+VUPKBGzy/j+qGcigfUzfBhOB7Wj7XU8QC7GwZw1t3Qx1ej1xDxe61C2l8Z3K5bmJiTONoBvr+dFwZKw1/f25vtKGrW77V2FMHzD9BoclLmM/tRsx21goC1YKCRWRSedO0gdG244hxzX7xOEOd1ohY80jsKGKHOmEJl04UnZzcIZxeeyMIjCTmVnq7sBimQbhTBNRPwc7XZ+xGGCHY3asGrYoVNSMu/3k7sBEHswC3piRQClSdfLXl4DLhMxFsqlK8De0Ec2IPH8phZYl6lKc29I30ZBPTS3b+nqeSetdIKsW53W20YaeSkhHTT4JbJ1NORvSDrTi+K4H9lUSuWwsuygTHUmnQV60FT5vBHvny2Wdv78olGMMsmzGA819LWfOu2MkMDbKMJc2ZgeeUW1SGmHFW8nlovqJOCY6mzaG6ZjznjCcaZNHXTOTVUVnJWFkupCwe6Z1HXV28XixooH1DNbFJSbm7iUhkpcNP3m0ubUOGOFzyRv3BSzbzJ0QBtlnWKEi8yVLZMUScLU03ZTu56JUUia+66nUyJPdWPy4pMPJVpRd5+u0/kJDYzSG9bwQkzJUfjXwAAAP//5udvpQ==" +} diff --git a/metricbeat/module/couchdb/server/_meta/data.json b/metricbeat/module/couchdb/server/_meta/data.json new file mode 100644 index 00000000000..db74c9772a3 --- /dev/null +++ b/metricbeat/module/couchdb/server/_meta/data.json @@ -0,0 +1,57 @@ +{ + "@timestamp":"2016-05-23T08:05:34.853Z", + "beat":{ + "hostname":"beathost", + "name":"beathost" + }, + "metricset":{ + "host":"localhost", + "module":"couchdb", + "name":"server", + "rtt":44269 + }, + "couchdb": { + "server": { + "couchdb": { + "open_os_files": 0, + "database_writes": 0, + "open_databases": 0, + "auth_cache_misses": 0, + "request_time": 142, + "database_reads": 0, + "auth_cache_hits": 0 + }, + "httpd": { + "requests": 3, + "viewReads": 0, + "bulk_requests": 0, + "clients_requesting_changes": 0, + "temporary_view_reads": 0 + }, + "httpd_request_methods": { + "GET": 3, + "PUT": 0, + "COPY": 0, + "HEAD": 0, + "POST": 0, + "DELETE": 0 + }, + "httpd_status_codes": { + "401": 0, + "405": 0, + "304": 0, + "400": 0, + "404": 0, + "409": 0, + "201": 0, + "202": 0, + "403": 0, + "500": 0, + "200": 3, + "301": 0, + "412": 0 + } + } + }, + "type":"metricsets" +} diff --git a/metricbeat/module/couchdb/server/_meta/docs.asciidoc b/metricbeat/module/couchdb/server/_meta/docs.asciidoc new file mode 100644 index 00000000000..7b528063dd8 --- /dev/null +++ b/metricbeat/module/couchdb/server/_meta/docs.asciidoc @@ -0,0 +1 @@ +This is the server metricset of the module couchdb. diff --git a/metricbeat/module/couchdb/server/_meta/fields.yml b/metricbeat/module/couchdb/server/_meta/fields.yml new file mode 100644 index 00000000000..24685c44d29 --- /dev/null +++ b/metricbeat/module/couchdb/server/_meta/fields.yml @@ -0,0 +1,180 @@ +- name: server + type: group + description: > + Contains CouchDB server stats + release: beta + fields: + - name: httpd + type: group + description: > + HTTP statistics + fields: + - name: view_reads + type: long + description: > + Number of view reads + + - name: bulk_requests + type: long + description: > + Number of bulk requests + + - name: clients_requesting_changes + type: long + description: > + Number of clients for continuous _changes + + - name: temporary_view_reads + type: long + description: > + Number of temporary view reads + + - name: requests + type: long + description: > + Number of HTTP requests + + - name: httpd_request_methods + type: group + description: > + HTTP request methods + fields: + - name: COPY + type: long + description: > + Number of HTTP COPY requests + + - name: HEAD + type: long + description: > + Number of HTTP HEAD requests + + - name: POST + type: long + description: > + Number of HTTP POST requests + + - name: DELETE + type: long + description: > + Number of HTTP DELETE requests + + - name: GET + type: long + description: > + Number of HTTP GET requests + + - name: PUT + type: long + description: > + Number of HTTP PUT requests + + - name: httpd_status_codes + type: group + description: > + HTTP status codes statistics + fields: + - name: "200" + type: long + description: > + Number of HTTP 200 OK responses + + - name: "201" + type: long + description: > + Number of HTTP 201 Created responses + + - name: "202" + type: long + description: > + Number of HTTP 202 Accepted responses + + - name: "301" + type: long + description: > + Number of HTTP 301 Moved Permanently responses + + - name: "304" + type: long + description: > + Number of HTTP 304 Not Modified responses + + - name: "400" + type: long + description: > + Number of HTTP 400 Bad Request responses + + - name: "401" + type: long + description: > + Number of HTTP 401 Unauthorized responses + + - name: "403" + type: long + description: > + Number of HTTP 403 Forbidden responses + + - name: "404" + type: long + description: > + Number of HTTP 404 Not Found responses + + - name: "405" + type: long + description: > + Number of HTTP 405 Method Not Allowed responses + + - name: "409" + type: long + description: > + Number of HTTP 409 Conflict responses + + - name: "412" + type: long + description: > + Number of HTTP 412 Precondition Failed responses + + - name: "500" + type: long + description: > + Number of HTTP 500 Internal Server Error responses + + - name: couchdb + type: group + description: > + couchdb statistics + fields: + - name: database_writes + type: long + description: > + Number of times a database was changed + + - name: open_databases + type: long + description: > + Number of open databases + + - name: auth_cache_misses + type: long + description: > + Number of authentication cache misses + + - name: request_time + type: long + description: > + Length of a request inside CouchDB without MochiWeb + + - name: database_reads + type: long + description: > + Number of times a document was read from a database + + - name: auth_cache_hits + type: long + description: > + Number of authentication cache hits + + - name: open_os_files + type: long + description: > + Number of file descriptors CouchDB has open diff --git a/metricbeat/module/couchdb/server/data.go b/metricbeat/module/couchdb/server/data.go new file mode 100644 index 00000000000..0d971f40a1d --- /dev/null +++ b/metricbeat/module/couchdb/server/data.go @@ -0,0 +1,137 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package server + +import ( + "encoding/json" + + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/logp" +) + +// Server type defines all fields of the Server Metricset +type Server struct { + Httpd Httpd `json:"httpd"` + HttpdRequestMethods HttpdRequestMethods `json:"httpd_request_methods"` + HttpdStatusCodes HttpdStatusCodes `json:"httpd_status_codes"` + Couchdb Couchdb `json:"couchdb"` +} + +// Httpd type defines httpd fields of the Server Metricset +type Httpd struct { + ViewReads General `json:"view_reads"` + BulkRequests General `json:"bulk_requests"` + ClientsRequestingChanges General `json:"clients_requesting_changes"` + TemporaryViewReads General `json:"temporary_view_reads"` + Requests General `json:"requests"` +} + +// HttpdRequestMethods type defines httpd requests methods fields of the Server Metricset +type HttpdRequestMethods struct { + Copy General `json:"COPY"` + Head General `json:"HEAD"` + Post General `json:"POST"` + Delete General `json:"DELETE"` + Get General `json:"GET"` + Put General `json:"PUT"` +} + +// HttpdStatusCodes type defines httpd status codes fields of the Server Metricset +type HttpdStatusCodes struct { + Num200 General `json:"200"` + Num201 General `json:"201"` + Num202 General `json:"202"` + Num301 General `json:"301"` + Num304 General `json:"304"` + Num400 General `json:"400"` + Num401 General `json:"401"` + Num403 General `json:"403"` + Num404 General `json:"404"` + Num405 General `json:"405"` + Num409 General `json:"409"` + Num412 General `json:"412"` + Num500 General `json:"500"` +} + +// Couchdb type defines couchdb fields of the Server Metricset +type Couchdb struct { + OpenOsFiles General `json:"open_os_files"` + OpenDatabases General `json:"open_databases"` + AuthCacheHits General `json:"auth_cache_hits"` + RequestTime General `json:"request_time"` + DatabaseReads General `json:"database_reads"` + DatabaseWrites General `json:"database_writes"` + AuthCacheMisses General `json:"auth_cache_misses"` +} + +// General type defines common fields of the Server Metricset +type General struct { + Current float64 `json:"current"` +} + +func eventMapping(content []byte) (common.MapStr, error) { + var data Server + err := json.Unmarshal(content, &data) + if err != nil { + logp.Err("Error: %+v", err) + return nil, err + } + + event := common.MapStr{ + "httpd": common.MapStr{ + "view_reads": data.Httpd.ViewReads.Current, + "bulk_requests": data.Httpd.BulkRequests.Current, + "clients_requesting_changes": data.Httpd.ClientsRequestingChanges.Current, + "temporary_view_reads": data.Httpd.TemporaryViewReads.Current, + "requests": data.Httpd.Requests.Current, + }, + "httpd_request_methods": common.MapStr{ + "COPY": data.HttpdRequestMethods.Copy.Current, + "HEAD": data.HttpdRequestMethods.Head.Current, + "POST": data.HttpdRequestMethods.Post.Current, + "DELETE": data.HttpdRequestMethods.Delete.Current, + "GET": data.HttpdRequestMethods.Get.Current, + "PUT": data.HttpdRequestMethods.Put.Current, + }, + "httpd_status_codes": common.MapStr{ + "200": data.HttpdStatusCodes.Num200.Current, + "201": data.HttpdStatusCodes.Num201.Current, + "202": data.HttpdStatusCodes.Num202.Current, + "301": data.HttpdStatusCodes.Num301.Current, + "304": data.HttpdStatusCodes.Num304.Current, + "400": data.HttpdStatusCodes.Num400.Current, + "401": data.HttpdStatusCodes.Num401.Current, + "403": data.HttpdStatusCodes.Num403.Current, + "404": data.HttpdStatusCodes.Num404.Current, + "405": data.HttpdStatusCodes.Num405.Current, + "409": data.HttpdStatusCodes.Num409.Current, + "412": data.HttpdStatusCodes.Num412.Current, + "500": data.HttpdStatusCodes.Num500.Current, + }, + "couchdb": common.MapStr{ + "database_writes": data.Couchdb.DatabaseWrites.Current, + "open_databases": data.Couchdb.OpenDatabases.Current, + "auth_cache_misses": data.Couchdb.AuthCacheMisses.Current, + "request_time": data.Couchdb.RequestTime.Current, + "database_reads": data.Couchdb.DatabaseReads.Current, + "auth_cache_hits": data.Couchdb.AuthCacheMisses.Current, + "open_os_files": data.Couchdb.OpenOsFiles.Current, + }, + } + return event, nil +} diff --git a/metricbeat/module/couchdb/server/server.go b/metricbeat/module/couchdb/server/server.go new file mode 100644 index 00000000000..2032fe9025f --- /dev/null +++ b/metricbeat/module/couchdb/server/server.go @@ -0,0 +1,83 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package server + +import ( + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/common/cfgwarn" + "github.com/elastic/beats/metricbeat/helper" + "github.com/elastic/beats/metricbeat/mb" + "github.com/elastic/beats/metricbeat/mb/parse" +) + +const ( + defaultScheme = "http" + defaultPath = "/_stats" +) + +var ( + hostParser = parse.URLHostParserBuilder{ + DefaultScheme: defaultScheme, + DefaultPath: defaultPath, + }.Build() +) + +func init() { + mb.Registry.MustAddMetricSet("couchdb", "server", New, + mb.WithHostParser(hostParser), + mb.DefaultMetricSet(), + ) +} + +// MetricSet type defines all fields of the MetricSet +type MetricSet struct { + mb.BaseMetricSet + http *helper.HTTP +} + +// New creates a new instance of the MetricSet. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + cfgwarn.Beta("The couchdb server metricset is beta.") + + config := struct{}{} + if err := base.Module().UnpackConfig(&config); err != nil { + return nil, err + } + + http, err := helper.NewHTTP(base) + if err != nil { + return nil, err + } + return &MetricSet{ + base, + http, + }, nil +} + +// Fetch methods implements the data gathering and data conversion to the right format. +func (m *MetricSet) Fetch() (common.MapStr, error) { + content, err := m.http.FetchContent() + if err != nil { + return nil, err + } + event, err := eventMapping(content) + if err != nil { + return nil, err + } + return event, nil +} diff --git a/metricbeat/module/couchdb/server/server_integration_test.go b/metricbeat/module/couchdb/server/server_integration_test.go new file mode 100644 index 00000000000..7e9162edcdc --- /dev/null +++ b/metricbeat/module/couchdb/server/server_integration_test.go @@ -0,0 +1,79 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration + +package server + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/libbeat/tests/compose" + mbtest "github.com/elastic/beats/metricbeat/mb/testing" +) + +func TestData(t *testing.T) { + compose.EnsureUp(t, "couchdb") + + f := mbtest.NewEventFetcher(t, getConfig()) + err := mbtest.WriteEvent(f, t) + if err != nil { + t.Fatal("write", err) + } +} + +func TestFetch(t *testing.T) { + compose.EnsureUp(t, "couchdb") + + f := mbtest.NewEventFetcher(t, getConfig()) + event, err := f.Fetch() + if !assert.NoError(t, err) { + t.FailNow() + } + + assert.NotNil(t, event) + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), event) +} + +func getConfig() map[string]interface{} { + return map[string]interface{}{ + "module": "couchdb", + "metricsets": []string{"server"}, + "hosts": []string{GetEnvHost() + ":" + GetEnvPort()}, + } +} + +func GetEnvHost() string { + host := os.Getenv("COUCHDB_HOST") + + if len(host) == 0 { + host = "127.0.0.1" + } + return host +} + +func GetEnvPort() string { + port := os.Getenv("COUCHDB_PORT") + + if len(port) == 0 { + port = "5984" + } + return port +} diff --git a/metricbeat/module/couchdb/server/server_test.go b/metricbeat/module/couchdb/server/server_test.go new file mode 100644 index 00000000000..a0ef3a19975 --- /dev/null +++ b/metricbeat/module/couchdb/server/server_test.go @@ -0,0 +1,92 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package server + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "path/filepath" + "testing" + "time" + + mbtest "github.com/elastic/beats/metricbeat/mb/testing" + + "github.com/stretchr/testify/assert" +) + +func TestFetchEventContent(t *testing.T) { + absPath, err := filepath.Abs("../_meta/test/") + assert.NoError(t, err) + + response, err := ioutil.ReadFile(absPath + "/serverstats.json") + assert.NoError(t, err) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + w.Header().Set("Content-Type", "application/json;") + w.Write([]byte(response)) + })) + defer server.Close() + + config := map[string]interface{}{ + "module": "couchdb", + "metricsets": []string{"server"}, + "hosts": []string{server.URL}, + } + f := mbtest.NewEventFetcher(t, config) + event, err := f.Fetch() + if err != nil { + t.Fatal(err) + } + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), event) +} + +func TestFetchTimeout(t *testing.T) { + absPath, err := filepath.Abs("../_meta/test/") + assert.NoError(t, err) + + response, err := ioutil.ReadFile(absPath + "/serverstats.json") + assert.NoError(t, err) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + w.Header().Set("Content-Type", "application/json;") + w.Write([]byte(response)) + <-r.Context().Done() + })) + defer server.Close() + + config := map[string]interface{}{ + "module": "couchdb", + "metricsets": []string{"server"}, + "hosts": []string{server.URL}, + "timeout": "50ms", + } + + f := mbtest.NewEventFetcher(t, config) + + start := time.Now() + _, err = f.Fetch() + elapsed := time.Since(start) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "request canceled (Client.Timeout exceeded") + } + + assert.True(t, elapsed < 5*time.Second, "elapsed time: %s", elapsed.String()) +} diff --git a/metricbeat/modules.d/couchdb.yml.disabled b/metricbeat/modules.d/couchdb.yml.disabled new file mode 100644 index 00000000000..265878fc9db --- /dev/null +++ b/metricbeat/modules.d/couchdb.yml.disabled @@ -0,0 +1,7 @@ +# Module: couchdb +# Docs: https://www.elastic.co/guide/en/beats/metricbeat/master/metricbeat-module-couchdb.html + +- module: couchdb + metricsets: ["server"] + period: 10s + hosts: ["localhost:5984"] diff --git a/metricbeat/tests/system/test_couchdb.py b/metricbeat/tests/system/test_couchdb.py new file mode 100644 index 00000000000..9e65b5dd45c --- /dev/null +++ b/metricbeat/tests/system/test_couchdb.py @@ -0,0 +1,35 @@ +import os +import metricbeat +import unittest + + +class Test(metricbeat.BaseTest): + + COMPOSE_SERVICES = ['couchdb'] + + @unittest.skipUnless(metricbeat.INTEGRATION_TESTS, "integration test") + def test_stats(self): + """ + Couchdb module outputs an event. + """ + self.render_config_template(modules=[{ + "name": "couchdb", + "metricsets": ["server"], + "hosts": self.get_hosts(), + "period": "5s", + }]) + proc = self.start_beat() + self.wait_until(lambda: self.output_lines() > 0, max_timeout=20) + proc.check_kill_and_wait() + self.assert_no_logged_warnings() + + output = self.read_output_json() + self.assertTrue(len(output) >= 1) + evt = output[0] + print(evt) + + self.assert_fields_are_documented(evt) + + def get_hosts(self): + return [os.getenv('COUCHDB_HOST', 'localhost') + ':' + + os.getenv('COUCHDB_PORT', '5894')] diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index c9fd68bc2ba..3178b56ac5d 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -170,6 +170,12 @@ metricbeat.modules: hosts: ["localhost:8091"] enabled: true +#------------------------------- Couchdb Module ------------------------------- +- module: couchdb + metricsets: ["server"] + period: 10s + hosts: ["localhost:5984"] + #-------------------------------- Docker Module -------------------------------- - module: docker metricsets: