Skip to content

Instantly share code, notes, and snippets.

@rubyist
Created June 5, 2015 16:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rubyist/cea2bea8098fac0d4b4a to your computer and use it in GitHub Desktop.
Save rubyist/cea2bea8098fac0d4b4a to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"errors"
"fmt"
"os"
"strconv"
"strings"
"time"
)
type Stat struct {
Key string
RequestHeaderSize int
RequestBodySize int
ResponseHeaderSize int
ResponseBodySize int
ResponseTime time.Duration
StatusCode int
Url string
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: lfs-stats <statsfile>")
os.Exit(1)
}
file, err := os.Open(os.Args[1])
if err != nil {
fmt.Printf("Error opening stats file: %s\n", err)
os.Exit(1)
}
// First line contains some meta info
r := bufio.NewReader(file)
line, err := r.ReadString('\n')
if err != nil {
fmt.Printf("Error reading stats file: %s\n", err)
os.Exit(1)
}
fmt.Println("Configuration:")
s := bufio.NewScanner(strings.NewReader(line))
s.Split(bufio.ScanWords)
for s.Scan() {
l := s.Text()
pieces := strings.Split(l, "=")
if len(pieces) != 2 {
fmt.Printf("Invalid format for line: %s\n", line)
os.Exit(1)
}
if pieces[0] == "time" {
t, err := strconv.Atoi(pieces[1])
if err != nil {
fmt.Printf("Error parsing time: %s\n", err)
os.Exit(1)
}
fmt.Printf("\tTime: %s\n", time.Unix(int64(t), 0))
} else {
fmt.Printf("\t%s: %s\n", strings.Title(pieces[0]), pieces[1])
}
}
fmt.Println("")
stats := make(map[string][]*Stat)
scanner := bufio.NewScanner(r)
for scanner.Scan() {
l := scanner.Text()
stat, err := parseRecord(l)
if err != nil {
fmt.Printf("Parse Error: %s\n", err)
os.Exit(1)
}
stats[stat.Key] = append(stats[stat.Key], stat)
}
if err := scanner.Err(); err != nil {
fmt.Printf("Error scanning input: %s\n", err)
os.Exit(1)
}
o := os.Stdout
fmt.Fprint(o, "HTTP Transfer Stats\n\n")
totalTransfers := 0
totalTime := int64(0)
for key, vs := range stats {
reqWireSize := 0
resWireSize := 0
reqHeaderSize := 0
resHeaderSize := 0
reqBodySize := 0
resBodySize := 0
keyTransfers := 0
keyTime := int64(0) // nanoseconds
for _, stat := range vs {
reqWireSize += stat.RequestHeaderSize + stat.RequestBodySize
resWireSize += stat.ResponseHeaderSize + stat.ResponseBodySize
reqHeaderSize += stat.RequestHeaderSize
resHeaderSize += stat.ResponseHeaderSize
reqBodySize += stat.RequestBodySize
resBodySize += stat.ResponseBodySize
totalTime += stat.ResponseTime.Nanoseconds()
keyTime += stat.ResponseTime.Nanoseconds()
keyTransfers++
totalTransfers++
}
wireSize := reqWireSize + resWireSize
fmt.Fprintf(o, "%s:\n", key)
fmt.Fprintf(o, "\tTransfers: %d\n", keyTransfers)
fmt.Fprintf(o, "\tTime: %.2fms\n", float64(keyTime)/1000000.0)
fmt.Fprintf(o, "\tWire Data (Bytes): %d\n", wireSize)
fmt.Fprintf(o, "\t\tRequest Size (Bytes): %d\n", reqWireSize)
fmt.Fprintf(o, "\t\t\tHeaders: %d\n", reqHeaderSize)
fmt.Fprintf(o, "\t\t\tBodies: %d\n", reqBodySize)
fmt.Fprintf(o, "\t\tResponse Size (Bytes): %d\n", resWireSize)
fmt.Fprintf(o, "\t\t\tHeaders: %d\n", resHeaderSize)
fmt.Fprintf(o, "\t\t\tBodies: %d\n", resBodySize)
fmt.Fprintf(o, "\tMean Wire Size: %d\n", wireSize/keyTransfers)
fmt.Fprintf(o, "\t\tRequests: %d\n", reqWireSize/keyTransfers)
fmt.Fprintf(o, "\t\tResponses: %d\n", resWireSize/keyTransfers)
fmt.Fprintf(o, "\tMean Transfer Time: %.2fms\n", float64(keyTime)/float64(keyTransfers)/1000000.0)
fmt.Fprintln(o, "")
}
fmt.Fprintf(o, "\nTotal Transfers: %d\n", totalTransfers)
fmt.Fprintf(o, "Total Time: %.2fms\n", float64(totalTime)/1000000.0)
}
func parseRecord(r string) (*Stat, error) {
tscanner := bufio.NewScanner(strings.NewReader(r))
tscanner.Split(bufio.ScanWords)
stat := &Stat{}
for tscanner.Scan() {
entry := tscanner.Text()
pieces := strings.Split(entry, "=")
if len(pieces) != 2 {
return nil, errors.New("Invalid format")
}
switch pieces[0] {
case "key":
stat.Key = pieces[1]
case "reqheader":
s, err := strconv.Atoi(pieces[1])
if err != nil {
return nil, errors.New("Invalid format")
}
stat.RequestHeaderSize = s
case "reqbody":
s, err := strconv.Atoi(pieces[1])
if err != nil {
return nil, errors.New("Invalid format")
}
stat.RequestBodySize = s
case "resheader":
s, err := strconv.Atoi(pieces[1])
if err != nil {
return nil, errors.New("Invalid format")
}
stat.ResponseHeaderSize = s
case "resbody":
s, err := strconv.Atoi(pieces[1])
if err != nil {
return nil, errors.New("Invalid format")
}
stat.ResponseBodySize = s
case "restime":
s, err := strconv.Atoi(pieces[1])
if err != nil {
return nil, errors.New("Invalid format")
}
stat.ResponseTime = time.Duration(s)
case "status":
s, err := strconv.Atoi(pieces[1])
if err != nil {
return nil, errors.New("Invalid format")
}
stat.StatusCode = s
case "url":
stat.Url = pieces[1]
default:
return nil, fmt.Errorf("Unknown key %s", pieces[0])
}
}
return stat, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment