Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
- "fxconfig"
- "fxgenerate"
- "fxlog"
- "fxmetrics"
- "fxtrace"
steps:
- name: Checkout
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/fxmetrics-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: "fxmetrics-ci"

on:
push:
branches:
- "feat**"
- "fix**"
- "hotfix**"
- "chore**"
paths:
- "fxmetrics/**.go"
- "fxmetrics/go.mod"
- "fxmetrics/go.sum"
pull_request:
types:
- opened
- synchronize
- reopened
branches:
- main
paths:
- "fxmetrics/**.go"
- "fxmetrics/go.mod"
- "fxmetrics/go.sum"

jobs:
ci:
uses: ./.github/workflows/common-ci.yml
secrets: inherit
with:
module: "fxmetrics"
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ Yokai is using [Fx](https://github.com/uber-go/fx) for its plugin system.

Yokai's `Fx modules` are the plugins for your Yokai application.

| Fx Module | Description |
|--------------------------|------------------------------------|
| [fxconfig](fxconfig) | Fx module for [config](config) |
| [fxgenerate](fxgenerate) | Fx module for [generate](generate) |
| [fxlog](fxlog) | Fx module for [log](log) |
| [fxtrace](fxtrace) | Fx module for [trace](trace) |
| Fx Module | Description |
|--------------------------|-------------------------------------------------------------------------|
| [fxconfig](fxconfig) | Fx module for [config](config) |
| [fxgenerate](fxgenerate) | Fx module for [generate](generate) |
| [fxlog](fxlog) | Fx module for [log](log) |
| [fxmetrics](fxmetrics) | Fx module for [prometheus](https://github.com/prometheus/client_golang) |
| [fxtrace](fxtrace) | Fx module for [trace](trace) |

They can also be used in any [Fx](https://github.com/uber-go/fx) based Go application.

Expand Down
64 changes: 64 additions & 0 deletions fxmetrics/.golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
run:
timeout: 5m
concurrency: 8

linters:
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- containedctx
- contextcheck
- decorder
- dogsled
- dupl
- durationcheck
- errcheck
- errchkjson
- errname
- errorlint
- forbidigo
- forcetypeassert
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- gofmt
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosimple
- govet
- grouper
- importas
- ineffassign
- interfacebloat
- logrlint
- maintidx
- makezero
- misspell
- nestif
- nilerr
- nilnil
- nlreturn
- nolintlint
- nosprintfhostport
- prealloc
- predeclared
- promlinter
- reassign
- staticcheck
- tenv
- thelper
- tparallel
- typecheck
- unconvert
- unparam
- unused
- usestdlibvars
- whitespace
206 changes: 206 additions & 0 deletions fxmetrics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Fx Metrics Module

[![ci](https://github.com/ankorstore/yokai/actions/workflows/fxmetrics-ci.yml/badge.svg)](https://github.com/ankorstore/yokai/actions/workflows/fxmetrics-ci.yml)
[![go report](https://goreportcard.com/badge/github.com/ankorstore/yokai/fxmetrics)](https://goreportcard.com/report/github.com/ankorstore/yokai/fxmetrics)
[![codecov](https://codecov.io/gh/ankorstore/yokai/graph/badge.svg?token=ghUBlFsjhR&flag=fxmetrics)](https://app.codecov.io/gh/ankorstore/yokai/tree/main/fxmetrics)
[![Deps](https://img.shields.io/badge/osi-deps-blue)](https://deps.dev/go/github.com%2Fankorstore%2Fyokai%2Ffxmetrics)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/ankorstore/yokai/fxmetrics)](https://pkg.go.dev/github.com/ankorstore/yokai/fxmetrics)

> [Fx](https://uber-go.github.io/fx/) module for [prometheus](https://github.com/prometheus/client_golang).

<!-- TOC -->
* [Installation](#installation)
* [Documentation](#documentation)
* [Dependencies](#dependencies)
* [Loading](#loading)
* [Registration](#registration)
* [Override](#override)
* [Testing](#testing)
<!-- TOC -->

## Installation

```shell
go get github.com/ankorstore/yokai/fxmetrics
```

## Documentation

### Dependencies

This module is intended to be used alongside:

- the [fxconfig](https://github.com/ankorstore/yokai/tree/main/fxconfig) module
- the [fxlog](https://github.com/ankorstore/yokai/tree/main/fxlog) module

### Loading

To load the module in your Fx application:

```go
package main

import (
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/fx"
)

func main() {
fx.New(
fxconfig.FxConfigModule, // load the module dependencies
fxlog.FxLogModule,
fxmetrics.FxMetricsModule, // load the module
fx.Invoke(func(registry *prometheus.Registry) { // invoke the metrics registry
// ...
}),
).Run()
}
```

### Registration

This module provides the possibility to register your metrics [collectors](https://github.com/prometheus/client_golang/blob/main/prometheus/collector.go) in a common `*prometheus.Registry` via `AsMetricsCollector()`:

```go
package main

import (
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/fx"
)

var SomeCounter = prometheus.NewCounter(prometheus.CounterOpts{
Name: "some_total",
Help: "some help",
})

func main() {
fx.New(
fxconfig.FxConfigModule, // load the module dependencies
fxlog.FxLogModule,
fxmetrics.FxMetricsModule, // load the module
fx.Options(
fxmetrics.AsMetricsCollector(SomeCounter), // register the counter
),
fx.Invoke(func() {
SomeCounter.Inc() // manipulate the counter
}),
).Run()
}
```

**Important**: even if convenient, it's recommended to **NOT** use the [promauto](https://github.com/prometheus/client_golang/tree/main/prometheus/promauto) way of registering metrics,
but to use instead `fxmetrics.AsMetricsCollector()`, as `promauto` uses a global registry that leads to data race
conditions in testing.

Also, if you want to register several collectors at once, you can use `fxmetrics.AsMetricsCollectors()`

### Override

By default, the `*prometheus.Registry` is created by the [DefaultMetricsRegistryFactory](factory.go).

If needed, you can provide your own factory and override the module:

```go
package main

import (
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/fx"
)

type CustomMetricsRegistryFactory struct{}

func NewCustomMetricsRegistryFactory() fxmetrics.MetricsRegistryFactory {
return &CustomMetricsRegistryFactory{}
}

func (f *CustomMetricsRegistryFactory) Create() (*prometheus.Registry, error) {
return prometheus.NewPedanticRegistry(), nil
}

func main() {
fx.New(
fxconfig.FxConfigModule, // load the module dependencies
fxlog.FxLogModule,
fxmetrics.FxMetricsModule, // load the module
fx.Decorate(NewCustomMetricsRegistryFactory), // override the module with a custom factory
fx.Invoke(func(registry *prometheus.Registry) { // invoke the custom registry
// ...
}),
).Run()
}
```

### Testing

This module provides the possibility to easily test your metrics with the prometheus package [testutil](https://github.com/prometheus/client_golang/tree/main/prometheus/testutil) helpers.

```go
package main_test

import (
"strings"
"testing"

"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"go.uber.org/fx"
"go.uber.org/fx/fxtest"
)

var SomeCounter = prometheus.NewCounter(prometheus.CounterOpts{
Name: "some_total",
Help: "some help",
})

func TestSomeCounter(t *testing.T) {
t.Setenv("APP_CONFIG_PATH", "testdata/config")

var registry *prometheus.Registry

fxtest.New(
t,
fx.NopLogger,
fxconfig.FxConfigModule,
fxlog.FxLogModule,
fxmetrics.FxMetricsModule,
fx.Options(
fxmetrics.AsMetricsCollector(SomeCounter),
),
fx.Invoke(func() {
SomeCounter.Add(9)
}),
fx.Populate(&registry),
).RequireStart().RequireStop()

// metric assertions
expectedHelp := `
# HELP some_total some help
# TYPE some_total counter
`
expectedMetric := `
some_total 9
`

err := testutil.GatherAndCompare(
registry,
strings.NewReader(expectedHelp+expectedMetric),
"some_total",
)
assert.NoError(t, err)
}
```
23 changes: 23 additions & 0 deletions fxmetrics/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package fxmetrics

import (
"github.com/prometheus/client_golang/prometheus"
)

// MetricsRegistryFactory is the interface for [prometheus.Registry] factories.
type MetricsRegistryFactory interface {
Create() (*prometheus.Registry, error)
}

// DefaultMetricsRegistryFactory is the default [MetricsRegistryFactory] implementation.
type DefaultMetricsRegistryFactory struct{}

// NewDefaultMetricsRegistryFactory returns a [DefaultMetricsRegistryFactory], implementing [MetricsRegistryFactory].
func NewDefaultMetricsRegistryFactory() MetricsRegistryFactory {
return &DefaultMetricsRegistryFactory{}
}

// Create returns a new [prometheus.Registry].
func (f *DefaultMetricsRegistryFactory) Create() (*prometheus.Registry, error) {
return prometheus.NewRegistry(), nil
}
29 changes: 29 additions & 0 deletions fxmetrics/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package fxmetrics_test

import (
"testing"

"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
)

func TestDefaultMetricsRegistryFactory(t *testing.T) {
t.Parallel()

factory := fxmetrics.NewDefaultMetricsRegistryFactory()

assert.IsType(t, &fxmetrics.DefaultMetricsRegistryFactory{}, factory)
assert.Implements(t, (*fxmetrics.MetricsRegistryFactory)(nil), factory)
}

func TestCreate(t *testing.T) {
t.Parallel()

factory := fxmetrics.NewDefaultMetricsRegistryFactory()

registry, err := factory.Create()
assert.NoError(t, err)

assert.IsType(t, &prometheus.Registry{}, registry)
}
Loading