forked from equinix-labs/otel-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver_json.go
123 lines (103 loc) · 3.43 KB
/
server_json.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
package otelcli
import (
"context"
"encoding/hex"
"encoding/json"
"log"
"os"
"path/filepath"
"strconv"
"time"
"github.com/equinix-labs/otel-cli/otlpserver"
"github.com/spf13/cobra"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)
// jsonSvr holds the command-line configured settings for otel-cli server json
var jsonSvr struct {
outDir string
stdout bool
maxSpans int
spansSeen int
}
func serverJsonCmd(config *Config) *cobra.Command {
cmd := cobra.Command{
Use: "json",
Short: "write spans to json or stdout",
Long: "",
Run: doServerJson,
}
addCommonParams(&cmd, config)
cmd.Flags().StringVar(&jsonSvr.outDir, "dir", "", "write spans to json in the specified directory")
cmd.Flags().BoolVar(&jsonSvr.stdout, "stdout", false, "write span jsons to stdout")
cmd.Flags().IntVar(&jsonSvr.maxSpans, "max-spans", 0, "exit the server after this many spans come in")
return &cmd
}
func doServerJson(cmd *cobra.Command, args []string) {
config := getConfig(cmd.Context())
stop := func(otlpserver.OtlpServer) {}
cs := otlpserver.NewGrpcServer(renderJson, stop)
// stops the grpc server after timeout
timeout := config.ParseCliTimeout()
if timeout > 0 {
go func() {
time.Sleep(timeout)
cs.Stop()
}()
}
runServer(config, renderJson, stop)
}
// writeFile takes the spans and events and writes them out to json files in the
// tid/sid/span.json and tid/sid/events.json files.
func renderJson(ctx context.Context, span *tracepb.Span, events []*tracepb.Span_Event, ss *tracepb.ResourceSpans, headers map[string]string, meta map[string]string) bool {
jsonSvr.spansSeen++ // count spans for exiting on --max-spans
// TODO: check for existence of outdir and error when it doesn't exist
var outpath string
if jsonSvr.outDir != "" {
// create trace directory
outpath = filepath.Join(jsonSvr.outDir, hex.EncodeToString(span.TraceId))
os.Mkdir(outpath, 0755) // ignore errors for now
// create span directory
outpath = filepath.Join(outpath, hex.EncodeToString(span.SpanId))
os.Mkdir(outpath, 0755) // ignore errors for now
}
// write span to file
// TODO: if a span comes in twice should we continue to overwrite span.json
// or attempt some kind of merge? (e.g. of attributes)
sjs, err := json.Marshal(span)
if err != nil {
log.Fatalf("failed to marshal span to json: %s", err)
}
// write the span to /path/tid/sid/span.json
writeJson(outpath, "span.json", sjs)
// only write events out if there is at least one
for i, e := range events {
ejs, err := json.Marshal(e)
if err != nil {
log.Fatalf("failed to marshal span event to json: %s", err)
}
// write events to /path/tid/sid/event-%d.json
// TODO: ordering might be a problem if people rely on it...
filename := "event-" + strconv.Itoa(i) + ".json"
writeJson(outpath, filename, ejs)
}
if jsonSvr.maxSpans > 0 && jsonSvr.spansSeen >= jsonSvr.maxSpans {
return true // will cause the server loop to exit
}
return false
}
// writeJson takes a directory path, a filename, and json. When the path is not empty
// string the json is written to path/filename. If --stdout was specified the json will
// be printed as a line to stdout.
func writeJson(path, filename string, js []byte) {
if path != "" {
spanfile := filepath.Join(path, filename)
err := os.WriteFile(spanfile, js, 0644)
if err != nil {
log.Fatalf("could not write to file %q: %s", spanfile, err)
}
}
if jsonSvr.stdout {
os.Stdout.Write(js)
os.Stdout.WriteString("\n")
}
}