oomparser.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  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. package oomparser
  15. import (
  16. "path"
  17. "regexp"
  18. "strconv"
  19. "time"
  20. "github.com/euank/go-kmsg-parser/kmsgparser"
  21. "k8s.io/klog"
  22. )
  23. var (
  24. containerRegexp = regexp.MustCompile(`Task in (.*) killed as a result of limit of (.*)`)
  25. lastLineRegexp = regexp.MustCompile(`Killed process ([0-9]+) \((.+)\)`)
  26. firstLineRegexp = regexp.MustCompile(`invoked oom-killer:`)
  27. )
  28. // OomParser wraps a kmsgparser in order to extract OOM events from the
  29. // individual kernel ring buffer messages.
  30. type OomParser struct {
  31. parser kmsgparser.Parser
  32. }
  33. // struct that contains information related to an OOM kill instance
  34. type OomInstance struct {
  35. // process id of the killed process
  36. Pid int
  37. // the name of the killed process
  38. ProcessName string
  39. // the time that the process was reported to be killed,
  40. // accurate to the minute
  41. TimeOfDeath time.Time
  42. // the absolute name of the container that OOMed
  43. ContainerName string
  44. // the absolute name of the container that was killed
  45. // due to the OOM.
  46. VictimContainerName string
  47. }
  48. // gets the container name from a line and adds it to the oomInstance.
  49. func getContainerName(line string, currentOomInstance *OomInstance) error {
  50. parsedLine := containerRegexp.FindStringSubmatch(line)
  51. if parsedLine == nil {
  52. return nil
  53. }
  54. currentOomInstance.ContainerName = path.Join("/", parsedLine[1])
  55. currentOomInstance.VictimContainerName = path.Join("/", parsedLine[2])
  56. return nil
  57. }
  58. // gets the pid, name, and date from a line and adds it to oomInstance
  59. func getProcessNamePid(line string, currentOomInstance *OomInstance) (bool, error) {
  60. reList := lastLineRegexp.FindStringSubmatch(line)
  61. if reList == nil {
  62. return false, nil
  63. }
  64. pid, err := strconv.Atoi(reList[1])
  65. if err != nil {
  66. return false, err
  67. }
  68. currentOomInstance.Pid = pid
  69. currentOomInstance.ProcessName = reList[2]
  70. return true, nil
  71. }
  72. // uses regex to see if line is the start of a kernel oom log
  73. func checkIfStartOfOomMessages(line string) bool {
  74. potential_oom_start := firstLineRegexp.MatchString(line)
  75. if potential_oom_start {
  76. return true
  77. }
  78. return false
  79. }
  80. // StreamOoms writes to a provided a stream of OomInstance objects representing
  81. // OOM events that are found in the logs.
  82. // It will block and should be called from a goroutine.
  83. func (self *OomParser) StreamOoms(outStream chan<- *OomInstance) {
  84. kmsgEntries := self.parser.Parse()
  85. defer self.parser.Close()
  86. for msg := range kmsgEntries {
  87. in_oom_kernel_log := checkIfStartOfOomMessages(msg.Message)
  88. if in_oom_kernel_log {
  89. oomCurrentInstance := &OomInstance{
  90. ContainerName: "/",
  91. VictimContainerName: "/",
  92. TimeOfDeath: msg.Timestamp,
  93. }
  94. for msg := range kmsgEntries {
  95. err := getContainerName(msg.Message, oomCurrentInstance)
  96. if err != nil {
  97. klog.Errorf("%v", err)
  98. }
  99. finished, err := getProcessNamePid(msg.Message, oomCurrentInstance)
  100. if err != nil {
  101. klog.Errorf("%v", err)
  102. }
  103. if finished {
  104. oomCurrentInstance.TimeOfDeath = msg.Timestamp
  105. break
  106. }
  107. }
  108. outStream <- oomCurrentInstance
  109. }
  110. }
  111. // Should not happen
  112. klog.Errorf("exiting analyzeLines. OOM events will not be reported.")
  113. }
  114. // initializes an OomParser object. Returns an OomParser object and an error.
  115. func New() (*OomParser, error) {
  116. parser, err := kmsgparser.NewParser()
  117. if err != nil {
  118. return nil, err
  119. }
  120. parser.SetLogger(glogAdapter{})
  121. return &OomParser{parser: parser}, nil
  122. }
  123. type glogAdapter struct{}
  124. var _ kmsgparser.Logger = glogAdapter{}
  125. func (glogAdapter) Infof(format string, args ...interface{}) {
  126. klog.V(4).Infof(format, args...)
  127. }
  128. func (glogAdapter) Warningf(format string, args ...interface{}) {
  129. klog.V(2).Infof(format, args...)
  130. }
  131. func (glogAdapter) Errorf(format string, args ...interface{}) {
  132. klog.Warningf(format, args...)
  133. }