﻿---
title: Metricset Details
description: This topic provides additional details about creating metricsets. Each metricset can have its own configuration variables defined. To make use of these...
url: https://www.elastic.co/elastic/docs-builder/docs/3016/extend/beats/metricset-details
products:
  - Beats
applies_to:
  - Elastic Stack: Generally available
---

# Metricset Details
This topic provides additional details about creating metricsets.

## Adding Special Configuration Options

Each metricset can have its own configuration variables defined. To make use of these variables, you must extend the `New` method. For example, let’s assume that you want to add a `password` config option to the metricset. You would extend `beat.yml` in the following way:
```yaml
metricbeat.modules:
- module: {module}
  metricsets: ["{metricset}"]
  password: "test1234"
```

To read in the new `password` config option, you need to modify the `New` method. First you define a config struct that contains the value types to be read. You can set default values, as needed. Then you pass the config to the `UnpackConfig` method for loading the configuration.
Your implementation should look something like this:
```go
type MetricSet struct {
	mb.BaseMetricSet
	password string
}

func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

	// Unpack additional configuration options.
	config := struct {
		Password string `config:"password"`
	}{
		Password: "",
	}
	err := base.Module().UnpackConfig(&config)
	if err != nil {
		return nil, err
	}

	return &MetricSet{
		BaseMetricSet: base,
		password:      config.Password,
	}, nil
}
```


### Timeout Connections to Services

Each time the `Fetch` method is called, it makes a request to the service, so it’s important to handle the connections correctly. We recommended that you set up the connections in the `New` method and persist them in the `MetricSet` object. This allows connections to be reused.
One very important point is that connections must respect the timeout variable: `base.Module().Config().Timeout`. If the timeout elapses before the request completes, the request must be ended and an error must be returned to make sure the next request can be started on time. By default the Timeout is set to Period, so one request gets ended before a new request is made.
If a request must be ended or has an error, make sure that you return a useful error message. This error message is also sent to Elasticsearch, making it possible to not only fetch metrics from the service, but also report potential problems or errors with the metricset.

### Data Transformation

If the data transformation that has to happen in the `Fetch` method is extensive, we recommend that you create a second file called `data.go` in the same package as the metricset. The `data.go` file should contain a function called `eventMapping(...)`. A separate file is not required, but is currently a best practice because it isolates the functionality of the metricset and `Fetch` method from the data mapping.

### fields.yml

You can find up to 3 different types of files named `fields.yml` in the beats repository for each metricbeat module:
- `metricbeat/fields.yml`: Contains the definitions to create the Elasticsearch template, the Kibana index pattern configuration and the exported fields documentation for metricsets. To make sure the Elasticsearch template is correct, it’s important to keep this file up-to-date with all the changes. Generally, you shouldn’t touch this file manually because it’s generated by some commands in the build environment.
- `metricbeat/module/{{module}}/_meta/fields.yml`: Contains the general top level structure for all metricsets in a module. Normally you only need to modify the description in this file. Here is an example for the `fields.yml` file from the MySQL module.
  ```yaml
  - key: mysql
    title: "MySQL"
    description: >
      MySQL server status metrics collected from MySQL.
    short_config: false
    release: ga
    version: 
      beta: 9.0.0
      ga: 9.1.0
    fields:
      - name: mysql
        type: group
        description: >
          `mysql` contains the metrics that were obtained from MySQL
          query.
        fields:
  ```
- `metricbeat/module/{{module}}/{metricset}/_meta/fields.yml`: Contains all fields definitions retrieved by the metricset. As field types, each field must have a core data type [supported by elasticsearch](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3016/reference/elasticsearch/mapping-reference/field-data-types#_core_datatypes). Here’s a very basic example that shows one group from the MySQL `status` metricset:
  ```yaml
  - name: status
    type: group
    description: >
      `status` contains the metrics that were obtained by the status SQL query.
    version:
      ga: 9.0.0 
    fields:
      - name: aborted
        type: group
        description: Aborted status fields.
        fields:
          - name: clients
            type: integer
            description: >
              The number of connections that were aborted because the client died without closing the connection properly.

          - name: connects
            type: integer
            version:
              beta: 9.1.0 
            description: >
              The number of failed attempts to connect to the MySQL server.
  ```


### Testing

It’s important to also add tests for your metricset. There are three different types of tests that you need for testing a Beat:
- unit tests
- integration tests
- system tests

We recommend that you use all three when you create a metricset. Unit tests are written in Go and have no dependencies. Integration tests are also written in Go but require the service from which the module collects metrics to also be running. System tests for Metricbeat also require the service to be running in most cases and are written in Python based on our small Python test framework. We use [venv](https://docs.python.org/3/library/venv.html) to deal with Python dependencies. You can simply run the command `make python-env`  and then `. build/python-env/bin/activate` .
You should use a combination of the three test types to test your metricsets because each method has advantages and disadvantages. To get started with your own tests, it’s best to look at the existing tests. You’ll find the unit and integration tests in the `_test.go` files under existing modules and metricsets. Integration tests usually take the form of `TestFetch` and `TestData`. The system tests are under `tests/systems`.

#### Adding a Test Environment

Integration and system tests need an environment that’s running the service. You can create this environment by using Docker and a docker compose file. If you add a module that requires a service, you must add the service to the virtual environment. To do this, you:
- Update the `docker-compose.yml` file with your environment
- Update the `docker-entrypoint.sh` script

The `docker-compose.yml` file is at the root of Metricbeat. Most services have existing Docker modules and can be added as simply as Redis:
```yaml
redis:
  image: redis:3.2.3
```

To allow the Beat to access your service, make sure that you define the environment variables in the docker compose file and add the link to the container:
```yaml
beat:
  links:
    - redis
  environment:
    - REDIS_HOST=redis
    - REDIS_PORT=6379
```

To make sure the service is running before the tests are started, modify the `docker-entrypoint.sh` script to add a check that verifies your service is running. For example, the check for Redis looks like this:
```shell
waitFor ${REDIS_HOST} ${REDIS_PORT} Redis
```

The environment expects your service to be available as soon as it receives a response from the given address and port.

#### Adding the standard metricset integration tests

There are normally two integration tests that are part of every metricset: `TestFetch` and `TestData`. Both tests will start up a new instance of your metricset and fetch an event. In order to start a metricset, you need to create a configuration object:
```go
func getConfig() map[string]interface{} {
    return map[string]interface{}{
    "module":           "{module}",
    "metricsets":       []string{"{metricset}"},
    "hosts":      []string{GetEnvHost() + ":" + GetEnvPort()}, 
  }
}

func GetEnvHost() string { 
    host := os.Getenv("{module}_HOST")
    if len(host) == 0 {
    host = "127.0.0.1"
  }
  return host
}

func GetEnvPort() string { 
    port := os.Getenv("{module}_PORT")

    if len(port) == 0 {
      port = "1234"
    }
  return port
}
```

The `TestFetch` integration test will return a single event from your metricset, which you can use to test the validity of the data. `TestData` will (re)generate the `_meta/data.json` file that documents the data reported by the metricset.
```go
import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/elastic/beats/libbeat/tests/compose"
	mbtest "github.com/elastic/beats/metricbeat/mb/testing"
)

func TestFetch(t *testing.T) {
	compose.EnsureUp(t, "{module}") 

	f := mbtest.NewReportingMetricSetV2Error(t, getConfig())

	events, errs := mbtest.ReportingFetchV2Error(f)
	if len(errs) > 0 {
		t.Fatalf("Expected 0 errord, had %d. %v\n", len(errs), errs)
	}

	assert.NotEmpty(t, events) 

}

func TestData(t *testing.T) {

	f := mbtest.NewReportingMetricSetV2Error(t, getConfig())

	err := mbtest.WriteEventsReporterV2Error(f, t, "") 
	if !assert.NoError(t, err) {
		t.FailNow()
	}
}
```


#### Running the Tests

To run all the tests, run `make testsuite`. To only run unit tests, run `mage unitTest`, or for integration tests `mage integTest`. Be aware that a running Docker environment is needed for integration and system tests.
To run `TestData` and generate the `data.json` file, run `go test -tags=integration -data -run TestData` in the directory where your test is located.
To run the integration tests for a single module, set the `MODULE` environment variable to the name of the directory of the module. For example you can run the following command to run integration tests for `apache` module:
```shell
MODULE=apache mage integTest
```


## Documentation

Each module must be documented. The documentation is based on Markdown and is in the file `module/{{module}}/_meta/docs.md` for the module and in `module/{{module}}/{metricset}/_meta/docs.md` for the metricset. Basic documentation with the config file and an example output is automatically generated. Use these files to document specific configuration options or usage examples.