Class: Fluent::Plugin::KubernetesParser

Inherits:
Parser
  • Object
show all
Defined in:
lib/fluent/plugin/parser_kubernetes.rb

Instance Method Summary collapse

Instance Method Details

#configure(conf) ⇒ Object

Raises:

  • (ConfigError)


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/fluent/plugin/parser_kubernetes.rb', line 31

def configure(conf)
  super

  raise ConfigError, "delimiter must be a single character. #{@delimiter} is not." if @delimiter.length != 1

  # Kubernetes logging format
  # https://kubernetes.io/docs/concepts/cluster-administration/system-logs/
  # https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md
  #
  # <klog header> "<message>" <key1>="<value1>" <key2>="<value2>" ...
  # Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
  #
  # where the fields are defined as follows:
  #   L                A single character, representing the log level (eg 'I' for INFO)
  #   mm               The month (zero padded; ie May is '05')
  #   dd               The day (zero padded)
  #   hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
  #   threadid         The space-padded thread ID as returned by GetTID()
  #   file             The file name
  #   line             The line number
  #   msg              The user-supplied message
  @klog_regexp = /
    ^
    (?<log_level>[A-Z])(?<month>\d{2})(?<day>\d{2})\s+
    (?<time>\d{2}:\d{2}:\d{2}(|\.\d+))\s+
    (?<threadid>\d+)\s+
    (?<file>[^ ]*):(?<line>\d+)\]\s
    (
      "(?<msg>([^"\\]*(?:\\.[^"\\]*)*))"(|\s+(?<kv>.*))
      |
      (?<greedy_msg>.*)
    )
    $
  /x

  # KV format used by containerd
  @kv_regexp = /
    (?<key>[^\s=]+)
    (
      \s
      |
      =
      (?<value>
        "(?<quoted>([^"\\]*(?:\\.[^"\\]*)*))"
        |
        \[(?<array>([^\]\\]*(?:\\.[^\]\\]*)*))\]
        |
        [^\s]*
      )
    )
  /x
end

#parse(text) {|time, record| ... } ⇒ Object

Yields:

  • (time, record)


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/fluent/plugin/parser_kubernetes.rb', line 84

def parse(text)
  if text.nil? || text.empty?
    yield nil, nil
    return
  end

  time = nil
  record = {}

  matches_klog = @klog_regexp.match(text)

  unless matches_klog.nil?
    captures = matches_klog.named_captures

    captures['msg'] = captures['greedy_msg'] unless captures['greedy_msg'].nil?
    captures.delete('greedy_msg')

    unless captures['time'].nil?
      record['time'] =
        format('%s-%s-%sT%s%s', @force_year.nil? ? Time.now.year : @force_year, captures['month'], captures['day'],
               captures['time'], @default_tz)
      captures.delete('month')
      captures.delete('day')
      captures.delete('time')
    end

    text = captures['kv']
    captures.delete('kv')

    captures.each do |key, value|
      if key == 'log_level'
        # As seen here:
        # https://github.com/kubernetes/klog/blob/9ad246211af1ed84621ee94a26fcce0038b69cd1/klog.go#L112
        record['level'] = case value
                          when 'I'
                            'info'
                          when 'W'
                            'warn'
                          when 'E'
                            'error'
                          when 'F'
                            'fatal'
                          else
                            value
                          end
      elsif %w[threadid line].include?(key)
        record[key] = value.to_i
      else
        record[key] = value
      end
    end
  end

  unless text.nil?
    text.scan(@kv_regexp).each do |key, value, quoted, array|
      record[key] = if !quoted.nil?
                      quoted.gsub(/\\(.)/, '\1')
                    elsif !array.nil?
                      array.split(',').map(&:strip)
                    else
                      value
                    end
    end
  end

  time = parse_time(record)

  yield time, record
end