Skip to content

Commit 4da7e50

Browse files
heyvitoofeefo
authored andcommitted
feat: Prevent Init without Setup
1 parent 289142c commit 4da7e50

File tree

4 files changed

+42
-11
lines changed

4 files changed

+42
-11
lines changed

consistency/consistency_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
ioprometheusclient "github.com/prometheus/client_model/go"
1313
"github.com/prometheus/common/expfmt"
1414
"github.com/stretchr/testify/require"
15+
"go.opentelemetry.io/otel/metric/noop"
1516

1617
"github.com/ofeefo/em"
1718
)
@@ -44,8 +45,11 @@ func TestLabelConsistency(t *testing.T) {
4445
// sample.txt is the raw payload of the complete_example /metrics endpoint.
4546
data, err := os.ReadFile("./raw.txt")
4647
require.NoError(t, err)
48+
em.SetupWithMeter(noop.Meter{})
49+
obj, err := em.Init[metrics]()
50+
require.NoError(t, err)
4751

48-
ids := getAllIds(t, metrics{})
52+
ids := getAllIds(t, obj)
4953

5054
p := &expfmt.TextParser{}
5155
mfs, err := p.TextToMetricFamilies(bytes.NewBuffer(data))

initializer.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ func MustInit[T any](attrs ...attribute.KeyValue) *T {
1616
}
1717

1818
func Init[T any](attrs ...attribute.KeyValue) (*T, error) {
19+
if !p.ready {
20+
return nil, fmt.Errorf("em.Init called before em.Setup")
21+
}
1922
base := new(T)
2023
if err := initRef(base, attrs...); err != nil {
2124
return nil, err
@@ -43,15 +46,24 @@ func initRef(base any, attrs ...attribute.KeyValue) error {
4346
continue
4447
}
4548

46-
fieldIface := fieldVal.Interface()
49+
var fieldIface any
50+
if fieldVal.Type().Kind() == reflect.Ptr {
51+
fieldIface = reflect.New(fieldVal.Type().Elem()).Interface()
52+
} else {
53+
fieldIface = fieldVal.Addr().Interface()
54+
}
4755
builder, ok := fieldIface.(buildable)
4856

4957
switch {
5058
case ok:
5159
if err := builder.init(field, attrs...); err != nil {
5260
return err
5361
}
54-
fieldVal.Set(reflect.ValueOf(builder))
62+
if fieldVal.Kind() == reflect.Ptr {
63+
fieldVal.Set(reflect.ValueOf(builder))
64+
} else {
65+
fieldVal.Set(reflect.ValueOf(builder).Elem())
66+
}
5567

5668
case fieldVal.Kind() == reflect.Struct:
5769
if fieldVal.CanAddr() {
@@ -71,6 +83,7 @@ func initRef(base any, attrs ...attribute.KeyValue) error {
7183
if err := initNested(field, n, fieldVal, attrs...); err != nil {
7284
return err
7385
}
86+
fieldVal.Set(n)
7487
}
7588
}
7689
return nil

initializer_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/stretchr/testify/assert"
77
"github.com/stretchr/testify/require"
88
"go.opentelemetry.io/otel/attribute"
9+
"go.opentelemetry.io/otel/metric/noop"
910
)
1011

1112
type metrics struct {
@@ -24,6 +25,7 @@ type nested struct {
2425
Counter Counter[float64] `id:"example_more_nested_counter"`
2526
}
2627
}
28+
2729
type Embedded struct {
2830
Histogram Histogram[int64] `id:"example_embedded_histogram"`
2931
UpDownCounter UpDownCounter[int64] `id:"example_embedded_updowncounter"`
@@ -50,6 +52,7 @@ func mustSetup(t *testing.T, attrs ...attribute.KeyValue) {
5052
func initAndCall(t *testing.T) func() {
5153
t.Helper()
5254
return func() {
55+
SetupWithMeter(noop.Meter{})
5356
var m *metrics
5457
require.NotPanics(t, func() { m = MustInit[metrics]() })
5558
require.NotPanics(t, func() {

provider.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
package em
22

33
import (
4+
"sync"
5+
46
"go.opentelemetry.io/otel/attribute"
57
"go.opentelemetry.io/otel/exporters/prometheus"
68
"go.opentelemetry.io/otel/metric"
9+
"go.opentelemetry.io/otel/metric/noop"
710
m2 "go.opentelemetry.io/otel/sdk/metric"
811
"go.opentelemetry.io/otel/sdk/resource"
912
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
1013
)
1114

1215
type provider struct {
13-
m metric.Meter
16+
m metric.Meter
17+
ready bool
18+
mu sync.Mutex
1419
}
1520

16-
var p *provider = nil
21+
var p = &provider{
22+
m: noop.Meter{},
23+
}
1724

1825
func MustSetup(name string, attrs ...attribute.KeyValue) {
1926
if err := Setup(name, attrs...); err != nil {
@@ -22,15 +29,18 @@ func MustSetup(name string, attrs ...attribute.KeyValue) {
2229
}
2330

2431
func SetupWithMeter(meter metric.Meter) {
25-
if p != nil {
26-
p.m = meter
27-
} else {
28-
p = &provider{m: meter}
32+
p.mu.Lock()
33+
defer p.mu.Unlock()
34+
if meter == nil {
35+
panic("nil meter provided to SetupWithMeter")
2936
}
37+
p = &provider{m: meter, ready: true}
3038
}
3139

3240
func Setup(name string, attrs ...attribute.KeyValue) error {
33-
if p != nil {
41+
p.mu.Lock()
42+
defer p.mu.Unlock()
43+
if p.ready {
3444
return nil
3545
}
3646

@@ -41,6 +51,7 @@ func Setup(name string, attrs ...attribute.KeyValue) error {
4151

4252
res := resource.NewWithAttributes(semconv.SchemaURL, attrs...)
4353
exp := m2.NewMeterProvider(m2.WithReader(promEx), m2.WithResource(res))
44-
p = &provider{m: exp.Meter(name)}
54+
p.m = exp.Meter(name)
55+
p.ready = true
4556
return nil
4657
}

0 commit comments

Comments
 (0)