mirror of
https://github.com/zrepl/zrepl.git
synced 2024-12-23 07:28:57 +01:00
10a14a8c50
package trace: - introduce the concept of tasks and spans, tracked as linked list within ctx - see package-level docs for an overview of the concepts - **main feature 1**: unique stack of task and span IDs - makes it easy to follow a series of log entries in concurrent code - **main feature 2**: ability to produce a chrome://tracing-compatible trace file - either via an env variable or a `zrepl pprof` subcommand - this is not a CPU profile, we already have go pprof for that - but it is very useful to visually inspect where the replication / snapshotter / pruner spends its time ( fixes #307 ) usage in package daemon/logging: - goal: every log entry should have a trace field with the ID stack from package trace - make `logging.GetLogger(ctx, Subsys)` the authoritative `logger.Logger` factory function - the context carries a linked list of injected fields which `logging.GetLogger` adds to the logger it returns - `logging.GetLogger` also uses package `trace` to get the task-and-span-stack and injects it into the returned logger's fields
95 lines
2.2 KiB
Go
95 lines
2.2 KiB
Go
package daemon
|
|
|
|
import (
|
|
"net/http"
|
|
// FIXME: importing this package has the side-effect of poisoning the http.DefaultServeMux
|
|
// FIXME: with the /debug/pprof endpoints
|
|
"context"
|
|
"net"
|
|
"net/http/pprof"
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
"golang.org/x/net/websocket"
|
|
|
|
"github.com/zrepl/zrepl/daemon/job"
|
|
"github.com/zrepl/zrepl/daemon/logging/trace"
|
|
)
|
|
|
|
type pprofServer struct {
|
|
cc chan PprofServerControlMsg
|
|
listener net.Listener
|
|
}
|
|
|
|
type PprofServerControlMsg struct {
|
|
// Whether the server should listen for requests on the given address
|
|
Run bool
|
|
// Must be set if Run is true, undefined otherwise
|
|
HttpListenAddress string
|
|
}
|
|
|
|
func NewPProfServer(ctx context.Context) *pprofServer {
|
|
|
|
s := &pprofServer{
|
|
cc: make(chan PprofServerControlMsg),
|
|
}
|
|
|
|
go s.controlLoop(ctx)
|
|
return s
|
|
}
|
|
|
|
func (s *pprofServer) controlLoop(ctx context.Context) {
|
|
outer:
|
|
for {
|
|
|
|
var msg PprofServerControlMsg
|
|
select {
|
|
case <-ctx.Done():
|
|
if s.listener != nil {
|
|
s.listener.Close()
|
|
}
|
|
break outer
|
|
case msg = <-s.cc:
|
|
// proceed
|
|
}
|
|
|
|
var err error
|
|
if msg.Run && s.listener == nil {
|
|
|
|
s.listener, err = net.Listen("tcp", msg.HttpListenAddress)
|
|
if err != nil {
|
|
s.listener = nil
|
|
continue
|
|
}
|
|
|
|
// FIXME: because net/http/pprof does not provide a mux,
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
|
|
mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
|
|
mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
|
|
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
|
|
mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
|
|
mux.Handle("/metrics", promhttp.Handler())
|
|
mux.Handle("/debug/zrepl/activity-trace", websocket.Handler(trace.ChrometraceClientWebsocketHandler))
|
|
go func() {
|
|
err := http.Serve(s.listener, mux)
|
|
if ctx.Err() != nil {
|
|
return
|
|
} else if err != nil {
|
|
job.GetLogger(ctx).WithError(err).Error("pprof server serve error")
|
|
}
|
|
}()
|
|
continue
|
|
}
|
|
|
|
if !msg.Run && s.listener != nil {
|
|
s.listener.Close()
|
|
s.listener = nil
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *pprofServer) Control(msg PprofServerControlMsg) {
|
|
s.cc <- msg
|
|
}
|