/
trace.go
100 lines (93 loc) · 2.89 KB
/
trace.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
package monitoring
//
//Copyright 2018 Telenor Digital AS
//
//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.
//
import (
"io"
"io/ioutil"
"net/http"
"os"
"runtime/trace"
"strconv"
"time"
"github.com/ExploratoryEngineering/logging"
)
//
// Tracing endpoint. The trace is controlled via an unbuffered channel of
// time.Duration values. Each value is read off the channel and a trace is
// started with the given duration. The channel will block writing while a
// trace is running and reading is blocked until someone sends something on
// the trace channel.
//
var traceChan chan time.Duration
// EnableTracing starts the tracing goroutine
func EnableTracing() {
traceChan = make(chan time.Duration)
go func() {
for duration := range traceChan {
traceFileName := time.Now().Format("trace_congress_2006-01-02T150405.out")
traceFile, err := os.Create(traceFileName)
if err != nil {
logging.Error("Unable to create trace file '%s': %v", traceFileName, err)
continue
}
logging.Warning("Trace started for %d seconds. Trace file name is %s", int(duration.Seconds()), traceFileName)
if err := trace.Start(traceFile); err != nil {
logging.Error("Unable to start the trace: %v", err)
traceFile.Close()
continue
}
time.Sleep(duration)
trace.Stop()
traceFile.Close()
logging.Warning("Trace is completed. Results are placed in %s", traceFileName)
}
}()
}
func getDuration(data io.Reader) time.Duration {
buf, err := ioutil.ReadAll(data)
if err != nil {
return 0
}
val, err := strconv.Atoi(string(buf))
if err != nil || val < 1 {
return 0
}
return time.Duration(val)
}
// TraceHandler is a simple http.HandleFunc that handles POST requests. A new
// trace is started with there's a POST request and the trace channel isn't
// blocking. If the trace channel is blocking 409 conflict will be returned.
// All other methods returns 405 method not allowed.
func TraceHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
seconds := getDuration(r.Body)
if seconds < 1 {
http.Error(w, "Specify time to trace in body", http.StatusBadRequest)
return
}
select {
case traceChan <- time.Second * seconds:
io.WriteString(w, "Trace started")
default:
http.Error(w, "Trace in progress", http.StatusConflict)
}
default:
http.Error(w, "Illegal method", http.StatusMethodNotAllowed)
}
}
}