gofuncy/go.go
Kevin Franklin Kim 310d95d293
fix: lint issues
2025-05-04 14:50:19 +02:00

142 lines
3.1 KiB
Go

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"
"go.opentelemetry.io/otel/trace"
)
type (
Options struct {
l *slog.Logger
ctx context.Context //nolint:containedctx // required
name string
// telemetry
meter metric.Meter
tracer trace.Tracer
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()
_, _ = fmt.Fprintf(h, "%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 o.tracer == nil {
o.tracer = otel.Tracer("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) {
defer close(err)
ctx := o.ctx
var span trace.Span
if o.tracer != nil {
ctx, span = o.tracer.Start(o.ctx, o.name)
defer span.End()
}
// create telemetry if enabled
if o.counter != nil {
o.counter.Add(ctx, 1, metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
defer o.counter.Add(ctx, -1, metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
}
if o.histogram != nil {
start := time.Now()
defer func() {
o.histogram.Record(ctx, time.Since(start).Milliseconds(), metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
}()
}
ctx = injectParentRoutineIntoContext(ctx, RoutineFromContext(ctx))
ctx = injectRoutineIntoContext(ctx, o.name)
err <- fn(ctx)
}(o, err)
return err
}