forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 1
/
service.go
151 lines (126 loc) · 3.51 KB
/
service.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
144
145
146
147
148
149
150
151
package service
import (
"expvar"
"flag"
"fmt"
"log"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"sync"
"syscall"
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/libbeat/monitoring"
"net/http"
_ "net/http/pprof"
)
// HandleSignals manages OS signals that ask the service/daemon to stop.
// The stopFunction should break the loop in the Beat so that
// the service shut downs gracefully.
func HandleSignals(stopFunction func()) {
var callback sync.Once
// On ^C or SIGTERM, gracefully stop the sniffer
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigc
logp.Debug("service", "Received sigterm/sigint, stopping")
callback.Do(stopFunction)
}()
// Handle the Windows service events
go ProcessWindowsControlEvents(func() {
logp.Debug("service", "Received svc stop/shutdown request")
callback.Do(stopFunction)
})
}
// cmdline flags
var memprofile, cpuprofile, httpprof *string
var cpuOut *os.File
func init() {
memprofile = flag.String("memprofile", "", "Write memory profile to this file")
cpuprofile = flag.String("cpuprofile", "", "Write cpu profile to file")
httpprof = flag.String("httpprof", "", "Start pprof http server")
}
// ProfileEnabled checks whether the beat should write a cpu or memory profile.
func ProfileEnabled() bool {
return withMemProfile() || withCPUProfile()
}
func withMemProfile() bool { return *memprofile != "" }
func withCPUProfile() bool { return *cpuprofile != "" }
// BeforeRun takes care of necessary actions such as creating files
// before the beat should run.
func BeforeRun() {
if withCPUProfile() {
cpuOut, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(cpuOut)
}
if *httpprof != "" {
go func() {
logp.Info("start pprof endpoint")
mux := http.NewServeMux()
// register pprof handler
mux.HandleFunc("/debug/pprof/", func(w http.ResponseWriter, r *http.Request) {
http.DefaultServeMux.ServeHTTP(w, r)
})
// register metrics handler
mux.HandleFunc("/debug/vars", metricsHandler)
endpoint := http.ListenAndServe(*httpprof, mux)
logp.Info("finished pprof endpoint: %v", endpoint)
}()
}
}
// report expvar and all libbeat/monitoring metrics
func metricsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
first := true
report := func(key string, value interface{}) {
if !first {
fmt.Fprintf(w, ",\n")
}
first = false
if str, ok := value.(string); ok {
fmt.Fprintf(w, "%q: %q", key, str)
} else {
fmt.Fprintf(w, "%q: %v", key, value)
}
}
fmt.Fprintf(w, "{\n")
monitoring.Do(monitoring.Full, report)
expvar.Do(func(kv expvar.KeyValue) {
report(kv.Key, kv.Value)
})
fmt.Fprintf(w, "\n}\n")
}
// Cleanup handles cleaning up the runtime and OS environments. This includes
// tasks such as stopping the CPU profile if it is running.
func Cleanup() {
if withCPUProfile() {
pprof.StopCPUProfile()
cpuOut.Close()
}
if withMemProfile() {
runtime.GC()
writeHeapProfile(*memprofile)
debugMemStats()
}
}
func debugMemStats() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
logp.Debug("mem", "Memory stats: In use: %d Total (even if freed): %d System: %d",
m.Alloc, m.TotalAlloc, m.Sys)
}
func writeHeapProfile(filename string) {
f, err := os.Create(filename)
if err != nil {
logp.Err("Failed creating file %s: %s", filename, err)
return
}
pprof.WriteHeapProfile(f)
f.Close()
logp.Info("Created memory profile file %s.", filename)
}