mirror of
https://github.com/ipfs/kubo.git
synced 2025-08-05 19:02:21 +08:00
add command to view active api requests
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
This commit is contained in:
@ -96,9 +96,16 @@ func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) http.Ha
|
||||
panic("must provide a valid ServerConfig")
|
||||
}
|
||||
|
||||
// setup request logger
|
||||
ctx.ReqLog = new(cmds.ReqLog)
|
||||
|
||||
// Wrap the internal handler with CORS handling-middleware.
|
||||
// Create a handler for the API.
|
||||
internal := internalHandler{ctx, root, cfg}
|
||||
internal := internalHandler{
|
||||
ctx: ctx,
|
||||
root: root,
|
||||
cfg: cfg,
|
||||
}
|
||||
c := cors.New(*cfg.cORSOpts)
|
||||
return &Handler{internal, c.Handler(internal)}
|
||||
}
|
||||
@ -158,6 +165,9 @@ func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
rlog := i.ctx.ReqLog.Add(req)
|
||||
defer rlog.Finish()
|
||||
|
||||
//ps: take note of the name clash - commands.Context != context.Context
|
||||
req.SetInvocContext(i.ctx)
|
||||
|
||||
@ -201,8 +211,8 @@ func guessMimeType(res cmds.Response) (string, error) {
|
||||
func sendResponse(w http.ResponseWriter, r *http.Request, res cmds.Response, req cmds.Request) {
|
||||
h := w.Header()
|
||||
// Expose our agent to allow identification
|
||||
h.Set("Server", "go-ipfs/" + config.CurrentVersionNumber)
|
||||
|
||||
h.Set("Server", "go-ipfs/"+config.CurrentVersionNumber)
|
||||
|
||||
mime, err := guessMimeType(res)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
105
commands/reqlog.go
Normal file
105
commands/reqlog.go
Normal file
@ -0,0 +1,105 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ReqLogEntry struct {
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
Active bool
|
||||
Command string
|
||||
Options map[string]interface{}
|
||||
Args []string
|
||||
ID int
|
||||
|
||||
req Request
|
||||
log *ReqLog
|
||||
}
|
||||
|
||||
func (r *ReqLogEntry) Finish() {
|
||||
r.log.lock.Lock()
|
||||
defer r.log.lock.Unlock()
|
||||
|
||||
r.Active = false
|
||||
r.EndTime = time.Now()
|
||||
|
||||
r.log.maybeCleanup()
|
||||
}
|
||||
|
||||
func (r *ReqLogEntry) Copy() *ReqLogEntry {
|
||||
out := *r
|
||||
out.log = nil
|
||||
return &out
|
||||
}
|
||||
|
||||
type ReqLog struct {
|
||||
Requests []*ReqLogEntry
|
||||
nextID int
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (rl *ReqLog) Add(req Request) *ReqLogEntry {
|
||||
rl.lock.Lock()
|
||||
defer rl.lock.Unlock()
|
||||
|
||||
log.Error("ADD: ", req)
|
||||
rle := &ReqLogEntry{
|
||||
StartTime: time.Now(),
|
||||
Active: true,
|
||||
Command: strings.Join(req.Path(), "/"),
|
||||
Options: req.Options(),
|
||||
Args: req.Arguments(),
|
||||
ID: rl.nextID,
|
||||
req: req,
|
||||
log: rl,
|
||||
}
|
||||
|
||||
rl.nextID++
|
||||
rl.Requests = append(rl.Requests, rle)
|
||||
return rle
|
||||
}
|
||||
|
||||
func (rl *ReqLog) maybeCleanup() {
|
||||
// only do it every so often or it might
|
||||
// become a perf issue
|
||||
if len(rl.Requests) == 0 {
|
||||
rl.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *ReqLog) cleanup() {
|
||||
var i int
|
||||
for ; i < len(rl.Requests); i++ {
|
||||
req := rl.Requests[i]
|
||||
if req.Active || req.EndTime.Add(time.Hour).After(time.Now()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
var j int
|
||||
for i < len(rl.Requests) {
|
||||
rl.Requests[j] = rl.Requests[i]
|
||||
j++
|
||||
i++
|
||||
}
|
||||
rl.Requests = rl.Requests[:len(rl.Requests)-i]
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *ReqLog) Report() []*ReqLogEntry {
|
||||
rl.lock.Lock()
|
||||
defer rl.lock.Unlock()
|
||||
out := make([]*ReqLogEntry, len(rl.Requests))
|
||||
|
||||
log.Error("REPORT: ", rl.Requests)
|
||||
|
||||
for i, e := range rl.Requests {
|
||||
out[i] = e.Copy()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
@ -21,6 +21,7 @@ type OptMap map[string]interface{}
|
||||
type Context struct {
|
||||
Online bool
|
||||
ConfigRoot string
|
||||
ReqLog *ReqLog
|
||||
|
||||
config *config.Config
|
||||
LoadConfig func(path string) (*config.Config, error)
|
||||
|
47
core/commands/active.go
Normal file
47
core/commands/active.go
Normal file
@ -0,0 +1,47 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
cmds "github.com/ipfs/go-ipfs/commands"
|
||||
)
|
||||
|
||||
var ActiveReqsCmd = &cmds.Command{
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "List commands run on this ipfs node",
|
||||
ShortDescription: `
|
||||
Lists running and recently run commands.
|
||||
`,
|
||||
},
|
||||
Run: func(req cmds.Request, res cmds.Response) {
|
||||
res.SetOutput(req.InvocContext().ReqLog.Report())
|
||||
},
|
||||
Marshalers: map[cmds.EncodingType]cmds.Marshaler{
|
||||
cmds.Text: func(res cmds.Response) (io.Reader, error) {
|
||||
out, ok := res.Output().(*[]*cmds.ReqLogEntry)
|
||||
if !ok {
|
||||
log.Errorf("%#v", res.Output())
|
||||
return nil, cmds.ErrIncorrectType
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
w := tabwriter.NewWriter(buf, 4, 4, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "Command\tActive\tStartTime\tRunTime")
|
||||
for _, req := range *out {
|
||||
if req.Active {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", req.Command, "true", req.StartTime, time.Now().Sub(req.StartTime))
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", req.Command, "false", req.StartTime, req.EndTime.Sub(req.StartTime))
|
||||
}
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
return buf, nil
|
||||
},
|
||||
},
|
||||
Type: []*cmds.ReqLogEntry{},
|
||||
}
|
@ -45,8 +45,9 @@ var DiagCmd = &cmds.Command{
|
||||
},
|
||||
|
||||
Subcommands: map[string]*cmds.Command{
|
||||
"net": diagNetCmd,
|
||||
"sys": sysDiagCmd,
|
||||
"net": diagNetCmd,
|
||||
"sys": sysDiagCmd,
|
||||
"cmds": ActiveReqsCmd,
|
||||
},
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user