forked from fluent/fluentd
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparser_csv.rb
More file actions
119 lines (103 loc) · 3.43 KB
/
Copy pathparser_csv.rb
File metadata and controls
119 lines (103 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
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
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'fluent/plugin/parser'
require 'csv'
module Fluent
module Plugin
class CSVParser < Parser
Plugin.register_parser('csv', self)
desc 'Names of fields included in each lines'
config_param :keys, :array, value_type: :string
desc 'The delimiter character (or string) of CSV values'
config_param :delimiter, :string, default: ','
desc 'The parser type used to parse CSV line'
config_param :parser_engine, :enum, list: [:normal, :fast], default: :normal, alias: :parser_type
def configure(conf)
super
if @parser_engine == :fast
@quote_char = '"'
@escape_pattern = Regexp.compile(@quote_char * 2)
m = method(:parse_fast)
self.singleton_class.module_eval do
define_method(:parse, m)
end
end
end
def parse(text, &block)
values = CSV.parse_line(text, col_sep: @delimiter)
unless values
yield nil, nil
return
end
r = Hash[@keys.zip(values)]
time, record = convert_values(parse_time(r), r)
yield time, record
end
def parse_fast(text, &block)
r = parse_fast_internal(text)
time, record = convert_values(parse_time(r), r)
yield time, record
end
# CSV.parse_line is too slow due to initialize lots of object and
# CSV module doesn't provide the efficient method for parsing single line.
# This method avoids the overhead of CSV.parse_line for typical patterns
def parse_fast_internal(text)
record = {}
text.chomp!
return record if text.empty?
# use while because while is now faster than each_with_index
columns = text.split(@delimiter, -1)
num_columns = columns.size
i = 0
j = 0
while j < num_columns
column = columns[j]
case column.count(@quote_char)
when 0
if column.empty?
column = nil
end
when 1
if column.start_with?(@quote_char)
to_merge = [column]
j += 1
while j < num_columns
merged_col = columns[j]
to_merge << merged_col
break if merged_col.end_with?(@quote_char)
j += 1
end
column = to_merge.join(@delimiter)[1..-2]
end
when 2
if column.start_with?(@quote_char) && column.end_with?(@quote_char)
column = column[1..-2]
end
else
if column.start_with?(@quote_char) && column.end_with?(@quote_char)
column = column[1..-2]
end
column.gsub!(@escape_pattern, @quote_char)
end
record[@keys[i]] = column
j += 1
i += 1
end
record
end
end
end
end