fluentd 0.12.0.pre.2 → 0.12.0.pre.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fluentd might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/example/v0_12_filter.conf +78 -0
- data/fluentd.gemspec +2 -1
- data/lib/fluent/agent.rb +2 -1
- data/lib/fluent/buffer.rb +9 -5
- data/lib/fluent/command/fluentd.rb +4 -0
- data/lib/fluent/config/basic_parser.rb +1 -0
- data/lib/fluent/config/configure_proxy.rb +7 -7
- data/lib/fluent/config/types.rb +1 -0
- data/lib/fluent/config/v1_parser.rb +1 -1
- data/lib/fluent/engine.rb +0 -25
- data/lib/fluent/env.rb +1 -0
- data/lib/fluent/event_router.rb +6 -2
- data/lib/fluent/filter.rb +12 -1
- data/lib/fluent/formatter.rb +85 -16
- data/lib/fluent/label.rb +4 -0
- data/lib/fluent/output.rb +1 -0
- data/lib/fluent/parser.rb +25 -23
- data/lib/fluent/plugin.rb +18 -0
- data/lib/fluent/plugin/buf_file.rb +1 -1
- data/lib/fluent/plugin/in_dummy.rb +103 -0
- data/lib/fluent/plugin/in_http.rb +30 -10
- data/lib/fluent/plugin/in_syslog.rb +4 -4
- data/lib/fluent/plugin/in_tail.rb +6 -6
- data/lib/fluent/plugin/out_file.rb +3 -3
- data/lib/fluent/plugin/socket_util.rb +2 -2
- data/lib/fluent/registry.rb +9 -27
- data/lib/fluent/root_agent.rb +26 -7
- data/lib/fluent/supervisor.rb +40 -27
- data/lib/fluent/test.rb +1 -0
- data/lib/fluent/test/base.rb +14 -0
- data/lib/fluent/test/filter_test.rb +33 -0
- data/lib/fluent/test/output_test.rb +7 -1
- data/lib/fluent/version.rb +1 -1
- data/test/config/test_config_parser.rb +6 -2
- data/test/config/test_configurable.rb +1 -1
- data/test/config/test_configure_proxy.rb +1 -1
- data/test/config/test_dsl.rb +1 -1
- data/test/config/test_literal_parser.rb +2 -2
- data/test/config/test_section.rb +1 -1
- data/test/config/test_system_config.rb +65 -15
- data/test/config/test_types.rb +63 -0
- data/test/helper.rb +2 -1
- data/test/plugin/test_buf_file.rb +1 -1
- data/test/plugin/test_buf_memory.rb +1 -1
- data/test/plugin/test_filter_grep.rb +17 -23
- data/test/plugin/test_filter_record_transformer.rb +18 -21
- data/test/plugin/test_in_dummy.rb +95 -0
- data/test/plugin/test_in_exec.rb +1 -1
- data/test/plugin/test_in_forward.rb +1 -1
- data/test/plugin/test_in_gc_stat.rb +1 -1
- data/test/plugin/test_in_http.rb +1 -1
- data/test/plugin/test_in_object_space.rb +1 -1
- data/test/plugin/test_in_status.rb +1 -1
- data/test/plugin/test_in_stream.rb +1 -1
- data/test/plugin/test_in_syslog.rb +1 -1
- data/test/plugin/test_in_tail.rb +1 -1
- data/test/plugin/test_in_tcp.rb +1 -1
- data/test/plugin/test_in_udp.rb +1 -1
- data/test/plugin/test_out_copy.rb +12 -1
- data/test/plugin/test_out_exec.rb +1 -1
- data/test/plugin/test_out_exec_filter.rb +1 -1
- data/test/plugin/test_out_file.rb +61 -8
- data/test/plugin/test_out_forward.rb +1 -1
- data/test/plugin/test_out_roundrobin.rb +12 -1
- data/test/plugin/test_out_stdout.rb +1 -1
- data/test/plugin/test_out_stream.rb +1 -1
- data/test/scripts/fluent/plugin/formatter_known.rb +4 -1
- data/{lib → test/scripts}/fluent/plugin/out_test.rb +0 -0
- data/test/scripts/fluent/plugin/parser_known.rb +2 -1
- data/test/test_buffer.rb +1 -1
- data/test/test_config.rb +1 -1
- data/test/test_configdsl.rb +1 -1
- data/test/test_event_router.rb +233 -0
- data/test/test_formatter.rb +160 -3
- data/test/test_input.rb +30 -0
- data/test/test_match.rb +100 -77
- data/test/test_mixin.rb +1 -1
- data/test/test_output.rb +1 -1
- data/test/test_parser.rb +5 -3
- data/test/test_plugin_classes.rb +60 -0
- metadata +32 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5a6a2cf3f13289f87a554bfafb531593977ffcd
|
4
|
+
data.tar.gz: bbb612c523e612b177980433bcc86cb7a6571d90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea0f0eeb1718e00caef76ae713101292daa2019cab3af19a2e26c4f2d2c74920026757e729b5e57b8f60f98f366035211e206a8f9d97849c3f0bfc0fad8ccdc5
|
7
|
+
data.tar.gz: b44314b0bcd226f4666b1079b297ad6f67a646b6be0f22a4e855e5176caac17cdf7b792a2b760bd0ba05184146cacdacc121f643661f41c41e25ee7d9632b5e3
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# An example config to use filter plugins.
|
2
|
+
# THIS FEATURE IS SUPPORTED FOR v0.12 AND ABOVE.
|
3
|
+
|
4
|
+
# in_forward to generate events to be tested.
|
5
|
+
# You can send an arbitrary event with an arbitrary tag.
|
6
|
+
# For example, the following command creates the event
|
7
|
+
# {"message":"hello world"} with tag = foo
|
8
|
+
#
|
9
|
+
# $ echo '{"message":"hello world"}' | fluent-cat foo
|
10
|
+
|
11
|
+
<source>
|
12
|
+
type forward
|
13
|
+
port 24224
|
14
|
+
</source>
|
15
|
+
|
16
|
+
# For all events with the tag "foo", filter it out
|
17
|
+
# UNLESS the value of the "message" field matches /keep this/
|
18
|
+
#
|
19
|
+
# - {"message":"keep this please"} is kept.
|
20
|
+
# - {"message":"Do not keep"} is filtered out.
|
21
|
+
# - {"messag22":"keep this please"} is filtered out.
|
22
|
+
|
23
|
+
<filter foo>
|
24
|
+
type grep
|
25
|
+
regexp1 message keep this
|
26
|
+
</filter>
|
27
|
+
|
28
|
+
# Matches the events that was kept by the above filter
|
29
|
+
<match foo>
|
30
|
+
type stdout
|
31
|
+
</match>
|
32
|
+
|
33
|
+
# For all events with the tag "bar", add the machine's hostname with
|
34
|
+
# the key "hostname" BEFORE forwarding to another instance of Fluentd
|
35
|
+
# at 123.4.2.4:24224.
|
36
|
+
|
37
|
+
<filter bar>
|
38
|
+
type record_transformer
|
39
|
+
<record>
|
40
|
+
hostname ${hostname}
|
41
|
+
</record>
|
42
|
+
</filter>
|
43
|
+
|
44
|
+
# By the time it is getting matched here, the event has
|
45
|
+
# the "hostname" field.
|
46
|
+
<match bar>
|
47
|
+
type forward
|
48
|
+
<server>
|
49
|
+
host 123.4.2.4
|
50
|
+
port 24225
|
51
|
+
</server>
|
52
|
+
</match>
|
53
|
+
|
54
|
+
# Composing two filters. For all events with the tag foo.bar,
|
55
|
+
#
|
56
|
+
# 1. The first filter filters out all events that has the field "hello"
|
57
|
+
# 2. Then, for those events, we downcase the value of the "name" field.
|
58
|
+
#
|
59
|
+
# - {"name":"SADA", "hello":100} gets filtered out
|
60
|
+
# - {"name":"SADA"} becomes {"name":"sada"}
|
61
|
+
# - {"last_name":"FURUHASHI"} throws an error because it has no field called "name"
|
62
|
+
|
63
|
+
<filter foo.bar>
|
64
|
+
type grep
|
65
|
+
exclude1 hello .
|
66
|
+
</filter>
|
67
|
+
|
68
|
+
<filter foo.bar>
|
69
|
+
type record_transformer
|
70
|
+
enable_ruby true
|
71
|
+
<record>
|
72
|
+
name ${name.downcase}
|
73
|
+
</record>
|
74
|
+
</filter>
|
75
|
+
|
76
|
+
<match foo.bar>
|
77
|
+
type stdout
|
78
|
+
</match>
|
data/fluentd.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ["lib"]
|
17
17
|
gem.has_rdoc = false
|
18
18
|
|
19
|
-
gem.required_ruby_version = '>= 1.9.
|
19
|
+
gem.required_ruby_version = '>= 1.9.3'
|
20
20
|
|
21
21
|
gem.add_runtime_dependency("msgpack", [">= 0.5.4", "< 0.6.0"])
|
22
22
|
gem.add_runtime_dependency("json", [">= 1.4.3"])
|
@@ -35,4 +35,5 @@ Gem::Specification.new do |gem|
|
|
35
35
|
gem.add_development_dependency("rr", [">= 1.0.0"])
|
36
36
|
gem.add_development_dependency("timecop", [">= 0.3.0"])
|
37
37
|
gem.add_development_dependency("test-unit", ["~> 3.0.2"])
|
38
|
+
gem.add_development_dependency("test-unit-rr", ["~> 1.0.3"])
|
38
39
|
end
|
data/lib/fluent/agent.rb
CHANGED
@@ -37,7 +37,7 @@ module Fluent
|
|
37
37
|
@started_filters = []
|
38
38
|
|
39
39
|
@log = Engine.log
|
40
|
-
@event_router = EventRouter.new(
|
40
|
+
@event_router = EventRouter.new(NoMatchMatch.new(log), self)
|
41
41
|
@error_collector = nil
|
42
42
|
end
|
43
43
|
|
@@ -137,6 +137,7 @@ module Fluent
|
|
137
137
|
log.info "adding filter#{@context.nil? ? '' : " in #{@context}"}", pattern: pattern, type: type
|
138
138
|
|
139
139
|
filter = Plugin.new_filter(type)
|
140
|
+
filter.router = @event_router
|
140
141
|
filter.configure(conf)
|
141
142
|
@filters << filter
|
142
143
|
@event_router.add_rule(pattern, filter)
|
data/lib/fluent/buffer.rb
CHANGED
@@ -237,11 +237,15 @@ module Fluent
|
|
237
237
|
|
238
238
|
def total_queued_chunk_size
|
239
239
|
total = 0
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
240
|
+
synchronize {
|
241
|
+
@map.each_value {|c|
|
242
|
+
total += c.size
|
243
|
+
}
|
244
|
+
@queue.synchronize {
|
245
|
+
@queue.each {|c|
|
246
|
+
total += c.size
|
247
|
+
}
|
248
|
+
}
|
245
249
|
}
|
246
250
|
total
|
247
251
|
end
|
@@ -50,6 +50,10 @@ op.on('-d', '--daemon PIDFILE', "daemonize fluent process") {|s|
|
|
50
50
|
opts[:daemonize] = s
|
51
51
|
}
|
52
52
|
|
53
|
+
op.on('--no-supervisor', "run without fluent supervisor") {
|
54
|
+
opts[:supervise] = false
|
55
|
+
}
|
56
|
+
|
53
57
|
op.on('--user USER', "change user") {|s|
|
54
58
|
opts[:chuser] = s
|
55
59
|
}
|
@@ -84,13 +84,13 @@ module Fluent
|
|
84
84
|
elsif a.is_a?(Hash)
|
85
85
|
opts.merge!(a)
|
86
86
|
else
|
87
|
-
raise ArgumentError, "wrong number of arguments (#{1 + args.length} for #{block ? 2 : 3})"
|
87
|
+
raise ArgumentError, "#{self.name}: wrong number of arguments (#{1 + args.length} for #{block ? 2 : 3})"
|
88
88
|
end
|
89
89
|
}
|
90
90
|
|
91
91
|
type = opts[:type]
|
92
92
|
if block && type
|
93
|
-
raise ArgumentError, "both of block and type cannot be specified"
|
93
|
+
raise ArgumentError, "#{self.name}: both of block and type cannot be specified"
|
94
94
|
end
|
95
95
|
|
96
96
|
begin
|
@@ -98,7 +98,7 @@ module Fluent
|
|
98
98
|
block ||= Configurable.lookup_type(type)
|
99
99
|
rescue ConfigError
|
100
100
|
# override error message
|
101
|
-
raise ArgumentError, "unknown config_argument type `#{type}'"
|
101
|
+
raise ArgumentError, "#{self.name}: unknown config_argument type `#{type}'"
|
102
102
|
end
|
103
103
|
|
104
104
|
if opts.has_key?(:default)
|
@@ -110,7 +110,7 @@ module Fluent
|
|
110
110
|
|
111
111
|
def config_argument(name, *args, &block)
|
112
112
|
if @argument
|
113
|
-
raise ArgumentError, "config_argument called twice"
|
113
|
+
raise ArgumentError, "#{self.name}: config_argument called twice"
|
114
114
|
end
|
115
115
|
name, block, opts = parameter_configuration(name, *args, &block)
|
116
116
|
|
@@ -130,7 +130,7 @@ module Fluent
|
|
130
130
|
name = name.to_sym
|
131
131
|
|
132
132
|
if @defaults.has_key?(name)
|
133
|
-
raise ArgumentError, "default value specified twice for #{name}"
|
133
|
+
raise ArgumentError, "#{self.name}: default value specified twice for #{name}"
|
134
134
|
end
|
135
135
|
|
136
136
|
@defaults[name] = defval
|
@@ -139,13 +139,13 @@ module Fluent
|
|
139
139
|
|
140
140
|
def config_section(name, *args, &block)
|
141
141
|
unless block_given?
|
142
|
-
raise ArgumentError, "config_section requires block parameter"
|
142
|
+
raise ArgumentError, "#{self.name}: config_section requires block parameter"
|
143
143
|
end
|
144
144
|
name = name.to_sym
|
145
145
|
|
146
146
|
opts = {}
|
147
147
|
unless args.empty? || args.size == 1 && args.first.is_a?(Hash)
|
148
|
-
raise ArgumentError, "unknown config_section arguments: #{args.inspect}"
|
148
|
+
raise ArgumentError, "#{self.name}: unknown config_section arguments: #{args.inspect}"
|
149
149
|
end
|
150
150
|
|
151
151
|
sub_proxy = ConfigureProxy.new(name, *args)
|
data/lib/fluent/config/types.rb
CHANGED
data/lib/fluent/engine.rb
CHANGED
@@ -207,29 +207,4 @@ module Fluent
|
|
207
207
|
end
|
208
208
|
|
209
209
|
Engine = EngineClass.new
|
210
|
-
|
211
|
-
module Test
|
212
|
-
@@test = false
|
213
|
-
|
214
|
-
def test?
|
215
|
-
@@test
|
216
|
-
end
|
217
|
-
|
218
|
-
def self.setup
|
219
|
-
@@test = true
|
220
|
-
|
221
|
-
Fluent.__send__(:remove_const, :Engine)
|
222
|
-
engine = Fluent.const_set(:Engine, EngineClass.new).init
|
223
|
-
|
224
|
-
engine.define_singleton_method(:now=) {|n|
|
225
|
-
@now = n.to_i
|
226
|
-
}
|
227
|
-
engine.define_singleton_method(:now) {
|
228
|
-
@now || super()
|
229
|
-
}
|
230
|
-
|
231
|
-
nil
|
232
|
-
end
|
233
|
-
end
|
234
210
|
end
|
235
|
-
|
data/lib/fluent/env.rb
CHANGED
data/lib/fluent/event_router.rb
CHANGED
@@ -38,7 +38,7 @@ module Fluent
|
|
38
38
|
# Collector is either of Output, Filter or other EventRouter.
|
39
39
|
#
|
40
40
|
class EventRouter
|
41
|
-
def initialize(
|
41
|
+
def initialize(default_collector, emit_error_handler)
|
42
42
|
@match_rules = []
|
43
43
|
@match_cache = MatchCache.new
|
44
44
|
@default_collector = default_collector
|
@@ -73,7 +73,7 @@ module Fluent
|
|
73
73
|
end
|
74
74
|
|
75
75
|
attr_reader :collector
|
76
|
-
attr_reader :
|
76
|
+
attr_reader :pattern_str
|
77
77
|
end
|
78
78
|
|
79
79
|
# called by Agent to add new match pattern and collector
|
@@ -97,6 +97,10 @@ module Fluent
|
|
97
97
|
@emit_error_handler.handle_emits_error(tag, es, e)
|
98
98
|
end
|
99
99
|
|
100
|
+
def emit_error_event(tag, time, record, error)
|
101
|
+
@emit_error_handler.emit_error_event(tag, time, record, error)
|
102
|
+
end
|
103
|
+
|
100
104
|
def match?(tag)
|
101
105
|
!!find(tag)
|
102
106
|
end
|
data/lib/fluent/filter.rb
CHANGED
@@ -4,12 +4,19 @@ module Fluent
|
|
4
4
|
include PluginId
|
5
5
|
include PluginLoggerMixin
|
6
6
|
|
7
|
+
attr_accessor :router
|
8
|
+
|
7
9
|
def initialize
|
8
10
|
super
|
9
11
|
end
|
10
12
|
|
11
13
|
def configure(conf)
|
12
14
|
super
|
15
|
+
|
16
|
+
if label_name = conf['@label']
|
17
|
+
label = Engine.root_agent.find_label(label_name)
|
18
|
+
@router = label.event_router
|
19
|
+
end
|
13
20
|
end
|
14
21
|
|
15
22
|
def start
|
@@ -24,7 +31,11 @@ module Fluent
|
|
24
31
|
def filter_stream(tag, es)
|
25
32
|
new_es = MultiEventStream.new
|
26
33
|
es.each { |time, record|
|
27
|
-
|
34
|
+
begin
|
35
|
+
new_es.add(time, filter(tag, time, record))
|
36
|
+
rescue => e
|
37
|
+
router.emit_error_event(tag, time, record, e)
|
38
|
+
end
|
28
39
|
}
|
29
40
|
new_es
|
30
41
|
end
|
data/lib/fluent/formatter.rb
CHANGED
@@ -77,11 +77,12 @@ module Fluent
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
module StructuredFormatMixin
|
81
|
+
def self.included(klass)
|
82
|
+
klass.instance_eval {
|
83
|
+
config_param :time_as_epoch, :bool, :default => false
|
84
|
+
}
|
85
|
+
end
|
85
86
|
|
86
87
|
def configure(conf)
|
87
88
|
super
|
@@ -99,10 +100,30 @@ module Fluent
|
|
99
100
|
def format(tag, time, record)
|
100
101
|
filter_record(tag, time, record)
|
101
102
|
record[@time_key] = time if @time_as_epoch
|
103
|
+
format_record(record)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class JSONFormatter
|
108
|
+
include Configurable
|
109
|
+
include HandleTagAndTimeMixin
|
110
|
+
include StructuredFormatMixin
|
111
|
+
|
112
|
+
def format_record(record)
|
102
113
|
"#{Yajl.dump(record)}\n"
|
103
114
|
end
|
104
115
|
end
|
105
116
|
|
117
|
+
class MessagePackFormatter
|
118
|
+
include Configurable
|
119
|
+
include HandleTagAndTimeMixin
|
120
|
+
include StructuredFormatMixin
|
121
|
+
|
122
|
+
def format_record(record)
|
123
|
+
record.to_msgpack
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
106
127
|
class LabeledTSVFormatter
|
107
128
|
include Configurable
|
108
129
|
include HandleTagAndTimeMixin
|
@@ -121,6 +142,37 @@ module Fluent
|
|
121
142
|
end
|
122
143
|
end
|
123
144
|
|
145
|
+
class CsvFormatter
|
146
|
+
include Configurable
|
147
|
+
include HandleTagAndTimeMixin
|
148
|
+
|
149
|
+
config_param :delimiter, :default => ',' do |val|
|
150
|
+
['\t', 'TAB'].include?(val) ? "\t" : val
|
151
|
+
end
|
152
|
+
config_param :force_quotes, :bool, :default => true
|
153
|
+
config_param :fields, :default => [] do |val|
|
154
|
+
val.split(',').map do |f|
|
155
|
+
f.strip!
|
156
|
+
f.size > 0 ? f : nil
|
157
|
+
end.compact
|
158
|
+
end
|
159
|
+
|
160
|
+
def initialize
|
161
|
+
super
|
162
|
+
require 'csv'
|
163
|
+
end
|
164
|
+
|
165
|
+
def format(tag, time, record)
|
166
|
+
filter_record(tag, time, record)
|
167
|
+
row = @fields.inject([]) do |memo, key|
|
168
|
+
memo << record[key]
|
169
|
+
memo
|
170
|
+
end
|
171
|
+
CSV.generate_line(row, :col_sep => @delimiter,
|
172
|
+
:force_quotes => @force_quotes)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
124
176
|
class SingleValueFormatter
|
125
177
|
include Configurable
|
126
178
|
|
@@ -134,41 +186,58 @@ module Fluent
|
|
134
186
|
end
|
135
187
|
end
|
136
188
|
|
189
|
+
class ProcWrappedFormatter
|
190
|
+
def initialize(proc)
|
191
|
+
@proc = proc
|
192
|
+
end
|
193
|
+
|
194
|
+
def configure(conf)
|
195
|
+
end
|
196
|
+
|
197
|
+
def format(tag, time, record)
|
198
|
+
@proc.call(tag, time, record)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
137
202
|
TEMPLATE_REGISTRY = Registry.new(:formatter_type, 'fluent/plugin/formatter_')
|
138
203
|
{
|
139
204
|
'out_file' => Proc.new { OutFileFormatter.new },
|
140
205
|
'json' => Proc.new { JSONFormatter.new },
|
206
|
+
'msgpack' => Proc.new { MessagePackFormatter.new },
|
141
207
|
'ltsv' => Proc.new { LabeledTSVFormatter.new },
|
208
|
+
'csv' => Proc.new { CsvFormatter.new },
|
142
209
|
'single_value' => Proc.new { SingleValueFormatter.new },
|
143
210
|
}.each { |name, factory|
|
144
211
|
TEMPLATE_REGISTRY.register(name, factory)
|
145
212
|
}
|
146
213
|
|
147
214
|
def self.register_template(name, factory_or_proc)
|
148
|
-
factory = if factory_or_proc.
|
149
|
-
Proc.new { factory_or_proc }
|
150
|
-
|
215
|
+
factory = if factory_or_proc.is_a?(Class) # XXXFormatter
|
216
|
+
Proc.new { factory_or_proc.new }
|
217
|
+
elsif factory_or_proc.arity == 3 # Proc.new { |tag, time, record| }
|
218
|
+
Proc.new { ProcWrappedFormatter.new(factory_or_proc) }
|
219
|
+
else # Proc.new { XXXFormatter.new }
|
151
220
|
factory_or_proc
|
152
221
|
end
|
153
222
|
|
154
223
|
TEMPLATE_REGISTRY.register(name, factory)
|
155
224
|
end
|
156
225
|
|
226
|
+
def self.lookup(format)
|
227
|
+
TEMPLATE_REGISTRY.lookup(format).call
|
228
|
+
end
|
229
|
+
|
230
|
+
# Keep backward-compatibility
|
157
231
|
def self.create(conf)
|
158
232
|
format = conf['format']
|
159
233
|
if format.nil?
|
160
234
|
raise ConfigError, "'format' parameter is required"
|
161
235
|
end
|
162
236
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
rescue ConfigError => e
|
167
|
-
raise ConfigError, "unknown format: '#{format}'"
|
237
|
+
formatter = lookup(format)
|
238
|
+
if formatter.respond_to?(:configure)
|
239
|
+
formatter.configure(conf)
|
168
240
|
end
|
169
|
-
|
170
|
-
formatter = factory.call
|
171
|
-
formatter.configure(conf)
|
172
241
|
formatter
|
173
242
|
end
|
174
243
|
end
|