@cryptotaxi247 / kubo / commits / 366d7db3d

add command to view active api requests

License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>

Jeromy committed Feb 1, 2016 at 15:44 UTC 366d7db3d3334aba490c7d5ce66e251a9088d5f0
5 files changed +169 -5
commands/http/handler.go
+13 -3
@@ -96,9 +96,16 @@ func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) http.Ha
96 panic("must provide a valid ServerConfig")
97 }
98
99 + // setup request logger
100 + ctx.ReqLog = new(cmds.ReqLog)
101 +
102 // Wrap the internal handler with CORS handling-middleware.
103 // Create a handler for the API.
101 - internal := internalHandler{ctx, root, cfg}
104 + internal := internalHandler{
105 + ctx: ctx,
106 + root: root,
107 + cfg: cfg,
108 + }
109 c := cors.New(*cfg.cORSOpts)
110 return &Handler{internal, c.Handler(internal)}
111 }
@@ -158,6 +165,9 @@ func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
165 return
166 }
167
168 + rlog := i.ctx.ReqLog.Add(req)
169 + defer rlog.Finish()
170 +
171 //ps: take note of the name clash - commands.Context != context.Context
172 req.SetInvocContext(i.ctx)
173
@@ -201,8 +211,8 @@ func guessMimeType(res cmds.Response) (string, error) {
211 func sendResponse(w http.ResponseWriter, r *http.Request, res cmds.Response, req cmds.Request) {
212 h := w.Header()
213 // Expose our agent to allow identification
204 - h.Set("Server", "go-ipfs/" + config.CurrentVersionNumber)
205 -
214 + h.Set("Server", "go-ipfs/"+config.CurrentVersionNumber)
215 +
216 mime, err := guessMimeType(res)
217 if err != nil {
218 http.Error(w, err.Error(), http.StatusInternalServerError)
commands/reqlog.go new
+105
@@ -0,0 +1,105 @@
1 +package commands
2 +
3 +import (
4 + "strings"
5 + "sync"
6 + "time"
7 +)
8 +
9 +type ReqLogEntry struct {
10 + StartTime time.Time
11 + EndTime time.Time
12 + Active bool
13 + Command string
14 + Options map[string]interface{}
15 + Args []string
16 + ID int
17 +
18 + req Request
19 + log *ReqLog
20 +}
21 +
22 +func (r *ReqLogEntry) Finish() {
23 + r.log.lock.Lock()
24 + defer r.log.lock.Unlock()
25 +
26 + r.Active = false
27 + r.EndTime = time.Now()
28 +
29 + r.log.maybeCleanup()
30 +}
31 +
32 +func (r *ReqLogEntry) Copy() *ReqLogEntry {
33 + out := *r
34 + out.log = nil
35 + return &out
36 +}
37 +
38 +type ReqLog struct {
39 + Requests []*ReqLogEntry
40 + nextID int
41 + lock sync.Mutex
42 +}
43 +
44 +func (rl *ReqLog) Add(req Request) *ReqLogEntry {
45 + rl.lock.Lock()
46 + defer rl.lock.Unlock()
47 +
48 + log.Error("ADD: ", req)
49 + rle := &ReqLogEntry{
50 + StartTime: time.Now(),
51 + Active: true,
52 + Command: strings.Join(req.Path(), "/"),
53 + Options: req.Options(),
54 + Args: req.Arguments(),
55 + ID: rl.nextID,
56 + req: req,
57 + log: rl,
58 + }
59 +
60 + rl.nextID++
61 + rl.Requests = append(rl.Requests, rle)
62 + return rle
63 +}
64 +
65 +func (rl *ReqLog) maybeCleanup() {
66 + // only do it every so often or it might
67 + // become a perf issue
68 + if len(rl.Requests) == 0 {
69 + rl.cleanup()
70 + }
71 +}
72 +
73 +func (rl *ReqLog) cleanup() {
74 + var i int
75 + for ; i < len(rl.Requests); i++ {
76 + req := rl.Requests[i]
77 + if req.Active || req.EndTime.Add(time.Hour).After(time.Now()) {
78 + break
79 + }
80 + }
81 +
82 + if i > 0 {
83 + var j int
84 + for i < len(rl.Requests) {
85 + rl.Requests[j] = rl.Requests[i]
86 + j++
87 + i++
88 + }
89 + rl.Requests = rl.Requests[:len(rl.Requests)-i]
90 + }
91 +}
92 +
93 +func (rl *ReqLog) Report() []*ReqLogEntry {
94 + rl.lock.Lock()
95 + defer rl.lock.Unlock()
96 + out := make([]*ReqLogEntry, len(rl.Requests))
97 +
98 + log.Error("REPORT: ", rl.Requests)
99 +
100 + for i, e := range rl.Requests {
101 + out[i] = e.Copy()
102 + }
103 +
104 + return out
105 +}
commands/request.go
+1
@@ -21,6 +21,7 @@ type OptMap map[string]interface{}
21 type Context struct {
22 Online bool
23 ConfigRoot string
24 + ReqLog *ReqLog
25
26 config *config.Config
27 LoadConfig func(path string) (*config.Config, error)
core/commands/active.go new
+47
@@ -0,0 +1,47 @@
1 +package commands
2 +
3 +import (
4 + "bytes"
5 + "fmt"
6 + "io"
7 + "text/tabwriter"
8 + "time"
9 +
10 + cmds "github.com/ipfs/go-ipfs/commands"
11 +)
12 +
13 +var ActiveReqsCmd = &cmds.Command{
14 + Helptext: cmds.HelpText{
15 + Tagline: "List commands run on this ipfs node",
16 + ShortDescription: `
17 +Lists running and recently run commands.
18 +`,
19 + },
20 + Run: func(req cmds.Request, res cmds.Response) {
21 + res.SetOutput(req.InvocContext().ReqLog.Report())
22 + },
23 + Marshalers: map[cmds.EncodingType]cmds.Marshaler{
24 + cmds.Text: func(res cmds.Response) (io.Reader, error) {
25 + out, ok := res.Output().(*[]*cmds.ReqLogEntry)
26 + if !ok {
27 + log.Errorf("%#v", res.Output())
28 + return nil, cmds.ErrIncorrectType
29 + }
30 + buf := new(bytes.Buffer)
31 +
32 + w := tabwriter.NewWriter(buf, 4, 4, 2, ' ', 0)
33 + fmt.Fprintln(w, "Command\tActive\tStartTime\tRunTime")
34 + for _, req := range *out {
35 + if req.Active {
36 + fmt.Fprintf(w, "%s\t%s\t%s\n", req.Command, "true", req.StartTime, time.Now().Sub(req.StartTime))
37 + } else {
38 + fmt.Fprintf(w, "%s\t%s\t%s\n", req.Command, "false", req.StartTime, req.EndTime.Sub(req.StartTime))
39 + }
40 + }
41 + w.Flush()
42 +
43 + return buf, nil
44 + },
45 + },
46 + Type: []*cmds.ReqLogEntry{},
47 +}
core/commands/diag.go
+3 -2
@@ -45,8 +45,9 @@ var DiagCmd = &cmds.Command{
45 },
46
47 Subcommands: map[string]*cmds.Command{
48 - "net": diagNetCmd,
49 - "sys": sysDiagCmd,
48 + "net": diagNetCmd,
49 + "sys": sysDiagCmd,
50 + "cmds": ActiveReqsCmd,
51 },
52 }
53