mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
``` git ls-files \*.js \*.go \ | xargs sed -i '1i // SPDX-FileCopyrightText: 2022 Free Mobile\n// SPDX-License-Identifier: AGPL-3.0-only\n' git ls-files \*.vue \ | xargs sed -i '1i <!-- SPDX-FileCopyrightText: 2022 Free Mobile -->\n<!-- SPDX-License-Identifier: AGPL-3.0-only -->\n' ```
115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
// SPDX-FileCopyrightText: 2022 Free Mobile
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
// Package metrics handles metrics for akvorado
|
|
//
|
|
// This is a wrapper around Prometheus Go client.
|
|
package metrics
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/collectors"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
|
|
"akvorado/common/reporter/logger"
|
|
"akvorado/common/reporter/stack"
|
|
)
|
|
|
|
// Metrics represents the internal state of the metric subsystem.
|
|
type Metrics struct {
|
|
logger logger.Logger
|
|
config Configuration
|
|
registry *prometheus.Registry
|
|
factoryCache map[string]*Factory
|
|
factoryCacheLock sync.RWMutex
|
|
}
|
|
|
|
// New creates a new metric registry and setup the appropriate
|
|
// exporters. The provided prefix is used for system-wide metrics.
|
|
func New(logger logger.Logger, configuration Configuration) (*Metrics, error) {
|
|
reg := prometheus.NewRegistry()
|
|
reg.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
|
|
reg.MustRegister(collectors.NewGoCollector())
|
|
m := Metrics{
|
|
logger: logger,
|
|
config: configuration,
|
|
registry: reg,
|
|
factoryCache: make(map[string]*Factory, 0),
|
|
}
|
|
|
|
return &m, nil
|
|
}
|
|
|
|
// HTTPHandler returns an handler to server Prometheus metrics.
|
|
func (m *Metrics) HTTPHandler() http.Handler {
|
|
return promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{
|
|
ErrorLog: promHTTPLogger{m.logger},
|
|
})
|
|
}
|
|
|
|
func getPrefix(module string) (moduleName string) {
|
|
if !strings.HasPrefix(module, stack.ModuleName) {
|
|
moduleName = stack.ModuleName
|
|
} else {
|
|
moduleName = strings.SplitN(module, ".", 2)[0]
|
|
}
|
|
moduleName = strings.ReplaceAll(moduleName, "/", "_")
|
|
moduleName = strings.ReplaceAll(moduleName, ".", "_")
|
|
moduleName = fmt.Sprintf("%s_", moduleName)
|
|
return
|
|
}
|
|
|
|
// Factory returns a factory to register new metrics with promauto. It
|
|
// includes the module as an automatic prefix. This method is expected
|
|
// to be called only from our own module to avoid walking the stack
|
|
// too often. It uses a cache to speedup things a little bit.
|
|
func (m *Metrics) Factory(skipCallstack int) *Factory {
|
|
callStack := stack.Callers()
|
|
call := callStack[1+skipCallstack] // Trial and error, there is a test to check it works
|
|
module := call.FunctionName()
|
|
|
|
// Hotpath
|
|
if factory := func() *Factory {
|
|
m.factoryCacheLock.RLock()
|
|
defer m.factoryCacheLock.RUnlock()
|
|
if factory, ok := m.factoryCache[module]; ok {
|
|
return factory
|
|
}
|
|
return nil
|
|
}(); factory != nil {
|
|
return factory
|
|
}
|
|
|
|
// Slow path
|
|
m.factoryCacheLock.Lock()
|
|
defer m.factoryCacheLock.Unlock()
|
|
moduleName := getPrefix(module)
|
|
factory := Factory{
|
|
prefix: moduleName,
|
|
registry: m.registry,
|
|
}
|
|
m.factoryCache[module] = &factory
|
|
return &factory
|
|
}
|
|
|
|
// Desc allocates and initializes and new metric description. Like for
|
|
// factory, names are prefixed with the module name. Unlike factory,
|
|
// there is no cache.
|
|
func (m *Metrics) Desc(skipCallstack int, name, help string, variableLabels []string) *prometheus.Desc {
|
|
callStack := stack.Callers()
|
|
call := callStack[1+skipCallstack] // Trial and error, there is a test to check it works
|
|
prefix := getPrefix(call.FunctionName())
|
|
name = fmt.Sprintf("%s%s", prefix, name)
|
|
return prometheus.NewDesc(name, help, variableLabels, nil)
|
|
}
|
|
|
|
// Collector register a custom collector.
|
|
func (m *Metrics) Collector(c prometheus.Collector) {
|
|
m.registry.MustRegister(c)
|
|
}
|