github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/summary/collector.go (about) 1 // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0. 2 3 package summary 4 5 import ( 6 "strings" 7 "sync" 8 "time" 9 10 "github.com/docker/go-units" 11 12 "github.com/pingcap/log" 13 "go.uber.org/zap" 14 ) 15 16 const ( 17 // BackupUnit tells summary in backup 18 BackupUnit = "backup" 19 // RestoreUnit tells summary in restore 20 RestoreUnit = "restore" 21 22 // TotalKV is a field we collect during backup/restore 23 TotalKV = "total kv" 24 // TotalBytes is a field we collect during backup/restore 25 TotalBytes = "total bytes" 26 // BackupDataSize is a field we collect after backup finish 27 BackupDataSize = "backup data size(after compressed)" 28 // RestoreDataSize is a field we collection after restore finish 29 RestoreDataSize = "restore data size(after compressed)" 30 ) 31 32 // LogCollector collects infos into summary log. 33 type LogCollector interface { 34 SetUnit(unit string) 35 36 CollectSuccessUnit(name string, unitCount int, arg interface{}) 37 38 CollectFailureUnit(name string, reason error) 39 40 CollectDuration(name string, t time.Duration) 41 42 CollectInt(name string, t int) 43 44 CollectUInt(name string, t uint64) 45 46 SetSuccessStatus(success bool) 47 48 Summary(name string) 49 } 50 51 type logFunc func(msg string, fields ...zap.Field) 52 53 var collector LogCollector = NewLogCollector(log.Info) 54 55 // InitCollector initilize global collector instance. 56 func InitCollector( // revive:disable-line:flag-parameter 57 hasLogFile bool, 58 ) { 59 logF := log.L().Info 60 if hasLogFile { 61 conf := new(log.Config) 62 // Always duplicate summary to stdout. 63 logger, _, err := log.InitLogger(conf) 64 if err == nil { 65 logF = func(msg string, fields ...zap.Field) { 66 logger.Info(msg, fields...) 67 log.Info(msg, fields...) 68 } 69 } 70 } 71 collector = NewLogCollector(logF) 72 } 73 74 type logCollector struct { 75 mu sync.Mutex 76 unit string 77 successUnitCount int 78 failureUnitCount int 79 successCosts map[string]time.Duration 80 successData map[string]uint64 81 failureReasons map[string]error 82 durations map[string]time.Duration 83 ints map[string]int 84 uints map[string]uint64 85 successStatus bool 86 startTime time.Time 87 88 log logFunc 89 } 90 91 // NewLogCollector returns a new LogCollector. 92 func NewLogCollector(log logFunc) LogCollector { 93 return &logCollector{ 94 successUnitCount: 0, 95 failureUnitCount: 0, 96 successCosts: make(map[string]time.Duration), 97 successData: make(map[string]uint64), 98 failureReasons: make(map[string]error), 99 durations: make(map[string]time.Duration), 100 ints: make(map[string]int), 101 uints: make(map[string]uint64), 102 log: log, 103 startTime: time.Now(), 104 } 105 } 106 107 func (tc *logCollector) SetUnit(unit string) { 108 tc.mu.Lock() 109 defer tc.mu.Unlock() 110 tc.unit = unit 111 } 112 113 func (tc *logCollector) CollectSuccessUnit(name string, unitCount int, arg interface{}) { 114 tc.mu.Lock() 115 defer tc.mu.Unlock() 116 117 switch v := arg.(type) { 118 case time.Duration: 119 tc.successUnitCount += unitCount 120 tc.successCosts[name] += v 121 case uint64: 122 tc.successData[name] += v 123 } 124 } 125 126 func (tc *logCollector) CollectFailureUnit(name string, reason error) { 127 tc.mu.Lock() 128 defer tc.mu.Unlock() 129 if _, ok := tc.failureReasons[name]; !ok { 130 tc.failureReasons[name] = reason 131 tc.failureUnitCount++ 132 } 133 } 134 135 func (tc *logCollector) CollectDuration(name string, t time.Duration) { 136 tc.mu.Lock() 137 defer tc.mu.Unlock() 138 tc.durations[name] += t 139 } 140 141 func (tc *logCollector) CollectInt(name string, t int) { 142 tc.mu.Lock() 143 defer tc.mu.Unlock() 144 tc.ints[name] += t 145 } 146 147 func (tc *logCollector) CollectUInt(name string, t uint64) { 148 tc.mu.Lock() 149 defer tc.mu.Unlock() 150 tc.uints[name] += t 151 } 152 153 func (tc *logCollector) SetSuccessStatus(success bool) { 154 tc.mu.Lock() 155 defer tc.mu.Unlock() 156 tc.successStatus = success 157 } 158 159 func logKeyFor(key string) string { 160 return strings.ReplaceAll(key, " ", "-") 161 } 162 163 func (tc *logCollector) Summary(name string) { 164 tc.mu.Lock() 165 defer func() { 166 tc.durations = make(map[string]time.Duration) 167 tc.ints = make(map[string]int) 168 tc.successCosts = make(map[string]time.Duration) 169 tc.failureReasons = make(map[string]error) 170 tc.mu.Unlock() 171 }() 172 173 logFields := make([]zap.Field, 0, len(tc.durations)+len(tc.ints)+3) 174 175 logFields = append(logFields, 176 zap.Int("total-ranges", tc.failureUnitCount+tc.successUnitCount), 177 zap.Int("ranges-succeed", tc.successUnitCount), 178 zap.Int("ranges-failed", tc.failureUnitCount), 179 ) 180 181 for key, val := range tc.durations { 182 logFields = append(logFields, zap.Duration(logKeyFor(key), val)) 183 } 184 for key, val := range tc.ints { 185 logFields = append(logFields, zap.Int(logKeyFor(key), val)) 186 } 187 for key, val := range tc.uints { 188 logFields = append(logFields, zap.Uint64(logKeyFor(key), val)) 189 } 190 191 if len(tc.failureReasons) != 0 || !tc.successStatus { 192 for unitName, reason := range tc.failureReasons { 193 logFields = append(logFields, zap.String("unit-name", unitName), zap.Error(reason)) 194 } 195 tc.log(name+" failed summary", logFields...) 196 return 197 } 198 199 totalDureTime := time.Since(tc.startTime) 200 logFields = append(logFields, zap.Duration("total-take", totalDureTime)) 201 for name, data := range tc.successData { 202 if name == TotalBytes { 203 logFields = append(logFields, 204 zap.String("total-kv-size", units.HumanSize(float64(data))), 205 zap.String("average-speed", units.HumanSize(float64(data)/totalDureTime.Seconds())+"/s")) 206 continue 207 } 208 if name == BackupDataSize { 209 if tc.failureUnitCount+tc.successUnitCount == 0 { 210 logFields = append(logFields, zap.String("Result", "Nothing to bakcup")) 211 } else { 212 logFields = append(logFields, 213 zap.String(BackupDataSize, units.HumanSize(float64(data)))) 214 } 215 continue 216 } 217 if name == RestoreDataSize { 218 if tc.failureUnitCount+tc.successUnitCount == 0 { 219 logFields = append(logFields, zap.String("Result", "Nothing to restore")) 220 } else { 221 logFields = append(logFields, 222 zap.String(RestoreDataSize, units.HumanSize(float64(data)))) 223 } 224 continue 225 } 226 logFields = append(logFields, zap.Uint64(logKeyFor(name), data)) 227 } 228 229 tc.log(name+" success summary", logFields...) 230 } 231 232 // SetLogCollector allow pass LogCollector outside. 233 func SetLogCollector(l LogCollector) { 234 collector = l 235 }