-
Notifications
You must be signed in to change notification settings - Fork 2
/
logfile.go
143 lines (118 loc) · 3.76 KB
/
logfile.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright 2022 Outreach Corporation. All Rights Reserved.
// Description: This file contains the implementation of a logfile tracer.
// The logfile tracer is an internal tracer, based on the otel tracer, that
// exports traces to an app sepcific log file.
package trace
import (
"context"
"encoding/json"
"fmt"
"net"
"os"
osexec "os/exec"
"os/user"
"runtime"
"strings"
"github.com/getoutreach/gobox/pkg/cli/logfile"
"github.com/getoutreach/gobox/pkg/events"
"github.com/getoutreach/gobox/pkg/log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric/noop"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
)
// NewLogFileTracer initializes a tracer that sends traces to a log file.
func NewLogFileTracer(ctx context.Context, serviceName string, config *Config) (tracer, error) {
tracer := &otelTracer{Config: *config}
mp := noop.NewMeterProvider()
otel.SetMeterProvider(mp)
exp, err := NewLogFileExporter(tracer.LogFile.Port)
if err != nil {
log.Error(ctx, "Unable to start trace exporter", events.NewErrorInfo(err))
}
r, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
"",
semconv.ServiceNameKey.String(serviceName),
),
)
if err != nil {
log.Error(ctx, "Unable to configure trace provider", events.NewErrorInfo(err))
}
tpOptions := []sdktrace.TracerProviderOption{
sdktrace.WithBatcher(exp),
sdktrace.WithResource(r),
sdktrace.WithSpanProcessor(Annotator{
globalTags: tracer.GlobalTags,
}),
}
tp := sdktrace.NewTracerProvider(tpOptions...)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(
propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
),
)
tracer.serviceName = serviceName
tracer.tracerProvider = tp
tracer.tracerProvider.Tracer(serviceName)
return tracer, nil
}
// NewLogFileExporter Creates a new exporter that sends all spans to the passed in port
// on localhost.
func NewLogFileExporter(port int) (sdktrace.SpanExporter, error) {
conn, err := net.Dial(logfile.TraceSocketType, fmt.Sprintf("localhost:%d", port))
if err != nil {
return nil, err
}
return &LogFileSpanExporter{conn: conn}, nil
}
// LogFileSpanExporter an exporter that sends all traces across the configured connection.
type LogFileSpanExporter struct {
conn net.Conn
}
// ExportSpans exports all the provided spans.
func (se *LogFileSpanExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
stubs := tracetest.SpanStubsFromReadOnlySpans(spans)
return json.NewEncoder(se.conn).Encode(stubs)
}
}
// Shutdown cleans up when the exporter close by ensuring that the connection gets closed.
func (se *LogFileSpanExporter) Shutdown(_ context.Context) error {
return se.conn.Close()
}
// CommonProps sets up common properties for traces in cli apps
func CommonProps() log.Marshaler {
commonProps := log.F{
"os.name": runtime.GOOS,
"os.arch": runtime.GOARCH,
}
if b, err := osexec.Command("git", "config", "user.email").Output(); err == nil {
email := strings.TrimSuffix(string(b), "\n")
// TODO(jaredallard): Turn the check into an config option
// In case of @outreach.io email, we want to add PII for easier debugging with devs
if strings.HasSuffix(email, "@outreach.io") {
commonProps["dev.email"] = email
if u, err := user.Current(); err == nil {
commonProps["os.user"] = u.Username
}
if hostname, err := os.Hostname(); err == nil {
commonProps["os.hostname"] = hostname
}
path, err := os.Getwd()
if err == nil {
commonProps["os.workDir"] = path
}
}
}
return commonProps
}