log.go 3.3 KB
Newer Older
1 2 3 4
package commands

import (
	"fmt"
5 6
	"io"
	"strings"
7

8
	tail "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail"
9 10 11 12
	cmds "github.com/jbenet/go-ipfs/commands"
	u "github.com/jbenet/go-ipfs/util"
)

13 14 15 16 17 18
// Golang os.Args overrides * and replaces the character argument with
// an array which includes every file in the user's CWD. As a
// workaround, we use 'all' instead. The util library still uses * so
// we convert it at this step.
var logAllKeyword = "all"

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
19
var LogCmd = &cmds.Command{
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
	Helptext: cmds.HelpText{
		Tagline: "Interact with the daemon log output",
		ShortDescription: `
'ipfs log' contains utility commands to affect or read the logging
output of a running daemon.
`,
	},

	Subcommands: map[string]*cmds.Command{
		"level": logLevelCmd,
		"read":  logReadCmd,
	},
}

var logLevelCmd = &cmds.Command{
35 36 37
	Helptext: cmds.HelpText{
		Tagline: "Change the logging level",
		ShortDescription: `
38
'ipfs log level' is a utility command used to change the logging
39 40
output of a running daemon.
`,
41
	},
42

43
	Arguments: []cmds.Argument{
44 45 46
		// TODO use a different keyword for 'all' because all can theoretically
		// clash with a subsystem name
		cmds.StringArg("subsystem", true, false, fmt.Sprintf("the subsystem logging identifier. Use '%s' for all subsystems.", logAllKeyword)),
47
		cmds.StringArg("level", true, false, "one of: debug, info, notice, warning, error, critical"),
48
	},
49
	Run: func(req cmds.Request) (interface{}, error) {
50

51
		args := req.Arguments()
52
		subsystem, level := args[0], args[1]
53 54 55 56 57 58

		if subsystem == logAllKeyword {
			subsystem = "*"
		}

		if err := u.SetLogLevel(subsystem, level); err != nil {
59
			return nil, err
60 61
		}

62
		s := fmt.Sprintf("Changed log level of '%s' to '%s'", subsystem, level)
63
		log.Info(s)
64
		return &MessageOutput{s}, nil
65
	},
66 67
	Marshalers: cmds.MarshalerMap{
		cmds.Text: MessageTextMarshaler,
68
	},
69
	Type: MessageOutput{},
70
}
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131

var logReadCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Read the logs",
		ShortDescription: `
'ipfs log read' is a utility command used to read the log output.

By default, the last 50 lines are returned. For more, use '-n=<number of lines>'.
Use '--stream' (or '-s') to stream the current output (in addition to the '-n' lines).
`,
	},

	Run: func(req cmds.Request) (interface{}, error) {
		path := fmt.Sprintf("%s/logs/events.log", req.Context().ConfigRoot)

		outChan := make(chan interface{})

		go func() {
			defer close(outChan)

			t, err := tail.TailFile(path, tail.Config{
				Location:  &tail.SeekInfo{0, 2},
				Follow:    true,
				MustExist: true,
				Logger:    tail.DiscardingLogger,
			})
			if err != nil {
				fmt.Println(err.Error())
				return
			}

			for line := range t.Lines {
				if line.Err != nil {
					fmt.Println(err.Error())
					return
				}
				// TODO: unpack the line text into a struct and output that
				outChan <- &MessageOutput{line.Text}
			}
		}()

		return (<-chan interface{})(outChan), nil
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			outChan, ok := res.Output().(<-chan interface{})
			if !ok {
				return nil, u.ErrCast()
			}

			return &cmds.ChannelMarshaler{
				Channel: outChan,
				Marshaler: func(v interface{}) (io.Reader, error) {
					output := v.(*MessageOutput)
					return strings.NewReader(output.Message + "\n"), nil
				},
			}, nil
		},
	},
	Type: MessageOutput{},
}