Skip to content

Commit c06bcdd

Browse files
authored
Refactor zmetrics to include appStart, appVersion and appRevision metrics and more (#82)
* Fix cache metrics and minor refactor on zmetrics * Refactor zmetrics: add app-name and app-version to labels * minor change * test * Fix tests * Fix middleware order * linter * Set version and revision in metrics * Update readme * Add version and revision metric * minor change
1 parent 60cf839 commit c06bcdd

File tree

16 files changed

+172
-81
lines changed

16 files changed

+172
-81
lines changed

cmd/demo-metrics/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func main() {
1313
logger.InitLogger(logger.Config{Level: constants.DebugLevel})
1414
r := runner.NewRunner()
1515

16-
r.AddTask(metrics.NewTaskMetrics("/metrics", "9090"))
16+
r.AddTask(metrics.NewTaskMetrics("/metrics", "9090", "demo"))
1717

1818
// Now start all the tasks
1919
r.StartAndWait()

cmd/demo-panic/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func main() {
1414
r := runner.NewRunner()
1515

1616
// This will panic
17-
r.AddTask(metrics.NewTaskMetrics("BADURL", "8080"))
17+
r.AddTask(metrics.NewTaskMetrics("BADURL", "8080", "demo"))
1818

1919
// Now start all the tasks
2020
r.StartAndWait()

pkg/metrics/collectors/counter.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,25 @@ func (c *Counter) Update(collector prometheus.Collector, value float64, labels .
2424

2525
return fmt.Errorf("invalid metric type, expected Counter")
2626
}
27+
28+
func (c *Counter) Inc(collector prometheus.Collector, labels ...string) error {
29+
if len(labels) > 0 {
30+
if metricVec, ok := collector.(*prometheus.CounterVec); ok {
31+
metric := metricVec.WithLabelValues(labels...)
32+
metric.Inc()
33+
return nil
34+
}
35+
return fmt.Errorf("invalid metric type, expected CounterVec for labels")
36+
}
37+
38+
if metric, ok := collector.(prometheus.Counter); ok {
39+
metric.Inc()
40+
return nil
41+
}
42+
43+
return fmt.Errorf("invalid metric type, expected Counter")
44+
}
45+
46+
func (c *Counter) Dec(_ prometheus.Collector, _ ...string) error {
47+
return fmt.Errorf("dec is not supported by counter metric")
48+
}

pkg/metrics/collectors/counter_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,12 @@ func TestCounterUpdate(t *testing.T) {
1515
assert.NoError(t, err)
1616
assert.Equal(t, float64(10), testutil.ToFloat64(counter))
1717
}
18+
19+
func TestCounterInc(t *testing.T) {
20+
counter := prometheus.NewCounter(prometheus.CounterOpts{Name: "test_counter"})
21+
c := &Counter{}
22+
23+
err := c.Inc(counter)
24+
assert.NoError(t, err)
25+
assert.Equal(t, float64(1), testutil.ToFloat64(counter))
26+
}

pkg/metrics/handler.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,17 @@ type IncrementDecrementHandler interface {
1717
}
1818

1919
func (t *taskMetrics) performMetricAction(name string, action func(MetricHandler, prometheus.Collector, ...string) error, labels ...string) error {
20-
name = formatMetricName(name)
20+
labels = append(labels, t.appName)
21+
2122
t.mux.RLock()
2223
metricDetail, ok := t.metrics[name]
2324
t.mux.RUnlock()
2425

2526
if !ok {
26-
return fmt.Errorf("error: Metric not found for %s", name)
27-
}
28-
29-
if err := action(metricDetail.Handler, metricDetail.Collector, labels...); err != nil {
30-
return fmt.Errorf("error performing action on metric %s. Err: %s", name, err.Error())
27+
return fmt.Errorf("metric %s not registered", name)
3128
}
3229

33-
return nil
30+
return action(metricDetail.Handler, metricDetail.Collector, labels...)
3431
}
3532

3633
func (t *taskMetrics) UpdateMetric(name string, value float64, labels ...string) error {

pkg/metrics/prometheus.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type TaskMetrics interface {
2323
IncrementMetric(name string, labels ...string) error
2424
DecrementMetric(name string, labels ...string) error
2525
Name() string
26+
AppName() string
2627
Stop() error
2728
}
2829

@@ -36,12 +37,18 @@ type taskMetrics struct {
3637
port string
3738
metrics map[string]MetricDetail
3839
mux sync.RWMutex
40+
appName string
3941
}
4042

41-
func NewTaskMetrics(path string, port string) TaskMetrics {
43+
func NewTaskMetrics(path, port, appName string) TaskMetrics {
44+
if appName == "" {
45+
panic("appName is mandatory")
46+
}
47+
4248
return &taskMetrics{
4349
path: path,
4450
port: port,
51+
appName: appName,
4552
metrics: make(map[string]MetricDetail),
4653
}
4754
}
@@ -50,6 +57,10 @@ func (t *taskMetrics) Name() string {
5057
return "metrics"
5158
}
5259

60+
func (t *taskMetrics) AppName() string {
61+
return t.appName
62+
}
63+
5364
func (t *taskMetrics) Start() error {
5465
router := chi.NewRouter()
5566

pkg/metrics/prometheus_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import (
99
)
1010

1111
func TestName(t *testing.T) {
12-
metrics := NewTaskMetrics("/metrics", "9090")
12+
metrics := NewTaskMetrics("/metrics", "9090", "test")
1313
assert.Equal(t, "metrics", metrics.Name())
1414
}
1515

1616
func TestStart(t *testing.T) {
1717
logger.InitLogger(logger.Config{})
18-
metrics := NewTaskMetrics("/metrics", "9091")
18+
metrics := NewTaskMetrics("/metrics", "9091", "test")
1919

2020
go func() {
2121
err := metrics.Start()

pkg/metrics/readme.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- [Introduction](#introduction)
66
- [Features](#features)
7+
- [Default Labels](#default-labels)
78
- [Code Structure](#code-structure)
89
- [Metrics Collectors](#metrics-collectors)
910
- [Counter](#counter)
@@ -28,6 +29,22 @@ This project provides a comprehensive metrics collection and reporting framework
2829
- Thread-safe metric updates with read-write mutexes.
2930
- Auto-registering of metrics based on type.
3031

32+
## Default Labels
33+
34+
Every metric reported by the framework automatically includes the following labels to provide more context about the application's environment:
35+
36+
- `app_name`: Identifies the name of the application.
37+
- `app_revision`: Specifies the current revision of the application, typically a Git commit hash or a similar identifier.
38+
- `app_version`: Indicates the current version of the application.
39+
40+
These labels help in distinguishing metrics across different environments, versions, and revisions of the application. For example, a metric for request duration might look like this:
41+
42+
```
43+
request_duration_ms{app_name="ledger-live",app_revision="main-9a60456",app_version="v0.8.3",method="GET",path="/addresses/{address}/transactions",status="200"} 435
44+
```
45+
46+
This approach ensures that metrics data can be correlated with specific releases and deployments of the application, providing valuable insights during analysis and troubleshooting.
47+
3148
## Code Structure
3249

3350
- `/metrics/collectors/`: Contains the implementation of various metrics collectors (Counter, Gauge, Histogram).

pkg/metrics/register.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import (
88
"strings"
99
)
1010

11+
const (
12+
APPNameLabel = "app_name"
13+
)
14+
1115
type collectorRegister func(name, help string, labels []string, handler MetricHandler) (prometheus.Collector, error)
1216

1317
var (
@@ -19,6 +23,7 @@ var (
1923
)
2024

2125
func (t *taskMetrics) RegisterMetric(name string, help string, labels []string, handler MetricHandler) error {
26+
labels = append(labels, APPNameLabel)
2227
var metric prometheus.Collector
2328

2429
name = formatMetricName(name)

pkg/metrics/register_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func TestRegisterMetric(t *testing.T) {
1414
assert.NoError(t, err)
1515
assert.NotNil(t, tm.metrics["test_counter"])
1616
assert.IsType(t, MetricDetail{}, tm.metrics["test_counter"])
17-
assert.IsType(t, prometheus.NewCounter(prometheus.CounterOpts{}), tm.metrics["test_counter"].Collector)
17+
assert.IsType(t, prometheus.NewCounterVec(prometheus.CounterOpts{}, []string{"app_name", "app_version"}), tm.metrics["test_counter"].Collector)
1818
}
1919

2020
func TestFormatMetricName(t *testing.T) {

0 commit comments

Comments
 (0)