How to write a Netdata collector in Go

Let's assume you want to write a collector named example.

Steps are:

โ— If you prefer reading the source code, then check the implementation of the example module, it should give you an idea of how things work.

Module Interface#

Every module should implement the following interface:

type Module interface {
Init() bool
Check() bool
Charts() *Charts
Collect() map[string]int64

Init method#

  • Init does module initialization.
  • If it returns false, the job will be disabled.

We propose to use the following template:

// example.go
func (e *Example) Init() bool {
err := e.validateConfig()
if err != nil {
e.Errorf("config validation: %v", err)
return false
someValue, err := e.initSomeValue()
if err != nil {
e.Errorf("someValue init: %v", err)
return false
e.someValue = someValue
// ...
return true

Move specific initialization methods into the init.go file. See suggested module layout.

Check method#

  • Check returns whether the job is able to collect metrics.
  • Called after Init and only if Init returned true.
  • If it returns false, the job will be disabled.

The simplest way to implement Check is to see if we are getting any metrics from Collect. A lot of modules use such approach.

// example.go
func (e *Example) Check() bool {
return len(e.Collect()) > 0

Charts method#

โ— Netdata module produces charts, not raw metrics.

Use agent/module package to create them, it contains charts and dimensions structs.

  • Charts returns the charts (*module.Charts).
  • Called after Check and only if Check returned true.
  • If it returns nil, the job will be disabled
  • โš ๏ธ Make sure not to share returned value between module instances (jobs).

Usually charts initialized in Init and Chart method just returns the charts instance:

// example.go
func (e *Example) Charts() *Charts {
return e.charts

Collect method#

  • Collect collects metrics.
  • Called only if Check returned true.
  • Called every update_every seconds.
  • map[string]int64 keys are charts dimensions ids'.

We propose to use the following template:

// example.go
func (e *Example) Collect() map[string]int64 {
ms, err := e.collect()
if err != nil {
if len(ms) == 0 {
return nil
return ms

Move metrics collection logic into the collect.go file. See suggested module layout.

Cleanup method#

  • Cleanup performs the job cleanup/teardown.
  • Called if Init or Check fails, or we want to stop the job after Collect.

If you have nothing to clean up:

// example.go
func (Example) Cleanup() {}

Module Layout#

The general idea is to not put everything in a single file.

We recommend using one file per logical area. This approach makes it easier to maintain the module.

Suggested minimal layout:

module_name.goModule configuration, implementation and registration.
charts.goCharts, charts templates and constructor functions.
init.goInitialization methods.
collect.goMetrics collection implementation.
module_name_test.goPublic methods/functions tests.
testdata/Files containing sample data.

File module_name.go#

โ— See the example example.go.

Don't overload this file with the implementation details.

Usually it contains only:

File charts.go#

โ— See the example: charts.go.

Put charts, charts templates and charts constructor functions in this file.

File init.go#

โ— See the example: init.go.

All the module initialization details should go in this file.

  • make a function for each value that needs to be initialized.
  • a function should return a value(s), not implicitly set/change any values in the main struct.
// init.go
// Prefer this approach.
func (e Example) initSomeValue() (someValue, error) {
// ...
return someValue, nil
// This approach is ok too, but we recommend to not use it.
func (e *Example) initSomeValue() error {
// ...
m.someValue = someValue
return nil

File collect.go#

โ— See the example: collect.go.

This file is the entry point for the metrics collection.

Feel free to split it into several files if you think it makes the code more readable.

Use collect_ prefix for the filenames: collect_this.go, collect_that.go, etc.

// collect.go
func (e *Example) collect() (map[string]int64, error) {
collected := make(map[string])int64
// ...
// ...
// ...
return collected, nil

File module_name_test.go#

โ— See the example: example_test.go.

if you have no experience in testing we recommend starting with testing package documentation.

we use assert and require packages from library, check their documentation.

Testing is mandatory.

  • test only public functions and methods (New, Init, Check, Charts, Cleanup, Collect).
  • do not create a test function per a case, use table driven tests . Prefer map[string]struct{ ... } over []struct{ ... }.
  • use helper functions to prepare test cases to keep them clean and readable.

Directory testdata/#

Put files with sample data in this directory if you need any. Its name should be testdata.

Directory and file names that begin with "." or "_" are ignored by the go tool, as are directories named "testdata".

Helper packages#

There are some helper packages for writing a module.

Last updated on

Monitor everything in real time โ€“ for free

Troubleshoot slowdowns and anomalies in your infrastructure with thousands of per-second metrics, meaningful visualizations, and insightful health alarms with zero configuration.

Get Netdata