stat.go 5.54 KB
Newer Older
Jeromy's avatar
Jeromy committed
1 2 3 4 5
package commands

import (
	"fmt"
	"io"
Jan Winkelmann's avatar
Jan Winkelmann committed
6
	"os"
Jeromy's avatar
Jeromy committed
7 8
	"time"

9 10
	cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"

Steven Allen's avatar
Steven Allen committed
11
	humanize "gx/ipfs/QmQMxG9D52TirZd9eLA37nxiNspnMRkKbyPWrVAa1gvtSy/go-humanize"
Steven Allen's avatar
Steven Allen committed
12
	cmds "gx/ipfs/QmQkW9fnCsg9SLHdViiAh6qfBppodsPZVpU92dZLqYtEfs/go-ipfs-cmds"
Hector Sanjuan's avatar
Hector Sanjuan committed
13 14
	metrics "gx/ipfs/QmSwVwKUWzdf3ppM3FbBbpuqHUNtUFJPQQdfvKmgZoz2gR/go-libp2p-metrics"
	peer "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer"
15
	protocol "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol"
16
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
Jeromy's avatar
Jeromy committed
17 18 19
)

var StatsCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
20
	Helptext: cmdkit.HelpText{
21
		Tagline: "Query IPFS statistics.",
22
		ShortDescription: `'ipfs stats' is a set of commands to help look at statistics
23
for your IPFS node.
24
`,
Jeromy's avatar
Jeromy committed
25
		LongDescription: `'ipfs stats' is a set of commands to help look at statistics
26
for your IPFS node.`,
Jeromy's avatar
Jeromy committed
27 28 29
	},

	Subcommands: map[string]*cmds.Command{
30 31 32
		"bw":      statBwCmd,
		"repo":    repoStatCmd,
		"bitswap": bitswapStatCmd,
Jeromy's avatar
Jeromy committed
33 34 35
	},
}

Kejie Zhang's avatar
Kejie Zhang committed
36 37 38 39 40 41 42
const (
	statPeerOptionName     = "peer"
	statProtoOptionName    = "proto"
	statPollOptionName     = "poll"
	statIntervalOptionName = "interval"
)

Jeromy's avatar
Jeromy committed
43
var statBwCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
44
	Helptext: cmdkit.HelpText{
Jeromy's avatar
Jeromy committed
45
		Tagline: "Print ipfs bandwidth information.",
46 47 48 49 50 51
		ShortDescription: `'ipfs stats bw' prints bandwidth information for the ipfs daemon.
It displays: TotalIn, TotalOut, RateIn, RateOut.
		`,
		LongDescription: `'ipfs stats bw' prints bandwidth information for the ipfs daemon.
It displays: TotalIn, TotalOut, RateIn, RateOut.

52 53 54 55 56
By default, overall bandwidth and all protocols are shown. To limit bandwidth
to a particular peer, use the 'peer' option along with that peer's multihash
id. To specify a specific protocol, use the 'proto' option. The 'peer' and
'proto' options cannot be specified simultaneously. The protocols that are
queried using this method are outlined in the specification:
57
https://github.com/libp2p/specs/blob/master/7-properties.md#757-protocol-multicodecs
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

Example protocol options:
  - /ipfs/id/1.0.0
  - /ipfs/bitswap
  - /ipfs/dht

Example:

    > ipfs stats bw -t /ipfs/bitswap
    Bandwidth
    TotalIn: 5.0MB
    TotalOut: 0B
    RateIn: 343B/s
    RateOut: 0B/s
    > ipfs stats bw -p QmepgFW7BHEtU4pZJdxaNiv75mKLLRQnPi1KaaXmQN4V1a
    Bandwidth
    TotalIn: 4.9MB
    TotalOut: 12MB
    RateIn: 0B/s
    RateOut: 0B/s
`,
Jeromy's avatar
Jeromy committed
79
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
80
	Options: []cmdkit.Option{
Kejie Zhang's avatar
Kejie Zhang committed
81 82 83 84
		cmdkit.StringOption(statPeerOptionName, "p", "Specify a peer to print bandwidth for."),
		cmdkit.StringOption(statProtoOptionName, "t", "Specify a protocol to print bandwidth for."),
		cmdkit.BoolOption(statPollOptionName, "Print bandwidth at an interval."),
		cmdkit.StringOption(statIntervalOptionName, "i", `Time interval to wait between updating output, if 'poll' is true.
85 86

    This accepts durations such as "300s", "1.5h" or "2h45m". Valid time units are:
87
    "ns", "us" (or "µs"), "ms", "s", "m", "h".`).WithDefault("1s"),
Jeromy's avatar
Jeromy committed
88 89
	},

keks's avatar
keks committed
90
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
91
		nd, err := cmdenv.GetNode(env)
Jeromy's avatar
Jeromy committed
92
		if err != nil {
keks's avatar
keks committed
93
			return err
Jeromy's avatar
Jeromy committed
94 95 96
		}

		// Must be online!
97
		if !nd.IsOnline {
keks's avatar
keks committed
98
			return cmdkit.Errorf(cmdkit.ErrClient, ErrNotOnline.Error())
Jeromy's avatar
Jeromy committed
99 100
		}

101
		if nd.Reporter == nil {
keks's avatar
keks committed
102
			return fmt.Errorf("bandwidth reporter disabled in config")
103 104
		}

Kejie Zhang's avatar
Kejie Zhang committed
105
		pstr, pfound := req.Options[statPeerOptionName].(string)
106
		tstr, tfound := req.Options["proto"].(string)
Jeromy's avatar
Jeromy committed
107
		if pfound && tfound {
keks's avatar
keks committed
108
			return cmdkit.Errorf(cmdkit.ErrClient, "please only specify peer OR protocol")
Jeromy's avatar
Jeromy committed
109 110 111 112 113 114
		}

		var pid peer.ID
		if pfound {
			checkpid, err := peer.IDB58Decode(pstr)
			if err != nil {
keks's avatar
keks committed
115
				return err
Jeromy's avatar
Jeromy committed
116 117 118 119
			}
			pid = checkpid
		}

Kejie Zhang's avatar
Kejie Zhang committed
120
		timeS, _ := req.Options[statIntervalOptionName].(string)
121 122
		interval, err := time.ParseDuration(timeS)
		if err != nil {
keks's avatar
keks committed
123
			return err
Jeromy's avatar
Jeromy committed
124 125
		}

Kejie Zhang's avatar
Kejie Zhang committed
126
		doPoll, _ := req.Options[statPollOptionName].(bool)
Jan Winkelmann's avatar
Jan Winkelmann committed
127 128 129
		for {
			if pfound {
				stats := nd.Reporter.GetBandwidthForPeer(pid)
130 131 132
				if err := res.Emit(&stats); err != nil {
					return err
				}
Jan Winkelmann's avatar
Jan Winkelmann committed
133 134 135
			} else if tfound {
				protoId := protocol.ID(tstr)
				stats := nd.Reporter.GetBandwidthForProtocol(protoId)
136 137 138
				if err := res.Emit(&stats); err != nil {
					return err
				}
Jan Winkelmann's avatar
Jan Winkelmann committed
139 140
			} else {
				totals := nd.Reporter.GetBandwidthTotals()
141 142 143
				if err := res.Emit(&totals); err != nil {
					return err
				}
Jeromy's avatar
Jeromy committed
144
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
145
			if !doPoll {
keks's avatar
keks committed
146
				return nil
Jan Winkelmann's avatar
Jan Winkelmann committed
147 148 149
			}
			select {
			case <-time.After(interval):
150
			case <-req.Context.Done():
151
				return req.Context.Err()
Jan Winkelmann's avatar
Jan Winkelmann committed
152 153
			}
		}
Jeromy's avatar
Jeromy committed
154 155
	},
	Type: metrics.Stats{},
Jan Winkelmann's avatar
Jan Winkelmann committed
156
	PostRun: cmds.PostRunMap{
keks's avatar
keks committed
157
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
Kejie Zhang's avatar
Kejie Zhang committed
158
			polling, _ := res.Request().Options[statPollOptionName].(bool)
keks's avatar
keks committed
159

keks's avatar
keks committed
160 161 162 163 164 165 166 167
			if polling {
				fmt.Fprintln(os.Stdout, "Total Up    Total Down  Rate Up     Rate Down")
			}
			for {
				v, err := res.Next()
				if err != nil {
					if err == io.EOF {
						return nil
Jeromy's avatar
Jeromy committed
168
					}
keks's avatar
keks committed
169 170
					return err
				}
Jeromy's avatar
Jeromy committed
171

keks's avatar
keks committed
172
				bs := v.(*metrics.Stats)
Jan Winkelmann's avatar
Jan Winkelmann committed
173

keks's avatar
keks committed
174 175 176
				if !polling {
					printStats(os.Stdout, bs)
					return nil
Jan Winkelmann's avatar
Jan Winkelmann committed
177
				}
Jeromy's avatar
Jeromy committed
178

keks's avatar
keks committed
179 180 181 182 183
				fmt.Fprintf(os.Stdout, "%8s    ", humanize.Bytes(uint64(bs.TotalOut)))
				fmt.Fprintf(os.Stdout, "%8s    ", humanize.Bytes(uint64(bs.TotalIn)))
				fmt.Fprintf(os.Stdout, "%8s/s  ", humanize.Bytes(uint64(bs.RateOut)))
				fmt.Fprintf(os.Stdout, "%8s/s      \r", humanize.Bytes(uint64(bs.RateIn)))
			}
Jeromy's avatar
Jeromy committed
184 185 186 187 188 189 190 191 192 193 194
		},
	},
}

func printStats(out io.Writer, bs *metrics.Stats) {
	fmt.Fprintln(out, "Bandwidth")
	fmt.Fprintf(out, "TotalIn: %s\n", humanize.Bytes(uint64(bs.TotalIn)))
	fmt.Fprintf(out, "TotalOut: %s\n", humanize.Bytes(uint64(bs.TotalOut)))
	fmt.Fprintf(out, "RateIn: %s/s\n", humanize.Bytes(uint64(bs.RateIn)))
	fmt.Fprintf(out, "RateOut: %s/s\n", humanize.Bytes(uint64(bs.RateOut)))
}