perfmonger 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fec4f6034a16fc8915547917510cb8c69299b095
4
- data.tar.gz: 6eb9b12b445f78adf0709d298ce71279abff2c8a
3
+ metadata.gz: 8e47b0d30a3300a01ae5efcc0bbbe10baf524561
4
+ data.tar.gz: 88988c5009fcbe62049c7355f3e8374b3c825f5d
5
5
  SHA512:
6
- metadata.gz: 101f22ee04ff2cdee38ddcae194970ce5af08c70a4850c295e38ecece1c14648bc1953b35558a9735f642423f03cac583b868010a9f35d8fd9a629d0ea9b1f5a
7
- data.tar.gz: 31321ba34ecc71ce31668f13e32d58f2d82fd2808dfbabb3c3d869ae78e70fba46020e3dbfe2779e8a2cd202e2c43e392ee1266a4a94db748b726949d7d8a34d
6
+ metadata.gz: 6f767fae2183025478897926d3b79d705fd30937f8c7f7cd6221dd29ad0d9ae03f7940c29be234613f8100f1c9dafb3e8d079075ab2d2d1879b2de33fdc2e482
7
+ data.tar.gz: e4f1d714cd58d57db382320224d356f919692f25a4552d5649accf7a27c63cac693049972dd994904cdb4d4a54c9f2c19e374b25d928f392e4bffbdb517d5460
data/.gitignore CHANGED
@@ -11,3 +11,6 @@
11
11
  /cpu.dat
12
12
  /disk.dat
13
13
  /perfmonger.pgr
14
+ /.wercker/
15
+ /spec/examples.txt
16
+ /perfmonger.pgr.gz
data/.travis.yml CHANGED
@@ -1,17 +1,18 @@
1
-
2
1
  language: ruby
3
2
  install:
4
3
  - sudo apt-get update
5
4
  - sudo apt-get install gnuplot
6
5
  - gnuplot -e "set terminal" < /dev/null 2>&1
7
6
  - bundle install
8
- - go_version="1.4.2"
7
+ - go_version="1.8.3"
9
8
  - wget http://golang.org/dl/go${go_version}.linux-amd64.tar.gz
10
9
  - sudo tar -C /usr/local -xzf go${go_version}.linux-amd64.tar.gz
11
10
  - export PATH=$PATH:/usr/local/go/bin
12
11
  - export GOPATH="$HOME/go"
13
12
  - export PATH="$PATH:$GOPATH/bin"
14
13
  - mkdir -p "$HOME/go/{src,pkg,bin}"
14
+ - go get -u github.com/mattn/go-isatty
15
+ - go get -u github.com/hayamiz/go-projson
15
16
  - go get -u golang.org/x/crypto/ssh/terminal
16
17
  - go get -u github.com/hayamiz/perfmonger/core/subsystem
17
18
  rvm:
@@ -22,3 +23,6 @@ rvm:
22
23
  script:
23
24
  - rake spec
24
25
  - rake test_core
26
+ notifications:
27
+ slack:
28
+ secure: fH8tRyxWHL60OV6QuJlzig9lCLbjfpHx8E6D2EzgQz7+/wqAxtoTUyiN7mbpEJa4hyQeZfpmMpDTnl2tHD6eI8yqjAsY4Q+jt21tCKyrKMegq9Pypd4eMP4o+DupT2mXm0K3cZ2Kgb+yP8AuJPoTy20j3kpmnDFWdqRnhgpBLC8=
data/HOWTO.md CHANGED
@@ -3,7 +3,6 @@
3
3
  ## TODO in release
4
4
 
5
5
  1. Make a commit for changing version number
6
- * Update version number in configure.ac
7
6
  * Add a new version number and release date to NEWS
8
7
  2. Tag the commit with name `v<VERSION>` ex) `v0.3.0`
9
8
  3. Push tags to the github repository
data/NEWS CHANGED
@@ -1,10 +1,19 @@
1
- ## 2017-09-14: PerfMonger 0.10.2
1
+ ## 2018-05-15: PerfMonger 0.11.0
2
+ * New features
3
+ * [play] subcommand:
4
+ * Add --color, --pretty option for pretty JSON output
5
+ * [live] subcommand:
6
+ * Add --color, --pretty option for pretty JSON output
7
+ * [fingerprint] subcommand:
8
+ * Collect additional info: numactl, ec2-metadata
2
9
  * Bug fixes
3
10
  * [plot] subcommand:
4
- * Delete temporary files after plot
5
-
6
- ## 2017-09-08: PerfMonger 0.10.1
7
- * Yanked wrongly shipped v0.10.0 and pushed up-to-date code
11
+ * Correctly filter out disk usages by --disk-only option
12
+ * Changes
13
+ * Dropped support of i386
14
+ * Use go-projson for JSON output
15
+ * [plot] subcommand:
16
+ * Stacked graphs layout in allcpu.pdf
8
17
 
9
18
  ## 2017-06-19: PerfMonger 0.10.0
10
19
  * New features
@@ -204,4 +213,4 @@ PerfMonger is available on yum repository from this release.
204
213
  ## 2011-12-09: PerfMonger 0.1.0 released
205
214
 
206
215
  * New features
207
- * I/O performance monitoring
216
+ * I/O performance monitoring
data/README.md CHANGED
@@ -5,31 +5,97 @@
5
5
 
6
6
  [![wercker status](https://app.wercker.com/status/44c3ade6a2406d337df6d93097a52fdf/m "wercker status")](https://app.wercker.com/project/bykey/44c3ade6a2406d337df6d93097a52fdf)
7
7
 
8
- PerfMonger is an yet anothor performance measurement/monitoring tool
9
- speaking JSON.
8
+ PerfMonger is a system performance monitor which enables high-resolution and holistic performance measurement with the programmer friendly interface.
9
+
10
+ * High-resolution: sub-second level monitoring is possible!
11
+ * Holistic performance measurement: monitoring CPU, Disk I/O, Network all at once.
12
+ * Programmer friendly: PerfMonger speaks monitoring results in JSON format, which makes later performance analysis much easier (ex. [jq](https://github.com/stedolan/jq)).
10
13
 
11
14
  **CAUTION: PerfMonger is still in early stage, so there may be a drastic change in the future. Do not use it for critical jobs**
12
15
 
13
16
  ## Target platform
14
17
 
15
18
  * GNU/Linux
19
+ * Mac OS X (experimental support)
16
20
 
17
- ## Prerequisites
18
-
19
- * Ruby 1.9.3 or later
20
- * gnuplot 4.6.0 or later (optional)
21
-
22
- Note: You need Cutter unit testing framework for building/running tests.
23
-
24
- ## How to install
21
+ ## How to installation
25
22
 
26
23
  gem install perfmonger
27
24
 
25
+ You need gnuplot 4.6.0 or later build with cairo terminals for plotting measurement data with `perfmonger plot` command.
26
+
28
27
  ### Build from source
29
28
 
29
+ You need Ruby 1.9.3 or later, and Go 1.8 or later to build perfmonger.
30
+
31
+ bundle
30
32
  rake build
31
33
 
32
- ## How to use: case study
34
+ ## Getting started
35
+
36
+ Basic usage of PerfMonger is:
37
+
38
+ * Run `perfmonger record` to record performance information logs
39
+ * Run `perfmonger play` to show performance information logs in JSON format
40
+
41
+ `perfmonger play` repatedly prints records of system performance including CPU
42
+ usages, disk usages, and network usages. One line includes only one record, so
43
+ you can easily process output records with `jq`, or any scripting languages like
44
+ Ruby, Python, and etc.
45
+
46
+ Pretty-printed structure of a record is as follows:
47
+
48
+ ```
49
+ {"time": 1500043743.504, # timestamp in unix epoch time
50
+ "cpu": { # CPU usages
51
+ "num_core": 2, # the number of cores
52
+ "all": { # aggregated CPU usage (max = num_core * 100%)
53
+ "usr": 50.0,
54
+ "sys": 50.0,
55
+ "idle": 100.0,
56
+ ...
57
+ },
58
+ "cores": [ # Usage of each CPU core
59
+ {
60
+ "usr": 25.0,
61
+ "sys": 25.0,
62
+ "idle": 50.0,
63
+ ...
64
+ },
65
+ {
66
+ "usr": 25.0,
67
+ "sys": 25.0,
68
+ "idle": 50.0,
69
+ ...
70
+ }
71
+ ]
72
+ },
73
+ "disk": { # Disk usages
74
+ "devices": ["sda"], # List of disk devices
75
+ "sda": { # Usage of device 'sda'
76
+ "riops": 10.0, # The number of read I/O per second
77
+ "wiops": 20.0, # The number of write I/O per second
78
+ "rkbyteps": 80.0, # Read transfer rate in KB/s
79
+ "wkbyteps": 160.0, # Write transfer rate in KB/s
80
+ ...
81
+ }
82
+ "total": { # Aggregated usage of all devices
83
+ "riops": 10.0,
84
+ "wiops": 20.0,
85
+ "rkbyteps": 80.0,
86
+ "wkbyteps": 160.0,
87
+ ...
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+
94
+ ## Typical use cases
95
+
96
+ ### `perfmonger live`: live performance monitoring
97
+
98
+ $ perfmonger live
33
99
 
34
100
  ### Monitor IO performance of /dev/sda for each 0.1 second
35
101
 
@@ -42,19 +108,3 @@ Note: You need Cutter unit testing framework for building/running tests.
42
108
  ### Monitor CPU usage and IO performance of /dev/sda, sdb for each 0.1 second
43
109
 
44
110
  $ perfmonger record -i 0.1 -d sda -d sdb
45
-
46
- ### Plot CPU and IOPS
47
-
48
- $ perfmonger record -i 0.1 -C -d sda > /tmp/perfmonger.log & sleep 10; pkill perfmonger
49
- $ perfmonger plot -o /path/to/output_dir/ -Tpng /tmp/perfmonger.log
50
- $ display /path/to/output_dir/read-iops.png
51
- $ display /path/to/output_dir/cpu.png
52
-
53
- ![Sample image of IOPS graph](https://raw.github.com/hayamiz/perfmonger/master/misc/sample-read-iops.png)
54
- ![Sample image of CPU usage graph](https://raw.github.com/hayamiz/perfmonger/master/misc/sample-cpu.png)
55
-
56
- ## Special Thanks
57
-
58
- Large portion of PerfMonger comes from
59
- [SYSSTAT](http://sebastien.godard.pagesperso-orange.fr/) codebase. Thanks for
60
- their great work.
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ task :default => [:spec, :test_core]
7
7
  desc "Run all specs in spec directory"
8
8
  RSpec::Core::RakeTask.new(:spec)
9
9
 
10
- task :spec => [:self_build_core]
10
+ task :spec => [:cross_build_core]
11
11
 
12
12
  desc "Cross build core recorder/player"
13
13
  task :cross_build_core do
@@ -18,20 +18,29 @@ task :cross_build_core do
18
18
  end
19
19
  end
20
20
 
21
- desc "Self build core recorder/player"
22
- task :self_build_core do
23
- Dir.chdir("./core") do
24
- sh "./build.sh -"
25
- end
26
- end
27
-
28
21
  task :build => :cross_build_core
29
22
 
23
+ desc "Install Golang libraries"
24
+ task :go_get do
25
+ sh "go get -u github.com/hayamiz/go-projson"
26
+ sh "go get -u github.com/hayamiz/perfmonger/core/subsystem"
27
+ sh "go get -u golang.org/x/crypto/ssh/terminal"
28
+ sh "go get -u github.com/mattn/go-isatty"
29
+ end
30
+
30
31
  desc "Run tests of core recorder/player"
31
- task :test_core do
32
+ task :test_core => [:cross_build_core] do
32
33
  Dir.chdir("./core/subsystem") do
33
34
  sh "go test -v -cover"
35
+
36
+ # running static analysis
37
+ sh "go vet *.go"
34
38
  end
39
+
40
+ Dir.chdir("./core") do
41
+ sh "go vet *.go"
42
+ end
43
+
35
44
  end
36
45
 
37
46
  desc "Removed generated files"
data/core/Makefile CHANGED
@@ -9,22 +9,6 @@ GO_SRC := utils.go
9
9
  all: build
10
10
 
11
11
 
12
- ../lib/exec/perfmonger-recorder_linux_386: perfmonger-recorder.go $(GO_DEPS)
13
- go build -o $@ perfmonger-recorder.go $(GO_SRC)
14
-
15
-
16
- ../lib/exec/perfmonger-player_linux_386: perfmonger-player.go $(GO_DEPS)
17
- go build -o $@ perfmonger-player.go $(GO_SRC)
18
-
19
-
20
- ../lib/exec/perfmonger-summarizer_linux_386: perfmonger-summarizer.go $(GO_DEPS)
21
- go build -o $@ perfmonger-summarizer.go $(GO_SRC)
22
-
23
-
24
- ../lib/exec/perfmonger-plot-formatter_linux_386: perfmonger-plot-formatter.go $(GO_DEPS)
25
- go build -o $@ perfmonger-plot-formatter.go $(GO_SRC)
26
-
27
-
28
12
  ../lib/exec/perfmonger-recorder_linux_amd64: perfmonger-recorder.go $(GO_DEPS)
29
13
  go build -o $@ perfmonger-recorder.go $(GO_SRC)
30
14
 
@@ -57,8 +41,8 @@ all: build
57
41
  go build -o $@ perfmonger-plot-formatter.go $(GO_SRC)
58
42
 
59
43
 
60
- build: ../lib/exec/perfmonger-recorder_linux_386 ../lib/exec/perfmonger-player_linux_386 ../lib/exec/perfmonger-summarizer_linux_386 ../lib/exec/perfmonger-plot-formatter_linux_386 ../lib/exec/perfmonger-recorder_linux_amd64 ../lib/exec/perfmonger-player_linux_amd64 ../lib/exec/perfmonger-summarizer_linux_amd64 ../lib/exec/perfmonger-plot-formatter_linux_amd64 ../lib/exec/perfmonger-recorder_darwin_amd64 ../lib/exec/perfmonger-player_darwin_amd64 ../lib/exec/perfmonger-summarizer_darwin_amd64 ../lib/exec/perfmonger-plot-formatter_darwin_amd64
44
+ build: ../lib/exec/perfmonger-recorder_linux_amd64 ../lib/exec/perfmonger-player_linux_amd64 ../lib/exec/perfmonger-summarizer_linux_amd64 ../lib/exec/perfmonger-plot-formatter_linux_amd64 ../lib/exec/perfmonger-recorder_darwin_amd64 ../lib/exec/perfmonger-player_darwin_amd64 ../lib/exec/perfmonger-summarizer_darwin_amd64 ../lib/exec/perfmonger-plot-formatter_darwin_amd64
61
45
 
62
46
  clean:
63
- rm -f ../lib/exec/perfmonger-recorder_linux_386 ../lib/exec/perfmonger-player_linux_386 ../lib/exec/perfmonger-summarizer_linux_386 ../lib/exec/perfmonger-plot-formatter_linux_386 ../lib/exec/perfmonger-recorder_linux_amd64 ../lib/exec/perfmonger-player_linux_amd64 ../lib/exec/perfmonger-summarizer_linux_amd64 ../lib/exec/perfmonger-plot-formatter_linux_amd64 ../lib/exec/perfmonger-recorder_darwin_amd64 ../lib/exec/perfmonger-player_darwin_amd64 ../lib/exec/perfmonger-summarizer_darwin_amd64 ../lib/exec/perfmonger-plot-formatter_darwin_amd64
47
+ rm -f ../lib/exec/perfmonger-recorder_linux_amd64 ../lib/exec/perfmonger-player_linux_amd64 ../lib/exec/perfmonger-summarizer_linux_amd64 ../lib/exec/perfmonger-plot-formatter_linux_amd64 ../lib/exec/perfmonger-recorder_darwin_amd64 ../lib/exec/perfmonger-player_darwin_amd64 ../lib/exec/perfmonger-summarizer_darwin_amd64 ../lib/exec/perfmonger-plot-formatter_darwin_amd64
64
48
 
data/core/build.sh CHANGED
@@ -28,7 +28,7 @@ if [[ $1 = "-" ]]; then
28
28
  TARGET=("${os} ${arch}")
29
29
  else
30
30
  # cross build
31
- TARGET=("linux 386" "linux amd64" "darwin amd64")
31
+ TARGET=("linux amd64" "darwin amd64")
32
32
  fi
33
33
 
34
34
  set -e
@@ -4,39 +4,38 @@ package main
4
4
 
5
5
  import (
6
6
  "bufio"
7
- "bytes"
8
7
  "encoding/gob"
8
+ "flag"
9
9
  "fmt"
10
10
  "io"
11
11
  "os"
12
12
 
13
+ projson "github.com/hayamiz/go-projson"
13
14
  ss "github.com/hayamiz/perfmonger/core/subsystem"
14
15
  )
15
16
 
16
- func showCpuStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
17
+ type PlayerOption struct {
18
+ logfile string
19
+ color bool
20
+ pretty bool
21
+ }
22
+
23
+ var option PlayerOption
24
+ var init_rec ss.StatRecord
25
+
26
+ func showCpuStat(printer *projson.JsonPrinter, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
17
27
  cusage, err := ss.GetCpuUsage(prev_rec.Cpu, cur_rec.Cpu)
18
28
  if err != nil {
19
29
  return err
20
30
  }
21
- buffer.WriteString(`,"cpu":`)
22
- cusage.WriteJsonTo(buffer)
31
+
32
+ printer.PutKey("cpu")
33
+ cusage.WriteJsonTo(printer)
23
34
 
24
35
  return nil
25
36
  }
26
37
 
27
- func showInterruptStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
28
- // intr_usage, err := ss.GetInterruptUsage(
29
- // prev_rec.Time, prev_rec.Interrupt,
30
- // cur_rec.Time, cur_rec.Interrupt)
31
- // if err != nil {
32
- // return err
33
- // }
34
- //
35
- // buffer.WriteString(`,"intr":`)
36
- // intr_usage.WriteJsonTo(buffer)
37
- //
38
- // return nil
39
-
38
+ func showInterruptStat(printer *projson.JsonPrinter, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
40
39
  intr_usage, err := ss.GetInterruptUsage(
41
40
  prev_rec.Time, prev_rec.Interrupt,
42
41
  cur_rec.Time, cur_rec.Interrupt)
@@ -44,13 +43,13 @@ func showInterruptStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *s
44
43
  return err
45
44
  }
46
45
 
47
- buffer.WriteString(`,"intr":`)
48
- intr_usage.WriteJsonTo(buffer)
46
+ printer.PutKey("intr")
47
+ intr_usage.WriteJsonTo(printer)
49
48
 
50
49
  return nil
51
50
  }
52
51
 
53
- func showDiskStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
52
+ func showDiskStat(printer *projson.JsonPrinter, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
54
53
  dusage, err := ss.GetDiskUsage(
55
54
  prev_rec.Time, prev_rec.Disk,
56
55
  cur_rec.Time, cur_rec.Disk)
@@ -58,13 +57,14 @@ func showDiskStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *ss.Sta
58
57
  return err
59
58
  }
60
59
 
61
- buffer.WriteString(`,"disk":`)
62
- dusage.WriteJsonTo(buffer)
60
+ printer.PutKey("disk")
61
+
62
+ dusage.WriteJsonTo(printer)
63
63
 
64
64
  return nil
65
65
  }
66
66
 
67
- func showNetStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
67
+ func showNetStat(printer *projson.JsonPrinter, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
68
68
  dusage, err := ss.GetNetUsage(
69
69
  prev_rec.Time, prev_rec.Net,
70
70
  cur_rec.Time, cur_rec.Net,
@@ -73,52 +73,77 @@ func showNetStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *ss.Stat
73
73
  return err
74
74
  }
75
75
 
76
- buffer.WriteString(`,"net":`)
77
- dusage.WriteJsonTo(buffer)
76
+ printer.PutKey("net")
77
+
78
+ dusage.WriteJsonTo(printer)
78
79
 
79
80
  return nil
80
81
  }
81
82
 
82
- func showStat(buffer *bytes.Buffer, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
83
- buffer.WriteString(fmt.Sprintf(`{"time":%.3f`, float64(cur_rec.Time.UnixNano())/1e9))
83
+ func showStat(printer *projson.JsonPrinter, prev_rec *ss.StatRecord, cur_rec *ss.StatRecord) error {
84
+ printer.Reset()
85
+ if option.pretty {
86
+ printer.SetStyle(projson.SmartStyle)
87
+ }
88
+ if option.color {
89
+ printer.SetColor(true)
90
+ }
91
+ printer.BeginObject()
92
+ printer.PutKey("time")
93
+ printer.PutFloatFmt(float64(cur_rec.Time.UnixNano())/1e9, "%.3f")
94
+ printer.PutKey("elapsed_time")
95
+ printer.PutFloatFmt((float64(cur_rec.Time.UnixNano())-float64(init_rec.Time.UnixNano()))/1e9,
96
+ "%.3f")
97
+
84
98
  if cur_rec.Cpu != nil {
85
- err := showCpuStat(buffer, prev_rec, cur_rec)
99
+ err := showCpuStat(printer, prev_rec, cur_rec)
86
100
  if err != nil {
87
101
  return err
88
102
  }
89
103
  }
90
104
  if cur_rec.Interrupt != nil {
91
- err := showInterruptStat(buffer, prev_rec, cur_rec)
105
+ err := showInterruptStat(printer, prev_rec, cur_rec)
92
106
  if err != nil {
93
107
  return err
94
108
  }
95
109
  }
96
110
  if cur_rec.Disk != nil {
97
- err := showDiskStat(buffer, prev_rec, cur_rec)
111
+ err := showDiskStat(printer, prev_rec, cur_rec)
98
112
  if err != nil {
99
113
  return err
100
114
  }
101
115
  }
102
116
  if cur_rec.Net != nil {
103
- err := showNetStat(buffer, prev_rec, cur_rec)
117
+ err := showNetStat(printer, prev_rec, cur_rec)
104
118
  if err != nil {
105
119
  return err
106
120
  }
107
121
  }
108
- buffer.WriteString("}\n")
122
+
123
+ printer.FinishObject()
109
124
 
110
125
  return nil
111
126
  }
112
127
 
128
+ func parseArgs() {
129
+ flag.BoolVar(&option.color, "color", false, "Use colored JSON output")
130
+ flag.BoolVar(&option.pretty, "pretty", false, "Use human readable JSON output")
131
+
132
+ flag.Parse()
133
+
134
+ option.logfile = flag.Arg(0)
135
+ }
136
+
113
137
  func main() {
114
- args := os.Args
115
138
  var in *os.File
116
139
  var out *bufio.Writer
117
140
 
118
- if len(args) < 2 {
141
+ parseArgs()
142
+
143
+ if option.logfile == "" {
119
144
  in = os.Stdin
120
145
  } else {
121
- f, err := os.Open(args[1])
146
+ f, err := os.Open(option.logfile)
122
147
  if err != nil {
123
148
  panic(err)
124
149
  }
@@ -159,9 +184,10 @@ func main() {
159
184
  } else if err != nil {
160
185
  panic(err)
161
186
  }
187
+ init_rec = records[curr]
162
188
  curr ^= 1
163
189
 
164
- buffer := bytes.NewBuffer([]byte{})
190
+ printer := projson.NewPrinter()
165
191
  for {
166
192
  prev_rec := &records[curr^1]
167
193
  cur_rec := &records[curr]
@@ -173,21 +199,26 @@ func main() {
173
199
  panic(err)
174
200
  }
175
201
 
176
- err = showStat(buffer, prev_rec, cur_rec)
202
+ err = showStat(printer, prev_rec, cur_rec)
177
203
  if err != nil {
178
- buffer.Reset()
204
+ printer.Reset()
179
205
  fmt.Fprintln(os.Stderr, "skip by err")
180
206
  continue
181
207
  }
182
208
 
183
- _, err = out.WriteString(buffer.String())
209
+ if str, err := printer.String(); err != nil {
210
+ fmt.Println("error", err)
211
+ fmt.Println(str)
212
+ } else {
213
+ _, err = out.WriteString(str + "\n")
214
+ }
184
215
  err = out.Flush()
185
216
  if err != nil {
186
217
  // stdout is closed
187
218
  break
188
219
  }
189
220
 
190
- buffer.Reset()
221
+ printer.Reset()
191
222
 
192
223
  curr ^= 1
193
224
  }