github.phpd.cn/cilium/cilium@v1.6.12/test/helpers/logutils/utils.go (about) 1 // Copyright 2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package logutils 16 17 import ( 18 "fmt" 19 "regexp" 20 "sort" 21 "strings" 22 ) 23 24 const ( 25 selfishThresholdMsg = "Goroutine took lock for more than" // from https://github.com/cilium/cilium/pull/5268 26 27 contextDeadlineExceeded = "context deadline exceeded" 28 errorLogs = "level=error" 29 warningLogs = "level=warning" 30 aPIPanicked = "Cilium API handler panicked" 31 ) 32 33 var countLogsMessages = []string{contextDeadlineExceeded, errorLogs, warningLogs, aPIPanicked, selfishThresholdMsg} 34 35 // LogErrorsSummary returns error and warning summary for given logs 36 func LogErrorsSummary(logs string) string { 37 var sb strings.Builder 38 for _, message := range countLogsMessages { 39 var prefix = "" 40 result := strings.Count(logs, message) 41 if result > 5 { 42 // Added a warning emoji just in case that are more than 5 warning in the logs. 43 prefix = "⚠️ " 44 } 45 fmt.Fprintf(&sb, "%sNumber of %q in logs: %d\n", prefix, message, result) 46 } 47 48 warns := getErrorWarningMsgs(logs, 5) 49 if len(warns) > 0 { 50 sb.WriteString(fmt.Sprintf("Top %d errors/warnings:\n", len(warns))) 51 } else { 52 sb.WriteString("No errors/warnings found in logs") 53 } 54 sb.WriteString(strings.Join(warns, "\n")) 55 sb.WriteString("\n") 56 return sb.String() 57 } 58 59 // getErrorWarningMsgs takes Cilium log and returns at most `n` 60 // top occurring error/warning messages 61 func getErrorWarningMsgs(logs string, n int) []string { 62 63 errors := map[string]int{} 64 warnings := map[string]int{} 65 for _, line := range strings.Split(logs, "\n") { 66 if strings.Contains(line, errorLogs) { 67 msg := getMsg(line) 68 errors[msg]++ 69 } else if strings.Contains(line, warningLogs) { 70 msg := getMsg(line) 71 warnings[msg]++ 72 } 73 } 74 75 errs := make([]message, 0, len(errors)+len(warnings)) 76 77 for msg, count := range errors { 78 errs = append(errs, message{msg, count, true}) 79 } 80 81 for msg, count := range warnings { 82 errs = append(errs, message{msg, count, false}) 83 } 84 85 sort.Sort(sort.Reverse(byImportance(errs))) 86 87 if n > len(errs) { 88 n = len(errs) 89 } 90 91 result := make([]string, n) 92 for i := 0; i < n; i++ { 93 result[i] = errs[i].msg 94 } 95 return result 96 } 97 98 // getMsg extracts message from log line 99 func getMsg(logLine string) string { 100 msgRegex := regexp.MustCompile(`msg=".*?"`) 101 errRegex := regexp.MustCompile(`error=".*?"`) 102 103 msg := msgRegex.FindString(logLine) 104 offset := 5 105 if len(msg) == 0 { 106 msg = errRegex.FindString(logLine) 107 offset = 7 108 } 109 if len(msg) > 0 { 110 msg = msg[offset : len(msg)-1] 111 return msg 112 } 113 return "" 114 } 115 116 type message struct { 117 msg string 118 count int 119 isError bool 120 } 121 122 type byImportance []message 123 124 // Len is part of sort.Interface 125 func (s byImportance) Len() int { 126 return len(s) 127 } 128 129 // Swap is part of sort.Interface 130 func (s byImportance) Swap(i, j int) { 131 s[i], s[j] = s[j], s[i] 132 } 133 134 // Less is part of sort.Interface 135 func (s byImportance) Less(i, j int) bool { 136 return (!s[i].isError && s[j].isError) || s[i].count < s[j].count 137 }