-
Notifications
You must be signed in to change notification settings - Fork 0
/
go.go
129 lines (116 loc) · 2.8 KB
/
go.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package gofuncy
import (
"context"
"crypto/sha256"
"fmt"
"log/slog"
"os"
"runtime"
"time"
"github.com/Ju0x/humanhash"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
type (
Options struct {
l *slog.Logger
ctx context.Context
name string
// telemetry
meter metric.Meter
counter metric.Int64UpDownCounter
counterName string
histogram metric.Int64Histogram
histogramName string
telemetryEnabled bool
}
Option func(*Options)
)
func WithName(name string) Option {
return func(o *Options) {
o.name = name
}
}
func WithContext(ctx context.Context) Option {
return func(o *Options) {
o.ctx = ctx
}
}
func WithTelemetryEnabled(enabled bool) Option {
return func(o *Options) {
o.telemetryEnabled = enabled
}
}
func WithMeter(meter metric.Meter) Option {
return func(o *Options) {
o.meter = meter
}
}
func WithCounterName(name string) Option {
return func(o *Options) {
o.counterName = name
}
}
func WithHistogramName(name string) Option {
return func(o *Options) {
o.histogramName = name
}
}
func Go(fn Func, opts ...Option) <-chan error {
o := &Options{
l: slog.Default(),
counterName: "gofuncy.routine.count",
histogramName: "gofuncy.routine.duration",
telemetryEnabled: os.Getenv("OTEL_ENABLED") == "true",
}
for _, opt := range opts {
if opt != nil {
opt(o)
}
}
if o.ctx == nil {
o.ctx = context.Background()
}
if o.name == "" {
if _, file, line, ok := runtime.Caller(0); ok {
h := sha256.New()
_, _ = h.Write([]byte(fmt.Sprintf("%s:%d", file, line)))
o.name, _ = humanhash.Humanize(h.Sum(nil), 2, "-")
}
}
// create telemetry if enabled
if o.telemetryEnabled {
if o.meter == nil {
o.meter = otel.Meter("gofuncy")
}
if value, err := o.meter.Int64UpDownCounter(o.counterName); err != nil {
o.l.Error("failed to initialize gauge", "error", err)
} else {
o.counter = value
}
if value, err := o.meter.Int64Histogram(o.histogramName); err != nil {
o.l.Error("failed to initialize histogram", "error", err)
} else {
o.histogram = value
}
}
err := make(chan error)
go func(o *Options, err chan<- error) {
// create telemetry if enabled
if o.counter != nil {
o.counter.Add(o.ctx, 1, metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
defer o.counter.Add(o.ctx, -1, metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
}
if o.histogram != nil {
start := time.Now()
defer func() {
o.histogram.Record(o.ctx, time.Since(start).Milliseconds(), metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
}()
}
ctx := injectParentRoutineIntoContext(o.ctx, RoutineFromContext(o.ctx))
ctx = injectRoutineIntoContext(ctx, o.name)
err <- fn(ctx)
}(o, err)
return err
}