-
Notifications
You must be signed in to change notification settings - Fork 21
PMM-5680 store logs #342
base: main
Are you sure you want to change the base?
PMM-5680 store logs #342
Changes from 57 commits
167cbf3
29764cc
c8d2f68
b673eaa
9eda60e
a6faa0b
6f61509
20a3ee2
229845d
9161345
aa77af2
c6281e4
5163ca9
e35cbc4
89a98da
989bb4f
9169b92
8562fbc
f0b2d6a
7a4863a
c20a64c
f2d3a40
50ebcaf
a0a6d44
a4bffaf
fb64846
fdc9f1a
60dc79d
8312f49
3daddce
dfc1597
cd38bd3
9ff8dc0
221a0b9
729e3f1
b24f6b5
cb9c6b1
7bd784b
b873ecc
c53f291
847932f
77c8c83
c686b5e
c0bb005
a709904
e1da1ac
5202e83
e44958a
1657e70
c18be41
1d77203
c4151d3
858e68c
611c10e
49cc0e6
60a0db6
ed5a7ce
9d688bb
545883a
96f10b0
9880641
60e10a4
a0b10af
729bc50
ecbac3d
592965b
f379d59
29028f2
f43cd91
796c46d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,10 +16,13 @@ | |
package agentlocal | ||
|
||
import ( | ||
"archive/zip" | ||
"bytes" | ||
"context" | ||
_ "expvar" // register /debug/vars | ||
"fmt" | ||
"html/template" | ||
"io" | ||
"log" | ||
"net" | ||
"net/http" | ||
|
@@ -47,6 +50,7 @@ import ( | |
"google.golang.org/protobuf/types/known/durationpb" | ||
|
||
"github.com/percona/pmm-agent/config" | ||
"github.com/percona/pmm-agent/storelogs" | ||
) | ||
|
||
const ( | ||
|
@@ -61,23 +65,28 @@ type Server struct { | |
configFilepath string | ||
|
||
l *logrus.Entry | ||
ringLogs *storelogs.LogsStore | ||
reload chan struct{} | ||
reloadCloseOnce sync.Once | ||
|
||
agentlocalpb.UnimplementedAgentLocalServer | ||
} | ||
|
||
// NewServer creates new server. | ||
// | ||
//` | ||
// Caller should call Run. | ||
func NewServer(cfg *config.Config, supervisor supervisor, client client, configFilepath string) *Server { | ||
func NewServer(cfg *config.Config, supervisor supervisor, client client, configFilepath string, ringLog *storelogs.LogsStore) *Server { | ||
logger := logrus.New() | ||
logger.Out = io.MultiWriter(os.Stderr, ringLog) | ||
|
||
return &Server{ | ||
cfg: cfg, | ||
supervisor: supervisor, | ||
client: client, | ||
configFilepath: configFilepath, | ||
l: logrus.WithField("component", "local-server"), | ||
l: logger.WithField("component", "local-server"), | ||
reload: make(chan struct{}), | ||
ringLogs: ringLog, | ||
} | ||
} | ||
|
||
|
@@ -289,6 +298,43 @@ func (s *Server) runJSONServer(ctx context.Context, grpcAddress string) { | |
mux.Handle("/debug/", http.DefaultServeMux) | ||
mux.Handle("/debug", debugPageHandler) | ||
mux.Handle("/", proxyMux) | ||
mux.HandleFunc("/logs.zip", func(w http.ResponseWriter, r *http.Request) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add a test for logs.zip? |
||
buf := &bytes.Buffer{} | ||
writer := zip.NewWriter(buf) | ||
b := &bytes.Buffer{} | ||
for _, serverLog := range s.ringLogs.GetLogs() { | ||
_, err := b.WriteString(serverLog) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
addData(writer, "server.txt", b.Bytes()) | ||
|
||
for id, logs := range s.supervisor.AgentsLogs() { | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
b := &bytes.Buffer{} | ||
for _, l := range logs { | ||
_, err := b.WriteString(l + "\n") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
addData(writer, fmt.Sprintf("%s.txt", id), b.Bytes()) | ||
} | ||
err = writer.Close() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
w.Header().Set("Content-Type", "application/zip") | ||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", "logs")) | ||
// io.Copy(w, buf) | ||
_, err = w.Write(buf.Bytes()) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
}) | ||
|
||
server := &http.Server{ | ||
Addr: address, | ||
|
@@ -314,6 +360,18 @@ func (s *Server) runJSONServer(ctx context.Context, grpcAddress string) { | |
_ = server.Close() // call Close() in all cases | ||
} | ||
|
||
// addData add data to zip file | ||
func addData(zipW *zip.Writer, name string, data []byte) { | ||
f, err := zipW.Create(name) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
_, err = f.Write(data) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
// check interfaces | ||
var ( | ||
_ agentlocalpb.AgentLocalServer = (*Server)(nil) | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,8 @@ package supervisor | |
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"runtime/pprof" | ||
|
@@ -43,6 +45,7 @@ import ( | |
"github.com/percona/pmm-agent/agents/postgres/pgstatstatements" | ||
"github.com/percona/pmm-agent/agents/process" | ||
"github.com/percona/pmm-agent/config" | ||
"github.com/percona/pmm-agent/storelogs" | ||
"github.com/percona/pmm-agent/utils/templates" | ||
) | ||
|
||
|
@@ -70,6 +73,7 @@ type agentProcessInfo struct { | |
done <-chan struct{} // closes when Process.Changes() channel closes | ||
requestedState *agentpb.SetStateRequest_AgentProcess | ||
listenPort uint16 | ||
logs *storelogs.LogsStore // store logs | ||
} | ||
|
||
// builtinAgentInfo describes built-in Agent. | ||
|
@@ -79,6 +83,7 @@ type builtinAgentInfo struct { | |
requestedState *agentpb.SetStateRequest_BuiltinAgent | ||
describe func(chan<- *prometheus.Desc) // agent's func to describe Prometheus metrics | ||
collect func(chan<- prometheus.Metric) // agent's func to provide Prometheus metrics | ||
logs *storelogs.LogsStore // store logs | ||
} | ||
|
||
// NewSupervisor creates new Supervisor object. | ||
|
@@ -141,6 +146,25 @@ func (s *Supervisor) AgentsList() []*agentlocalpb.AgentInfo { | |
return res | ||
} | ||
|
||
// AgentsLogs returns logs for all Agents managed by this supervisor. | ||
func (s *Supervisor) AgentsLogs() map[string][]string { | ||
s.rw.RLock() | ||
defer s.rw.RUnlock() | ||
s.arw.RLock() | ||
defer s.arw.RUnlock() | ||
|
||
var res map[string][]string | ||
|
||
for _, agent := range s.agentProcesses { | ||
res[agent.requestedState.Type.String()] = agent.logs.GetLogs() | ||
} | ||
|
||
for _, agent := range s.builtinAgents { | ||
res[agent.requestedState.Type.String()] = agent.logs.GetLogs() | ||
} | ||
return res | ||
} | ||
|
||
// Changes returns channel with Agent's state changes. | ||
func (s *Supervisor) Changes() <-chan *agentpb.StateChangedRequest { | ||
return s.changes | ||
|
@@ -325,8 +349,9 @@ func filter(existing, new map[string]agentpb.AgentParams) (toStart, toRestart, t | |
|
||
//nolint:golint | ||
const ( | ||
type_TEST_SLEEP inventorypb.AgentType = 998 // process | ||
type_TEST_NOOP inventorypb.AgentType = 999 // built-in | ||
type_TEST_SLEEP inventorypb.AgentType = 998 // process | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [golangci-lint] reported by reviewdog 🐶 |
||
type_TEST_NOOP inventorypb.AgentType = 999 // built-in | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [golangci-lint] reported by reviewdog 🐶 |
||
COUNT_AGENT_LOGS = 10 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [golangci-lint] reported by reviewdog 🐶 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's use camelCase. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's increase the value |
||
) | ||
|
||
// startProcess starts Agent's process. | ||
|
@@ -339,13 +364,8 @@ func (s *Supervisor) startProcess(agentID string, agentProcess *agentpb.SetState | |
|
||
ctx, cancel := context.WithCancel(s.ctx) | ||
agentType := strings.ToLower(agentProcess.Type.String()) | ||
l := logrus.WithFields(logrus.Fields{ | ||
"component": "agent-process", | ||
"agentID": agentID, | ||
"type": agentType, | ||
}) | ||
ringLog, l := s.newLogger("agent-process", agentID, agentType) | ||
l.Debugf("Starting: %s.", processParams) | ||
|
||
process := process.New(processParams, agentProcess.RedactWords, l) | ||
go pprof.Do(ctx, pprof.Labels("agentID", agentID, "type", agentType), process.Run) | ||
|
||
|
@@ -368,20 +388,30 @@ func (s *Supervisor) startProcess(agentID string, agentProcess *agentpb.SetState | |
done: done, | ||
requestedState: proto.Clone(agentProcess).(*agentpb.SetStateRequest_AgentProcess), | ||
listenPort: port, | ||
logs: ringLog, | ||
} | ||
return nil | ||
} | ||
|
||
func (s *Supervisor) newLogger(component string, agentID string, agentType string) (*storelogs.LogsStore, *logrus.Entry) { | ||
ringLog := storelogs.New(COUNT_AGENT_LOGS) | ||
logger := logrus.New() | ||
logger.SetFormatter(logrus.StandardLogger().Formatter) | ||
logger.Out = io.MultiWriter(os.Stderr, ringLog) | ||
l := logger.WithFields(logrus.Fields{ | ||
"component": component, | ||
"agentID": agentID, | ||
"type": agentType, | ||
}) | ||
return ringLog, l | ||
} | ||
|
||
// startBuiltin starts built-in Agent. | ||
// Must be called with s.rw held for writing. | ||
func (s *Supervisor) startBuiltin(agentID string, builtinAgent *agentpb.SetStateRequest_BuiltinAgent) error { | ||
ctx, cancel := context.WithCancel(s.ctx) | ||
agentType := strings.ToLower(builtinAgent.Type.String()) | ||
l := logrus.WithFields(logrus.Fields{ | ||
"component": "agent-builtin", | ||
"agentID": agentID, | ||
"type": agentType, | ||
}) | ||
ringLog, l := s.newLogger("agent-process", agentID, agentType) | ||
|
||
done := make(chan struct{}) | ||
var agent agents.BuiltinAgent | ||
|
@@ -486,6 +516,7 @@ func (s *Supervisor) startBuiltin(agentID string, builtinAgent *agentpb.SetState | |
requestedState: proto.Clone(builtinAgent).(*agentpb.SetStateRequest_BuiltinAgent), | ||
describe: agent.Describe, | ||
collect: agent.Collect, | ||
logs: ringLog, | ||
} | ||
return nil | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,9 +28,12 @@ import ( | |
"github.com/percona/pmm-agent/client" | ||
"github.com/percona/pmm-agent/config" | ||
"github.com/percona/pmm-agent/connectionchecker" | ||
"github.com/percona/pmm-agent/storelogs" | ||
"github.com/percona/pmm-agent/versioner" | ||
) | ||
|
||
const COUNT_SERVER_LOGS = 500 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [golangci-lint] reported by reviewdog 🐶 |
||
|
||
// Run implements `pmm-agent run` default command. | ||
func Run() { | ||
l := logrus.WithField("component", "main") | ||
|
@@ -40,6 +43,7 @@ func Run() { | |
// handle termination signals | ||
signals := make(chan os.Signal, 1) | ||
signal.Notify(signals, unix.SIGTERM, unix.SIGINT) | ||
ringLog := storelogs.New(COUNT_SERVER_LOGS) | ||
go func() { | ||
s := <-signals | ||
signal.Stop(signals) | ||
|
@@ -55,7 +59,7 @@ func Run() { | |
config.ConfigureLogger(cfg) | ||
l.Debugf("Loaded configuration: %+v", cfg) | ||
|
||
run(ctx, cfg, configFilepath) | ||
run(ctx, cfg, configFilepath, ringLog) | ||
|
||
if ctx.Err() != nil { | ||
return | ||
|
@@ -65,7 +69,7 @@ func Run() { | |
|
||
// run runs all pmm-agent components with given configuration until ctx is cancellled. | ||
// See documentation for NewXXX, Run, and Done | ||
func run(ctx context.Context, cfg *config.Config, configFilepath string) { | ||
func run(ctx context.Context, cfg *config.Config, configFilepath string, ringLog *storelogs.LogsStore) { | ||
var cancel context.CancelFunc | ||
ctx, cancel = context.WithCancel(ctx) | ||
|
||
|
@@ -77,7 +81,8 @@ func run(ctx context.Context, cfg *config.Config, configFilepath string) { | |
connectionChecker := connectionchecker.New(&cfg.Paths) | ||
v := versioner.New(&versioner.RealExecFunctions{}) | ||
client := client.New(cfg, supervisor, connectionChecker, v) | ||
localServer := agentlocal.NewServer(cfg, supervisor, client, configFilepath) | ||
|
||
localServer := agentlocal.NewServer(cfg, supervisor, client, configFilepath, ringLog) | ||
|
||
go func() { | ||
_ = client.Run(ctx) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's revert