Class: Fluent::Config::V1Parser

Inherits:
LiteralParser show all
Defined in:
lib/fluent/config/v1_parser.rb

Constant Summary collapse

ELEMENT_NAME =
/[a-zA-Z0-9_]+/
ELEM_SYMBOLS =
['match', 'source', 'filter', 'system']
RESERVED_PARAMS =
%W(@type @id @label @log_level)

Constants inherited from BasicParser

BasicParser::LINE_END, BasicParser::LINE_END_WITHOUT_SPACING_AND_COMMENT, BasicParser::SPACING, BasicParser::SPACING_WITHOUT_COMMENT, BasicParser::ZERO_OR_MORE_SPACING

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from LiteralParser

#eval_embedded_code, #eval_escape_char, #parse_literal, #scan_double_quoted_string, #scan_embedded_code, #scan_json, #scan_nonquoted_string, #scan_single_quoted_string, #scan_string, unescape_char

Methods inherited from BasicParser

#check, #eof?, #getch, #line_end, #parse_error!, #prev_match, #scan, #skip, #spacing, #spacing_without_comment

Methods included from BasicParser::ClassMethods

#def_literal, #def_symbol, #symbol

Constructor Details

#initialize(strscan, include_basepath, fname, eval_context) ⇒ V1Parser

Returns a new instance of V1Parser.



36
37
38
39
40
41
# File 'lib/fluent/config/v1_parser.rb', line 36

def initialize(strscan, include_basepath, fname, eval_context)
  super(strscan, eval_context)
  @include_basepath = include_basepath
  @fname = fname
  @logger = defined?($log) ? $log : nil
end

Class Method Details

.parse(data, fname, basepath = Dir.pwd, eval_context = nil) ⇒ Object



30
31
32
33
34
# File 'lib/fluent/config/v1_parser.rb', line 30

def self.parse(data, fname, basepath = Dir.pwd, eval_context = nil)
  ss = StringScanner.new(data)
  ps = V1Parser.new(ss, basepath, fname, eval_context)
  ps.parse!
end

Instance Method Details

#error_sampleObject

override



187
188
189
# File 'lib/fluent/config/v1_parser.rb', line 187

def error_sample
  "#{@fname} #{super}"
end

#eval_include(attrs, elems, uri) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/fluent/config/v1_parser.rb', line 150

def eval_include(attrs, elems, uri)
  # replace space(s)(' ') with '+' to prevent invalid uri due to space(s).
  # See: https://github.com/fluent/fluentd/pull/2780#issuecomment-576081212
  u = URI.parse(uri.gsub(/ /, '+'))
  if u.scheme == 'file' || (!u.scheme.nil? && u.scheme.length == 1) || u.path == uri.gsub(/ /, '+') # file path
    # When the Windows absolute path then u.scheme.length == 1
    # e.g. C:
    path = URI.decode_www_form_component(u.path)
    if path[0] != ?/
      pattern = File.expand_path("#{@include_basepath}/#{path}")
    else
      pattern = path
    end
    Dir.glob(pattern).sort.each { |entry|
      basepath = File.dirname(entry)
      fname = File.basename(entry)
      data = File.read(entry)
      data.force_encoding('UTF-8')
      ss = StringScanner.new(data)
      V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
    }
  else
    require 'open-uri'
    basepath = '/'
    fname = path
    data = URI.open(uri) { |f| f.read }
    data.force_encoding('UTF-8')
    ss = StringScanner.new(data)
    V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
  end
rescue SystemCallError => e
  cpe = ConfigParseError.new("include error #{uri} - #{e}")
  cpe.set_backtrace(e.backtrace)
  raise cpe
end

#parse!Object



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/fluent/config/v1_parser.rb', line 43

def parse!
  attrs, elems = parse_element(true, nil)
  root = Element.new('ROOT', '', attrs, elems)
  root.v1_config = true

  spacing
  unless eof?
    parse_error! "expected EOF"
  end

  return root
end

#parse_element(root_element, elem_name, attrs = {}, elems = []) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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
# File 'lib/fluent/config/v1_parser.rb', line 59

def parse_element(root_element, elem_name, attrs = {}, elems = [])
  while true
    spacing
    if eof?
      if root_element
        break
      end
      parse_error! "expected end tag '</#{elem_name}>' but got end of file"
    end

    if skip(/\<\//)
      e_name = scan(ELEMENT_NAME)
      spacing
      unless skip(/\>/)
        parse_error! "expected character in tag name"
      end
      unless line_end
        parse_error! "expected end of line after end tag"
      end
      if e_name != elem_name
        parse_error! "unmatched end tag"
      end
      break

    elsif skip(/\</)
      e_name = scan(ELEMENT_NAME)
      spacing
      e_arg = scan_string(/(?:#{ZERO_OR_MORE_SPACING}\>)/)
      spacing
      unless skip(/\>/)
        parse_error! "expected '>'"
      end
      unless line_end
        parse_error! "expected end of line after tag"
      end
      e_arg ||= ''
      # call parse_element recursively
      e_attrs, e_elems = parse_element(false, e_name)
      new_e = Element.new(e_name, e_arg, e_attrs, e_elems)
      new_e.v1_config = true
      elems << new_e

    elsif root_element && skip(/(\@include|include)#{SPACING}/)
      if !prev_match.start_with?('@')
        @logger.warn "'include' is deprecated. Use '@include' instead" if @logger
      end
      parse_include(attrs, elems)

    else
      k = scan_string(SPACING)
      spacing_without_comment
      if prev_match.include?("\n") || eof? # support 'tag_mapped' like "without value" configuration
        attrs[k] = ""
      else
        if k == '@include'
          parse_include(attrs, elems)
        elsif RESERVED_PARAMS.include?(k)
          v = parse_literal
          unless line_end
            parse_error! "expected end of line"
          end
          attrs[k] = v
        else
          if k.start_with?('@')
            if root_element || ELEM_SYMBOLS.include?(elem_name)
              parse_error! "'@' is the system reserved prefix. Don't use '@' prefix parameter in the configuration: #{k}"
            else
              # TODO: This is for backward compatibility. It will throw an error in the future.
              @logger.warn "'@' is the system reserved prefix. It works in the nested configuration for now but it will be rejected: #{k}" if @logger
            end
          end

          v = parse_literal
          unless line_end
            parse_error! "expected end of line"
          end
          attrs[k] = v
        end
      end
    end
  end

  return attrs, elems
end

#parse_include(attrs, elems) ⇒ Object



144
145
146
147
148
# File 'lib/fluent/config/v1_parser.rb', line 144

def parse_include(attrs, elems)
  uri = scan_string(LINE_END)
  eval_include(attrs, elems, uri)
  line_end
end