Skip to content

Commit

Permalink
add command to view active api requests
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: Jeromy <jeromyj@gmail.com>
  • Loading branch information
whyrusleeping committed Feb 1, 2016
1 parent 62ba5eb commit 366d7db
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 5 deletions.
16 changes: 13 additions & 3 deletions commands/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
}
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
105 changes: 105 additions & 0 deletions commands/reqlog.go
Original file line number Diff line number Diff line change
@@ -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
}
1 change: 1 addition & 0 deletions commands/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
47 changes: 47 additions & 0 deletions core/commands/active.go
Original file line number Diff line number Diff line change
@@ -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{},
}
5 changes: 3 additions & 2 deletions core/commands/diag.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ var DiagCmd = &cmds.Command{
},

Subcommands: map[string]*cmds.Command{
"net": diagNetCmd,
"sys": sysDiagCmd,
"net": diagNetCmd,
"sys": sysDiagCmd,
"cmds": ActiveReqsCmd,
},
}

Expand Down

0 comments on commit 366d7db

Please sign in to comment.