-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinstrumentation.go
94 lines (84 loc) · 2.78 KB
/
instrumentation.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
package box
import (
"context"
"fmt"
"log"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
)
var TracerName = "github.com/jan-xyz/box"
// EndpointTracing adds tracing for the endpoint. The Span name defaults to "Endpoint".
func EndpointTracing[TIn, TOut any](tp trace.TracerProvider) Middleware[TIn, TOut] {
tracer := tp.Tracer(TracerName)
spanName := "Endpoint"
return func(next Endpoint[TIn, TOut]) Endpoint[TIn, TOut] {
return func(ctx context.Context, req TIn) (TOut, error) {
ctx, span := tracer.Start(ctx, spanName)
defer span.End()
// first need to convert to `any` to use the type assertion
switch val := any(req).(type) {
case fmt.Stringer, string:
span.SetAttributes(attribute.String("req", fmt.Sprintf("%s", val)))
default:
span.SetAttributes(attribute.String("req", fmt.Sprintf("%v", val)))
}
resp, err := next(ctx, req)
span.RecordError(err)
if err != nil {
span.SetStatus(codes.Error, err.Error())
}
return resp, err
}
}
}
// EndpointLogging logs the requests and response and errors.
func EndpointLogging[TIn, TOut any]() Middleware[TIn, TOut] {
return func(next Endpoint[TIn, TOut]) Endpoint[TIn, TOut] {
return func(ctx context.Context, req TIn) (TOut, error) {
// first need to convert to `any` to use the type assertion
switch val := any(req).(type) {
case fmt.Stringer, string:
log.Printf("incoming: %s", val)
default:
log.Printf("incoming: %v", val)
}
resp, err := next(ctx, req)
// first need to convert to `any` to use the type assertion
switch val := any(resp).(type) {
case fmt.Stringer, string:
log.Printf("incoming: %s", val)
default:
log.Printf("outgoing: %v", val)
}
return resp, err
}
}
}
// EndpointMetrics records RED metrics for your Endpoint. It adds an Endpoint dimension to it.
// the default dimension value is "Endpoint".
func EndpointMetrics[TIn, TOut any](mp metric.MeterProvider) Middleware[TIn, TOut] {
meter := mp.Meter(TracerName)
endpointName := "Endpoint"
attrs := attribute.String("endpoint", endpointName)
return func(next Endpoint[TIn, TOut]) Endpoint[TIn, TOut] {
return func(ctx context.Context, req TIn) (TOut, error) {
start := time.Now()
if counter, Merr := meter.Int64Counter("requests"); Merr == nil {
counter.Add(ctx, 1, metric.WithAttributes(attrs))
}
if hist, Merr := meter.Float64Histogram("latency"); Merr == nil {
defer func() { hist.Record(ctx, time.Since(start).Seconds(), metric.WithAttributes(attrs)) }()
}
resp, err := next(ctx, req)
if err != nil {
if counter, Merr := meter.Int64Counter("errors"); Merr == nil {
counter.Add(ctx, 1, metric.WithAttributes(attrs))
}
}
return resp, err
}
}
}