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  }