-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprofiler.go
More file actions
132 lines (111 loc) · 3.23 KB
/
Copy pathprofiler.go
File metadata and controls
132 lines (111 loc) · 3.23 KB
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
package profiler
import (
"encoding/json"
"errors"
"fmt"
"github.com/grafana/pyroscope-go"
"time"
log "github.com/sirupsen/logrus"
"net"
"net/http"
"runtime"
"sync"
)
var profiler *pyroscope.Profiler
var profilerMu sync.Mutex
var server http.Server
func init() {
runtime.SetMutexProfileFraction(5)
runtime.SetBlockProfileRate(5)
server = http.Server{
Addr: ":6061",
ReadTimeout: 60 * time.Second,
ReadHeaderTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 15 * time.Second,
}
http.HandleFunc("/profiler/toggle", restrict(profilerToggle))
go func() {
if err := server.ListenAndServe(); err != nil && !errors.Is(http.ErrServerClosed, err) {
log.Error("failed on serve profiler server")
}
}()
}
type TogglingRequest struct {
CollectorAddress string `json:"collectorAddress"`
ApplicationName string `json:"applicationName"`
Tags map[string]string `json:"tags"`
}
// func WithCustomMux()
func profilerToggle(w http.ResponseWriter, r *http.Request) {
profilerMu.Lock()
defer profilerMu.Unlock()
if profiler != nil {
err := stopProfiler()
if err != nil {
log.WithError(err).Error("error in toggle OFF profiler")
http.Error(w, fmt.Sprintf("error while stoping profiler: %v", err), http.StatusInternalServerError)
return
}
} else {
var request TogglingRequest
err := json.NewDecoder(r.Body).Decode(&request)
if err != nil {
http.Error(w, fmt.Sprintf("error while toggling profiler: %v", err), http.StatusBadRequest)
return
}
defer r.Body.Close()
log.Infof("toggling profiler ON")
err = startProfiler(request.ApplicationName, request.CollectorAddress, request.Tags)
if err != nil {
log.WithError(err).Error("error in toggle ON profiler")
http.Error(w, fmt.Sprintf("error while starting profiler: %v", err), http.StatusInternalServerError)
return
}
}
}
// restrict middleware to prevent request from external clients other than localhost
func restrict(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
http.Error(w, "could not parse IP address", http.StatusInternalServerError)
return
}
if ip != "127.0.0.1" && ip != "::1" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
}
}
func stopProfiler() error {
log.Infof("toggling proflier OFF")
profiler.Flush(false)
err := profiler.Stop()
profiler = nil
return err
}
func startProfiler(applicationName, collectorAddress string, tags map[string]string) error {
var err error
profiler, err = pyroscope.Start(pyroscope.Config{
ApplicationName: applicationName,
ServerAddress: collectorAddress,
Tags: tags,
ProfileTypes: []pyroscope.ProfileType{
// these profile types are enabled by default:
pyroscope.ProfileCPU,
pyroscope.ProfileAllocObjects,
pyroscope.ProfileAllocSpace,
pyroscope.ProfileInuseObjects,
pyroscope.ProfileInuseSpace,
// these profile types are optional:
pyroscope.ProfileGoroutines,
pyroscope.ProfileMutexCount,
pyroscope.ProfileMutexDuration,
pyroscope.ProfileBlockCount,
pyroscope.ProfileBlockDuration,
},
})
return err
}