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