-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2277 from ipfs/feat/active-cmds
commands diagnostics
- Loading branch information
Showing
6 changed files
with
260 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
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() { | ||
log := r.log | ||
log.lock.Lock() | ||
defer log.lock.Unlock() | ||
|
||
r.Active = false | ||
r.EndTime = time.Now() | ||
r.log.maybeCleanup() | ||
|
||
// remove references to save memory | ||
r.req = nil | ||
r.log = nil | ||
|
||
} | ||
|
||
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() | ||
|
||
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 | ||
// drop all logs at are inactive and more than an hour old | ||
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] | ||
} | ||
} | ||
|
||
// Report generates a copy of all the entries in the requestlog | ||
func (rl *ReqLog) Report() []*ReqLogEntry { | ||
rl.lock.Lock() | ||
defer rl.lock.Unlock() | ||
out := make([]*ReqLogEntry, len(rl.Requests)) | ||
|
||
for i, e := range rl.Requests { | ||
out[i] = e.Copy() | ||
} | ||
|
||
return out | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package commands | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"sort" | ||
"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()) | ||
}, | ||
Options: []cmds.Option{ | ||
cmds.BoolOption("v", "verbose", "print more verbose output"), | ||
}, | ||
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) | ||
|
||
verbose, _, _ := res.Request().Option("v").Bool() | ||
|
||
w := tabwriter.NewWriter(buf, 4, 4, 2, ' ', 0) | ||
if verbose { | ||
fmt.Fprint(w, "ID\t") | ||
} | ||
fmt.Fprint(w, "Command\t") | ||
if verbose { | ||
fmt.Fprint(w, "Arguments\tOptions\t") | ||
} | ||
fmt.Fprintln(w, "Active\tStartTime\tRunTime") | ||
|
||
for _, req := range *out { | ||
if verbose { | ||
fmt.Fprintf(w, "%d\t", req.ID) | ||
} | ||
fmt.Fprintf(w, "%s\t", req.Command) | ||
if verbose { | ||
fmt.Fprintf(w, "%v\t[", req.Args) | ||
var keys []string | ||
for k, _ := range req.Options { | ||
keys = append(keys, k) | ||
} | ||
sort.Strings(keys) | ||
|
||
for _, k := range keys { | ||
fmt.Fprintf(w, "%s=%v,", k, req.Options[k]) | ||
} | ||
fmt.Fprintf(w, "]\t") | ||
} | ||
|
||
var live time.Duration | ||
if req.Active { | ||
live = time.Now().Sub(req.StartTime) | ||
} else { | ||
live = req.EndTime.Sub(req.StartTime) | ||
} | ||
t := req.StartTime.Format(time.Stamp) | ||
fmt.Fprintf(w, "%t\t%s\t%s\n", req.Active, t, live) | ||
} | ||
w.Flush() | ||
|
||
return buf, nil | ||
}, | ||
}, | ||
Type: []*cmds.ReqLogEntry{}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#!/bin/sh | ||
# | ||
# Copyright (c) 2016 Jeromy Johnson | ||
# MIT Licensed; see the LICENSE file in this repository. | ||
# | ||
|
||
test_description="Test active request commands" | ||
|
||
. lib/test-lib.sh | ||
|
||
test_init_ipfs | ||
test_launch_ipfs_daemon | ||
|
||
test_expect_success "command works" ' | ||
ipfs diag cmds > cmd_out | ||
' | ||
|
||
test_expect_success "invoc shows up in output" ' | ||
grep "diag/cmds" cmd_out > /dev/null | ||
' | ||
|
||
test_expect_success "start longer running command" ' | ||
ipfs log tail & | ||
LOGPID=$! | ||
' | ||
|
||
test_expect_success "long running command shows up" ' | ||
ipfs diag cmds > cmd_out2 | ||
' | ||
|
||
test_expect_success "output looks good" ' | ||
grep "log/tail" cmd_out2 | grep "true" > /dev/null | ||
' | ||
|
||
test_expect_success "kill log cmd" ' | ||
kill $LOGPID | ||
go-sleep 0.5s | ||
kill $LOGPID | ||
wait $LOGPID || true | ||
' | ||
|
||
test_expect_success "long running command inactive" ' | ||
ipfs diag cmds > cmd_out3 | ||
' | ||
|
||
test_expect_success "command shows up as inactive" ' | ||
grep "log/tail" cmd_out3 | grep "false" | ||
' | ||
|
||
test_kill_ipfs_daemon | ||
test_done |