// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package parse import ( "fmt" "log" "strings" ) // Rlog contains the processed logs type Rlog struct { Logs []*Logmsg // In the order in the log file ServerCall map[string]*Logmsg // ID->Request, client->server ServerReply map[string]*Logmsg // ID->Response, server->client (includes Errors) ClientCall map[string]*Logmsg ClientReply map[string]*Logmsg ClientNotifs []*Logmsg ServerNotifs []*Logmsg Histogram *LogHist } func newRlog(x []*Logmsg) *Rlog { return &Rlog{Logs: x, ServerCall: make(map[string]*Logmsg), ServerReply: make(map[string]*Logmsg), ClientCall: make(map[string]*Logmsg), ClientReply: make(map[string]*Logmsg), ClientNotifs: []*Logmsg{}, ServerNotifs: []*Logmsg{}, Histogram: &LogHist{}, } } // Counts returns a one-line summary of an Rlog func (r *Rlog) Counts() string { return fmt.Sprintf("logs:%d srvC:%d srvR:%d clC:%d clR:%d clN:%d srvN:%d", len(r.Logs), len(r.ServerCall), len(r.ServerReply), len(r.ClientCall), len(r.ClientReply), len(r.ClientNotifs), len(r.ServerNotifs)) } // ToRlog reads a log file and returns a *Rlog func ToRlog(fname string) (*Rlog, error) { x, err := ReadLogs(fname) if err != nil { return nil, err } ans := newRlog(x) for _, l := range x { switch l.Type { case ClRequest: ans.ServerCall[l.ID] = l case ClResponse: ans.ServerReply[l.ID] = l if l.Type != ReportErr { n := 0 fmt.Sscanf(l.Elapsed, "%d", &n) ans.Histogram.add(n) } case SvRequest: ans.ClientCall[l.ID] = l case SvResponse: ans.ClientReply[l.ID] = l case ToClient: ans.ClientNotifs = append(ans.ClientNotifs, l) case ToServer: ans.ServerNotifs = append(ans.ServerNotifs, l) case ReportErr: ans.ServerReply[l.ID] = l l.Method = ans.ServerCall[l.ID].Method // Method not in log message default: log.Fatalf("eh? %s/%s (%s)", l.Type, l.Method, l.ID) } } return ans, nil } // LogHist gets ints, and puts them into buckets: // <=10, <=30, 100, 300, 1000, ... // It produces a historgram of elapsed times in milliseconds type LogHist struct { cnts []int } func (l *LogHist) add(n int) { if n < 0 { n = 0 } bucket := 0 for ; n > 0; n /= 10 { if n < 10 { break } if n < 30 { bucket++ break } bucket += 2 } if len(l.cnts) <= bucket { for j := len(l.cnts); j < bucket+10; j++ { l.cnts = append(l.cnts, 0) } } l.cnts[bucket]++ } // String returns a string describing a histogram func (l *LogHist) String() string { top := len(l.cnts) - 1 for ; top > 0 && l.cnts[top] == 0; top-- { } labs := []string{"10", "30"} out := strings.Builder{} out.WriteByte('[') for i := 0; i <= top; i++ { label := labs[i%2] labs[i%2] += "0" fmt.Fprintf(&out, "%s:%d ", label, l.cnts[i]) } out.WriteByte(']') return out.String() }