-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
extractor.go
94 lines (85 loc) · 2.61 KB
/
extractor.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
// Copyright (c) 2020 The Jaeger Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package uiconv
import (
"encoding/json"
"errors"
"fmt"
"os"
"go.uber.org/zap"
uimodel "github.com/jaegertracing/jaeger/model/json"
)
// extractor reads the spans from reader, filters by traceID, and stores as JSON into uiFile.
type extractor struct {
uiFile *os.File
traceID string
reader *spanReader
logger *zap.Logger
}
// newExtractor creates extractor.
func newExtractor(uiFile string, traceID string, reader *spanReader, logger *zap.Logger) (*extractor, error) {
f, err := os.OpenFile(uiFile, os.O_CREATE|os.O_WRONLY, os.ModePerm)
if err != nil {
return nil, fmt.Errorf("cannot create output file: %w", err)
}
logger.Sugar().Infof("Writing spans to UI file %s", uiFile)
return &extractor{
uiFile: f,
traceID: traceID,
reader: reader,
logger: logger,
}, nil
}
// Run executes the extraction.
func (e *extractor) Run() error {
e.logger.Info("Parsing captured file for trace", zap.String("trace_id", e.traceID))
var (
spans []uimodel.Span
span *uimodel.Span
err error
)
for span, err = e.reader.NextSpan(); err == nil; span, err = e.reader.NextSpan() {
if string(span.TraceID) == e.traceID {
spans = append(spans, *span)
}
}
if !errors.Is(err, errNoMoreSpans) {
return fmt.Errorf("failed when scanning the file: %w", err)
}
trace := uimodel.Trace{
TraceID: uimodel.TraceID(e.traceID),
Spans: spans,
Processes: make(map[uimodel.ProcessID]uimodel.Process),
}
// (ys) The following is not exactly correct because it does not dedupe the processes,
// but I don't think it affects the UI.
for i := range spans {
span := &spans[i]
pid := uimodel.ProcessID(fmt.Sprintf("p%d", i))
trace.Processes[pid] = *span.Process
span.Process = nil
span.ProcessID = pid
}
jsonBytes, err := json.Marshal(trace)
if err != nil {
return fmt.Errorf("failed to marshal UI trace: %w", err)
}
e.uiFile.Write([]byte(`{"data": [`))
e.uiFile.Write(jsonBytes)
e.uiFile.Write([]byte(`]}`))
e.uiFile.Sync()
e.uiFile.Close()
e.logger.Sugar().Infof("Wrote spans to UI file %s", e.uiFile.Name())
return nil
}