Writing Metricbeat modules based on other modules: Introducing light modules
Metricbeat has modules that support the collection of metrics using specific protocols in a generic way, allowing you to leverage the Elastic Stack for fetching, storing, and analyzing information coming from other existing solutions. These generic modules also provide a way of extending Metricbeat functionality by monitoring services that don’t have their own native modules. Some examples are the modules for Prometheus, Jolokia, and AWS cloudwatch.
Internally, we sometimes call these modules “input” modules, because of the parallelism we find between them and Filebeat inputs. Filebeat inputs can be used on their own, but they are also the foundations of Filebeat modules. Filebeat modules can be seen as packaged Filebeat configurations that cover specific services or use cases. They work by collecting information using inputs, but also include documentation, field mappings, and dashboards, extending filebeat functionality without Go code. We wondered if we could have something similar in Metricbeat, and thus, we created the concept of Metricbeat light modules.
Light modules are a new way of defining Metricbeat modules based on existing implementations. They don't contain any Go code — all functionality is provided by existing metricsets. They can be seen as predefined configurations for generic metricsets. They speed up the development of new modules for services whose monitoring is based on protocols we already support, and they provide a way to share existing Metricbeat configurations and dashboards.
Implementing Metricbeat light modules
Metricbeat light modules keep the known structure of modules and metricsets, but instead of using Go code to define the modules and metricsets, files containing definitions as metadata are used.
Each light module includes a module.yml
file with the list of light metricsets, and for each metricset, there is a directory with a manifest.yml
with its definition. Light modules included in the beats repository can also provide documentation files, dashboards, field mappings, or automated tests, just like any other module.
A basic light module with two metricsets would have a structure like this one:
├── module.yml ├── _meta │ ├── config.yml │ └── docs.asciidoc ├── metricset1 │ ├── manifest.yml │ └── _meta │ └── docs.asciidoc └── metricset2 ├── manifest.yml └── _meta └── docs.asciidoc
A manifest file includes a reference to an existing
metricset, allowing to override or define new defaults. For example the
following file would define a module based on the metricset
somemetricset
of the module somemodule
, and would set a couple of default settings:
input: module: somemodule metricset: somemetricset defaults: some_option: foo other_option: bar default: true
A Metricbeat user could then use it with a configuration similar to the one of any other module:
- module: module metricsets: ['metricset1', 'metricset2'] ...
Light modules are designed as a fallback mechanism to current modules — if a module is included in Metricbeat configuration and is not found along the registered ones, then Metricbeat looks for an existing light module and metricset with the given name in its home path. If it is found, it is loaded and instantiated.
As we have seen, a light module manifest references a metricset as its input implementation. The reference metricset must be an existing native metricset — it cannot reference another light module. The final module would be instantiated and customized with the default settings defined in the manifest.
There can be modules that mix native metricsets with light metricsets, for example we plan to implement some AWS metricsets, like elb
, as light modules based on the AWS cloudwatch
metricset.
The first module implemented as a light module is for CockroachDB; its beta version is included in Metricbeat 7.3.
Example: CockroachDB light module
CockroachDB is a good service to use as an initial example because it can be monitored using an existing metricset — the Prometheus collector — but it requires a small customization to set the proper metrics endpoint (it uses /_status/vars
as the metrics path instead of the default /metrics
). If we wanted to monitor CockroachDB with the Prometheus collector metricset, we could do it using a configuration like the following one:
- module: prometheus metricsets: ['collector'] hosts: ['localhost:8080'] metrics_path: '/_status/vars'
If we want to migrate this configuration to a light module, we can do it with the following files:
x-pack/metricbeat/module/cockroachdb/module.yml
name: cockroachdb metricsets: - status
x-pack/metricbeat/module/cockroachdb/status/manifest.yml
input: module: prometheus metricset: collector defaults: metrics_path: /_status/vars default: true
With this module definition we are telling Metricbeat to use the collector
metricset of the prometheus
module, but replacing the default metrics_path
with /_status/vars
. We are also indicating that this is a default metricset of this module, so it is instantiated if no other metricset is specified in the configuration file.
Despite not having code, testing of this module is done in a similar way to other modules. But because its definition won't be included in the test binary files, we have to take a few things into account.
First, base module and metricset may not be registered in the modules registry of the testing binary, so we have to explicitly import it so it gets registered:
import ( _ "github.com/elastic/beats/metricbeat/module/prometheus" _ "github.com/elastic/beats/metricbeat/module/prometheus/collector" )
And the modules registry doesn’t know anything about light modules in
test binaries, so we have to explicitly indicate where they are located.
They will be loaded from this location as configuration files, so we
also remove the permission checks that are applied to these files. Both
things can be done in an
init()
function in the test files of the module:
func init() { os.Setenv("BEAT_STRICT_PERMS", "false") mb.Registry.SetSecondarySource( xpackmb.NewLightModulesSource("../../../module")) }
Once the module is ready, it can be used in a way that feels familiar to Metricbeat users:
- module: cockroachdb metricsets: ['status'] hosts: ['localhost:8080']
Events generated look like these:
{ "@timestamp": "2019-05-22T10:00:30.818Z", "event": { "dataset": "cockroachdb.status", "duration": 29065910, "module": "cockroachdb" }, "metricset": { "name": "status" }, "prometheus": { "labels": { "le": "1638399", "store": "1" }, "metrics": { "raft_process_applycommitted_latency_bucket": 106946, "raft_process_commandcommit_latency_bucket": 111112, "raft_process_handleready_latency_bucket": 21243, "raft_process_logcommit_latency_bucket": 53080 } }, "service": { "address": "172.17.0.2:8080", "type": "cockroachdb" } }
They are events like the ones that would be generated by the Prometheus collector metricset if used on its own, but with the event and service information replaced to indicate the module and dataset used.
Prometheus namespacing is intentionally kept so it reuses the fields mappings of the original metricset.
Finally, this module also includes a dashboard that makes use of some of the available metrics, like:
prometheus.metrics.sql_conns
for the number of active SQL connectionsprometheus.metrics.replicas
for the number of replicasprometheus.metrics.ranges_unavailable
for the number of unavailable ranges
What's next?
There are many use cases where this new framework for developing Metricbeat modules can be useful. Besides the example based on Prometheus seen in this article, we are already working on the AWS ELB metricset based on the cloudwatch metricset, and we also plan to start working on supporting more Java services using the Jolokia module.
On the tooling side, we think that metricbeat
subcommands give users a complete toolset for contributing new modules without more requirements than the Elastic Stack itself. In this sense we are thinking about ways to extend
metricbeat test
, or introduce new subcommands that can be used for light modules validation.
Finally, we invite you to create your own light modules. If you're already using one of the mentioned generic modules to monitor some services, consider migrating this configuration to a module others can use.
If you want to learn more, take a look at the design issue, the initial implementation, and the pull request for the CockroachDB module. And if you have any questions, remember that we are always happy to help on the Discuss forums.