Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#57 new cli flag save single snapshot to file #58

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a0734a1
chore (.gitignore): ignore .vscode folder and any *.code-workspace files
dsidirop Feb 6, 2022
979f846
clean (toputils.go): consolidate 1024-related constants used in Psize…
dsidirop Feb 6, 2022
c235b19
clean (toputils.go): neutral cleanups in Psize() to make it more read…
dsidirop Feb 7, 2022
4de3db5
feat (cli flags): add new flag '-b' which causes all traffic to be pr…
dsidirop Feb 7, 2022
9721b8f
Merge pull request #1 from dsidirop/ksidirop/NTOP-0053-flag-to-displa…
dsidirop Feb 7, 2022
1e0be58
clean (Psize unit tests): refactor the test harness to have it employ…
dsidirop Feb 7, 2022
d2e1879
Merge pull request #2 from dsidirop/ksidirop/NTOP-0053-flag-to-displa…
dsidirop Feb 7, 2022
2f22922
clean (Psize unit tests): refactor the test harness to have it employ…
dsidirop Feb 7, 2022
884c494
Merge branch 'master' of https://github.com/dsidirop/nats-top into ks…
dsidirop Feb 7, 2022
c0c8c1f
Merge pull request #3 from dsidirop/ksidirop/NTOP-0053-flag-to-displa…
dsidirop Feb 7, 2022
d7ef625
chore (.gitignore): ignore .vscode folder and any *.code-workspace files
ksidirop-laerdal Feb 8, 2022
64edada
feat (new cli flag): add new flag '-b' to print traffic in raw bytes
ksidirop-laerdal Feb 8, 2022
ed12dae
Merge pull request #4 from dsidirop/ksidirop/NTOP-0053-support-raw-by…
dsidirop Feb 8, 2022
398aee6
Merge branch 'main' of github.com:nats-io/nats-top
ksidirop-laerdal Feb 8, 2022
184d950
clean (StartUI): trivial neutral cleanups
ksidirop-laerdal Feb 8, 2022
9ca0311
refa (redraw mech): the redraw channel now records the reason behind …
ksidirop-laerdal Feb 8, 2022
9edf722
feat (cli -r <max>): new flag "-r <max>" to specify the maximum numbe…
ksidirop-laerdal Feb 8, 2022
03078ef
Merge pull request #6 from dsidirop/ksidirop/NTOP-0052-support-refres…
dsidirop Feb 8, 2022
6e6ce7b
feat (FetchStats()): extracted the fetching logic of MonitorStats() i…
ksidirop-laerdal Feb 8, 2022
6d6ea58
feat (MonitorStats()): simplify the method by having it invoke FetchS…
ksidirop-laerdal Feb 8, 2022
e6ce7c9
clean (FetchStats()): trivial simplification
ksidirop-laerdal Feb 8, 2022
4aa3ad6
feat (cli flag -o): new flag to save a snapshot of the output directl…
ksidirop-laerdal Feb 8, 2022
5bf03b9
Merge pull request #7 from dsidirop/ksidirop/NTOP-0057-new-cli-flag-s…
dsidirop Feb 8, 2022
7df6868
feat (cli flag '-o -'): passing '-o -' now prints the snapshot to the…
ksidirop-laerdal Feb 8, 2022
240cd40
Merge pull request #8 from dsidirop/ksidirop/NTOP-0057-new-cli-flag-s…
dsidirop Feb 8, 2022
01d723f
fix (fetchStats()): fixed a bug which was causing errors to not be re…
ksidirop-laerdal Feb 8, 2022
46448ba
fix (fetchStats()): improve the way we compare errors
ksidirop-laerdal Feb 8, 2022
4a962b0
Merge branch 'master' of github.com:dsidirop/nats-top into ksidirop/N…
ksidirop-laerdal Feb 9, 2022
12e9e9e
Merge branch 'main' of github.com:nats-io/nats-top into ksidirop/NTOP…
ksidirop-laerdal Feb 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions nats-top.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
delay = flag.Int("d", 1, "Refresh interval in seconds.")
sortBy = flag.String("sort", "cid", "Value for which to sort by the connections.")
lookupDNS = flag.Bool("lookup", false, "Enable client addresses DNS lookup.")
outputFile = flag.String("o", "", "Save the very first nats-top snapshot to the given file and exit. If '-' is passed then the snapshot is printed the standard output.")
showVersion = flag.Bool("v", false, "Show nats-top version.")
displayRawBytes = flag.Bool("b", false, "Display traffic in raw bytes.")
maxStatsRefreshes = flag.Int("r", -1, "Specifies the maximum number of times nats-top should refresh nats-stats before exiting.")
Expand Down Expand Up @@ -115,16 +116,43 @@ func main() {
}
engine.SortOpt = sortOpt

if *outputFile != "" {
saveStatsSnapshotToFile(engine, outputFile)
return
}

err = ui.Init()
if err != nil {
panic(err)
}
defer ui.Close()

go engine.MonitorStats()

StartUI(engine)
}

func saveStatsSnapshotToFile(engine *top.Engine, outputFile *string) {
stats := engine.FetchStatsSnapshot()
text := generateParagraph(engine, stats)

if *outputFile == "-" {
fmt.Print(text)
return
}

f, err := os.OpenFile(*outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
log.Fatalf("nats-top: failed to open output file '%s': %s\n", *outputFile, err)
}

if _, err = f.WriteString(text); err != nil {
log.Fatalf("nats-top: failed to write stats-snapshot to output file '%s': %s\n", *outputFile, err)
}

f.Close() //no point to error check process will exit anyway
}

// clearScreen tries to ensure resetting original state of screen
func clearScreen() {
fmt.Print("\033[2J\033[1;1H\033[?25l")
Expand Down
6 changes: 5 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ and releases of the binary are also [available](https://github.com/nats-io/nats-
## Usage

```
usage: nats-top [-s server] [-m http_port] [-ms https_port] [-n num_connections] [-d delay_secs] [-r max] [-sort by]
usage: nats-top [-s server] [-m http_port] [-ms https_port] [-n num_connections] [-d delay_secs] [-r max] [-o FILE] [-sort by]
[-cert FILE] [-key FILE ][-cacert FILE] [-k] [-b]
```

Expand All @@ -60,6 +60,10 @@ usage: nats-top [-s server] [-m http_port] [-ms https_port] [-n num_connections]

Specify the maximum number of times nats-top should refresh nats-stats before exiting (default: `0` which stands for `"no limit"`).

- `-o file`

Saves the very first nats-top snapshot to the given file and exits. If '-' is passed then the snapshot is printed to the standard output.

- `-sort by `

Field to use for sorting the connections.
Expand Down
165 changes: 89 additions & 76 deletions util/toputils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -90,8 +91,35 @@ func (engine *Engine) Request(path string) (interface{}, error) {
// MonitorStats is ran as a goroutine and takes options
// which can modify how poll values then sends to channel.
func (engine *Engine) MonitorStats() error {
var pollTime time.Time
delay := time.Duration(engine.Delay) * time.Second
isFirstTime := true
lastPollTime := time.Now()

for {
select {
case <-engine.ShutdownCh:
return nil
case <-time.After(delay):
stats, newLastPollTime := engine.fetchStats(isFirstTime, lastPollTime)
if stats != nil && errors.Is(stats.Error, errDud) {
isFirstTime = false
lastPollTime = newLastPollTime
}

engine.StatsCh <- stats
}
}
}

func (engine *Engine) FetchStatsSnapshot() *Stats {
stats, _ := engine.fetchStats(true, time.Now())

return stats
}

var errDud = fmt.Errorf("")

func (engine *Engine) fetchStats(isFirstTime bool, lastPollTime time.Time) (*Stats, time.Time) {
var inMsgsDelta int64
var outMsgsDelta int64
var inBytesDelta int64
Expand All @@ -107,89 +135,74 @@ func (engine *Engine) MonitorStats() error {
var inBytesRate float64
var outBytesRate float64

first := true
pollTime = time.Now()

delay := time.Duration(engine.Delay) * time.Second
stats := &Stats{
Varz: &server.Varz{},
Connz: &server.Connz{},
Rates: &Rates{},
Error: errDud,
}

for {
stats := &Stats{
Varz: &server.Varz{},
Connz: &server.Connz{},
Rates: &Rates{},
Error: fmt.Errorf(""),
// Get /varz
{
result, err := engine.Request("/varz")
if err != nil {
stats.Error = err
return stats, time.Time{}
}

select {
case <-engine.ShutdownCh:
return nil
case <-time.After(delay):
// Get /varz
{
result, err := engine.Request("/varz")
if err != nil {
stats.Error = err
engine.StatsCh <- stats
continue
}
if varz, ok := result.(*server.Varz); ok {
stats.Varz = varz
}
}
if varz, ok := result.(*server.Varz); ok {
stats.Varz = varz
}
}

// Get /connz
{
result, err := engine.Request("/connz")
if err != nil {
stats.Error = err
engine.StatsCh <- stats
continue
}
if connz, ok := result.(*server.Connz); ok {
stats.Connz = connz
}
}
// Get /connz
{
result, err := engine.Request("/connz")
if err != nil {
stats.Error = err
return stats, time.Time{}
}

// Periodic snapshot to get per sec metrics
inMsgsVal := stats.Varz.InMsgs
outMsgsVal := stats.Varz.OutMsgs
inBytesVal := stats.Varz.InBytes
outBytesVal := stats.Varz.OutBytes

inMsgsDelta = inMsgsVal - inMsgsLastVal
outMsgsDelta = outMsgsVal - outMsgsLastVal
inBytesDelta = inBytesVal - inBytesLastVal
outBytesDelta = outBytesVal - outBytesLastVal

inMsgsLastVal = inMsgsVal
outMsgsLastVal = outMsgsVal
inBytesLastVal = inBytesVal
outBytesLastVal = outBytesVal

now := time.Now()
tdelta := now.Sub(pollTime)
pollTime = now

// Calculate rates but the first time
if first {
first = false
} else {
inMsgsRate = float64(inMsgsDelta) / tdelta.Seconds()
outMsgsRate = float64(outMsgsDelta) / tdelta.Seconds()
inBytesRate = float64(inBytesDelta) / tdelta.Seconds()
outBytesRate = float64(outBytesDelta) / tdelta.Seconds()
}
if connz, ok := result.(*server.Connz); ok {
stats.Connz = connz
}
}

stats.Rates = &Rates{
InMsgsRate: inMsgsRate,
OutMsgsRate: outMsgsRate,
InBytesRate: inBytesRate,
OutBytesRate: outBytesRate,
}
// Periodic snapshot to get per sec metrics
inMsgsVal := stats.Varz.InMsgs
outMsgsVal := stats.Varz.OutMsgs
inBytesVal := stats.Varz.InBytes
outBytesVal := stats.Varz.OutBytes

inMsgsDelta = inMsgsVal - inMsgsLastVal
outMsgsDelta = outMsgsVal - outMsgsLastVal
inBytesDelta = inBytesVal - inBytesLastVal
outBytesDelta = outBytesVal - outBytesLastVal

inMsgsLastVal = inMsgsVal
outMsgsLastVal = outMsgsVal
inBytesLastVal = inBytesVal
outBytesLastVal = outBytesVal

now := time.Now()
tdelta := now.Sub(lastPollTime)

// Calculate rates but the first time
if !isFirstTime {
inMsgsRate = float64(inMsgsDelta) / tdelta.Seconds()
outMsgsRate = float64(outMsgsDelta) / tdelta.Seconds()
inBytesRate = float64(inBytesDelta) / tdelta.Seconds()
outBytesRate = float64(outBytesDelta) / tdelta.Seconds()
}

engine.StatsCh <- stats
}
stats.Rates = &Rates{
InMsgsRate: inMsgsRate,
OutMsgsRate: outMsgsRate,
InBytesRate: inBytesRate,
OutBytesRate: outBytesRate,
}

return stats, now
}

// SetupHTTPS sets up the http client and uri to use for polling.
Expand Down