fluent-plugin-azurestorage 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 776476b29eb416de1bf483aaf7f2a7a864ed10fe
4
- data.tar.gz: 95cdf6fa62d1f4e772179694a1a3f7a611ee3149
3
+ metadata.gz: 19fc61140e33ed5db2e6f41736d8a41515fd2eab
4
+ data.tar.gz: c83005cb236f4b19a8c547945930e4783bd02366
5
5
  SHA512:
6
- metadata.gz: cec4a7f65d722e23bb49e46211c482b10f7c3e9f15e0ed286b960599aac7034a5d110db122ab072ace1ea056a820b68d7e2f55a5b9033f6252a6ba71710718cc
7
- data.tar.gz: b21756a7d13066e1155853ee3ed2bd9262ef24304483f0306c509f34538ae4ee9029efd0a1d161432f65520785c30e7033f35aeee9e10a68acaca710bc0cd6ad
6
+ metadata.gz: ca632bb038f91cf996ff63bb171bdda0e67baee3c6911513ef4f884862cb2fd0ea17161273518134eb952eb406b2561ae1b07968466e1cb190b112c7a7bbd65a
7
+ data.tar.gz: 12c019aa3ef994cd7089f5fe419e5ffd301226429054a85f18b94e3abf24b157c2427854755f772fa27fd726d70632fdbf21178c61fce7950253fb20bd36309f
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  Gemfile.lock
10
10
  vendor
11
11
  .ruby-version
12
+ *.komodoproject
data/README.md CHANGED
@@ -16,6 +16,48 @@ $ gem install fluent-plugin-azurestorage
16
16
 
17
17
  ## Configuration
18
18
 
19
+ ### v0.14 style
20
+
21
+ ```
22
+ <match pattern>
23
+ type azurestorage
24
+
25
+ azure_storage_account <your azure storage account>
26
+ azure_storage_access_key <your azure storage access key>
27
+ azure_container <your azure storage container>
28
+ azure_storage_type blob
29
+ store_as gzip
30
+ auto_create_container true
31
+ path logs/
32
+ azure_object_key_format %{path}%{time_slice}_%{index}.%{file_extension}
33
+ time_slice_format %Y%m%d-%H
34
+ # if you want to use ${tag} or %Y/%m/%d/ like syntax in path / s3_object_key_format,
35
+ # need to specify tag for ${tag} and time for %Y/%m/%d in <buffer> argument.
36
+ <buffer tag,time>
37
+ @type file
38
+ path /var/log/fluent/azurestorage
39
+ timekey 3600 # 1 hour partition
40
+ timekey_wait 10m
41
+ timekey_use_utc true # use utc
42
+ </buffer>
43
+ </match>
44
+ ```
45
+
46
+ For `<buffer>`, you can use any record field in `path` / `azure_object_key_format`.
47
+
48
+ ```
49
+ path logs/${tag}/${foo}
50
+ <buffer tag,foo>
51
+ # parameters...
52
+ </buffer>
53
+ ```
54
+
55
+ See official article for more detail: Buffer section configurations
56
+
57
+ Note that this configuration doesn't work with fluentd v0.12.
58
+
59
+ ### v0.12 style
60
+
19
61
  ```
20
62
  <match pattern>
21
63
  type azurestorage
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.8
1
+ 0.1.0
@@ -17,9 +17,9 @@ Gem::Specification.new do |gem|
17
17
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  gem.require_paths = ['lib']
19
19
 
20
- gem.add_dependency "fluentd", [">= 0.12.0", "< 2"]
20
+ gem.add_dependency "fluentd", [">= 0.14.0", "< 2"]
21
21
  gem.add_dependency "azure", [">= 0.7.1", "<= 0.7.7"]
22
- gem.add_dependency "fluent-mixin-config-placeholders", ">= 0.3.0"
22
+ gem.add_dependency "uuidtools", ">= 2.1.5"
23
23
  gem.add_development_dependency "rake", ">= 0.9.2"
24
24
  gem.add_development_dependency "test-unit", ">= 3.0.8"
25
25
  gem.add_development_dependency "test-unit-rr", ">= 1.0.3"
@@ -1,16 +1,18 @@
1
- module Fluent
2
- require 'fluent/mixin/config_placeholders'
3
-
4
- class AzureStorageOutput < Fluent::TimeSlicedOutput
1
+ require 'azure'
2
+ require 'fluent/plugin/upload_service'
3
+ require 'zlib'
4
+ require 'time'
5
+ require 'tempfile'
6
+ require 'fluent/plugin/output'
7
+
8
+ module Fluent::Plugin
9
+ class AzureStorageOutput < Fluent::Plugin::Output
5
10
  Fluent::Plugin.register_output('azurestorage', self)
6
11
 
12
+ helpers :compat_parameters, :formatter, :inject
13
+
7
14
  def initialize
8
15
  super
9
- require 'azure'
10
- require 'fluent/plugin/upload_service'
11
- require 'zlib'
12
- require 'time'
13
- require 'tempfile'
14
16
 
15
17
  @compressor = nil
16
18
  end
@@ -26,27 +28,32 @@ module Fluent
26
28
  config_param :format, :string, :default => "out_file"
27
29
  config_param :command_parameter, :string, :default => nil
28
30
 
29
- attr_reader :bs
31
+ DEFAULT_FORMAT_TYPE = "out_file"
30
32
 
31
- include Fluent::Mixin::ConfigPlaceholders
33
+ config_section :format do
34
+ config_set_default :@type, DEFAULT_FORMAT_TYPE
35
+ end
32
36
 
33
- def placeholders
34
- [:percent]
37
+ config_section :buffer do
38
+ config_set_default :chunk_keys, ['time']
39
+ config_set_default :timekey, (60 * 60 * 24)
35
40
  end
36
41
 
42
+ attr_reader :bs
43
+
37
44
  def configure(conf)
45
+ compat_parameters_convert(conf, :buffer, :formatter, :inject)
38
46
  super
39
47
 
40
48
  begin
41
49
  @compressor = COMPRESSOR_REGISTRY.lookup(@store_as).new(:buffer_type => @buffer_type, :log => log)
42
50
  rescue => e
43
- $log.warn "#{@store_as} not found. Use 'text' instead"
51
+ log.warn "#{@store_as} not found. Use 'text' instead"
44
52
  @compressor = TextCompressor.new
45
53
  end
46
54
  @compressor.configure(conf)
47
55
 
48
- @formatter = Plugin.new_formatter(@format)
49
- @formatter.configure(conf)
56
+ @formatter = formatter_create
50
57
 
51
58
  if @localtime
52
59
  @path_slicer = Proc.new {|path|
@@ -70,6 +77,13 @@ module Fluent
70
77
  else
71
78
  'blob'
72
79
  end
80
+ # For backward compatibility
81
+ # TODO: Remove time_slice_format when end of support compat_parameters
82
+ @configured_time_slice_format = conf['time_slice_format']
83
+ end
84
+
85
+ def multi_workers_ready?
86
+ true
73
87
  end
74
88
 
75
89
  def start
@@ -88,25 +102,32 @@ module Fluent
88
102
  end
89
103
 
90
104
  def format(tag, time, record)
91
- @formatter.format(tag, time, record)
105
+ r = inject_values_to_record(tag, time, record)
106
+ @formatter.format(tag, time, r)
92
107
  end
93
108
 
94
109
  def write(chunk)
95
110
  i = 0
111
+ metadata = chunk.metadata
96
112
  previous_path = nil
113
+ time_slice_format = @configured_time_slice_format || timekey_to_timeformat(@buffer_config['timekey'])
114
+ time_slice = if metadata.timekey.nil?
115
+ ''.freeze
116
+ else
117
+ Time.at(metadata.timekey).utc.strftime(time_slice_format)
118
+ end
97
119
 
98
120
  begin
99
121
  path = @path_slicer.call(@path)
100
122
  values_for_object_key = {
101
- "path" => path,
102
- "time_slice" => chunk.key,
103
- "file_extension" => @compressor.ext,
104
- "index" => i,
105
- "uuid_flush" => uuid_random
106
- }
107
- storage_path = @azure_object_key_format.gsub(%r(%{[^}]+})) { |expr|
108
- values_for_object_key[expr[2...expr.size-1]]
123
+ "%{path}" => path,
124
+ "%{time_slice}" => time_slice,
125
+ "%{file_extension}" => @compressor.ext,
126
+ "%{index}" => i,
127
+ "%{uuid_flush}" => uuid_random
109
128
  }
129
+ storage_path = @azure_object_key_format.gsub(%r(%{[^}]+}), values_for_object_key)
130
+ storage_path = extract_placeholders(storage_path, metadata)
110
131
  if (i > 0) && (storage_path == previous_path)
111
132
  raise "duplicated path is generated. use %{index} in azure_object_key_format: path = #{storage_path}"
112
133
  end
@@ -140,8 +161,24 @@ module Fluent
140
161
  end
141
162
  end
142
163
 
164
+ def uuid_random
165
+ require 'uuidtools'
166
+ ::UUIDTools::UUID.random_create.to_s
167
+ end
168
+
169
+ # This is stolen from Fluentd
170
+ def timekey_to_timeformat(timekey)
171
+ case timekey
172
+ when nil then ''
173
+ when 0...60 then '%Y%m%d%H%M%S' # 60 exclusive
174
+ when 60...3600 then '%Y%m%d%H%M'
175
+ when 3600...86400 then '%Y%m%d%H'
176
+ else '%Y%m%d'
177
+ end
178
+ end
179
+
143
180
  class Compressor
144
- include Configurable
181
+ include Fluent::Configurable
145
182
 
146
183
  def initialize(opts = {})
147
184
  super()
@@ -220,7 +257,7 @@ module Fluent
220
257
  end
221
258
  end
222
259
 
223
- COMPRESSOR_REGISTRY = Registry.new(:azurestorage_compressor_type, 'fluent/plugin/azurestorage_compressor_')
260
+ COMPRESSOR_REGISTRY = Fluent::Registry.new(:azurestorage_compressor_type, 'fluent/plugin/azurestorage_compressor_')
224
261
  {
225
262
  'gzip' => GzipCompressor,
226
263
  'json' => JsonCompressor,
@@ -1,10 +1,14 @@
1
1
  require 'fluent/test'
2
+ require 'fluent/test/driver/output'
3
+ require 'fluent/test/helpers'
2
4
  require 'fluent/plugin/out_azurestorage'
3
5
 
4
6
  require 'test/unit/rr'
5
7
  require 'zlib'
6
8
  require 'fileutils'
7
9
 
10
+ include Fluent::Test::Helpers
11
+
8
12
  class AzureStorageOutputTest < Test::Unit::TestCase
9
13
  def setup
10
14
  require 'azure'
@@ -21,9 +25,16 @@ class AzureStorageOutputTest < Test::Unit::TestCase
21
25
  ]
22
26
 
23
27
  def create_driver(conf = CONFIG)
24
- Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::AzureStorageOutput) do
28
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::AzureStorageOutput) do
29
+ # for testing.
30
+ def contents
31
+ @emit_streams
32
+ end
33
+
25
34
  def write(chunk)
26
- chunk.read
35
+ @emit_streams = []
36
+ event = chunk.read
37
+ @emit_streams << event
27
38
  end
28
39
 
29
40
  private
@@ -64,11 +75,9 @@ class AzureStorageOutputTest < Test::Unit::TestCase
64
75
  conf = CONFIG.clone
65
76
  conf << "\nstore_as lzo\n"
66
77
  d = create_driver(conf)
67
- assert_equal 'lzo', d.instance.instance_variable_get(:@compressor).ext
68
- assert_equal 'application/x-lzop', d.instance.instance_variable_get(:@compressor).content_type
69
- rescue => e
70
- # TODO: replace code with disable lzop command
71
- assert(e.is_a?(Fluent::ConfigError))
78
+ # Fallback to text/plain.
79
+ assert_equal 'txt', d.instance.instance_variable_get(:@compressor).ext
80
+ assert_equal 'text/plain', d.instance.instance_variable_get(:@compressor).content_type
72
81
  end
73
82
 
74
83
  def test_path_slicing
@@ -93,109 +102,118 @@ class AzureStorageOutputTest < Test::Unit::TestCase
93
102
  def test_format
94
103
  d = create_driver
95
104
 
96
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
97
- d.emit({"a"=>1}, time)
98
- d.emit({"a"=>2}, time)
99
-
100
- d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n]
101
- d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
105
+ time = event_time("2011-01-02 13:14:15 UTC")
106
+ d.run(default_tag: "test") do
107
+ d.feed(time, {"a"=>1})
108
+ d.feed(time, {"a"=>2})
109
+ end
110
+ formatted = d.formatted
102
111
 
103
- d.run
112
+ assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n], formatted[0]
113
+ assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n], formatted[1]
104
114
  end
105
115
 
106
116
  def test_format_included_tag_and_time
107
117
  config = [CONFIG, 'include_tag_key true', 'include_time_key true'].join("\n")
108
118
  d = create_driver(config)
109
119
 
110
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
111
- d.emit({"a"=>1}, time)
112
- d.emit({"a"=>2}, time)
120
+ time = event_time("2011-01-02 13:14:15 UTC")
121
+ d.run(default_tag: "test") do
122
+ d.feed(time, {"a"=>1})
123
+ d.feed(time, {"a"=>2})
124
+ end
125
+ formatted = d.formatted
113
126
 
114
- d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":1,"tag":"test","time":"2011-01-02T13:14:15Z"}\n]
115
- d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":2,"tag":"test","time":"2011-01-02T13:14:15Z"}\n]
116
-
117
- d.run
127
+ assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":1,"tag":"test","time":"2011-01-02T13:14:15Z"}\n], formatted[0]
128
+ assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":2,"tag":"test","time":"2011-01-02T13:14:15Z"}\n], d.formatted[1]
118
129
  end
119
130
 
120
131
  def test_format_with_format_ltsv
121
132
  config = [CONFIG, 'format ltsv'].join("\n")
122
133
  d = create_driver(config)
123
134
 
124
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
125
- d.emit({"a"=>1, "b"=>1}, time)
126
- d.emit({"a"=>2, "b"=>2}, time)
127
-
128
- d.expect_format %[a:1\tb:1\n]
129
- d.expect_format %[a:2\tb:2\n]
135
+ time = event_time("2011-01-02 13:14:15 UTC")
136
+ d.run(default_tag: "test") do
137
+ d.feed(time, {"a"=>1, "b"=>1})
138
+ d.feed(time, {"a"=>2, "b"=>2})
139
+ end
140
+ formatted = d.formatted
130
141
 
131
- d.run
142
+ assert_equal %[a:1\tb:1\n], formatted[0]
143
+ assert_equal %[a:2\tb:2\n], formatted[1]
132
144
  end
133
145
 
134
146
  def test_format_with_format_json
135
147
  config = [CONFIG, 'format json'].join("\n")
136
148
  d = create_driver(config)
137
149
 
138
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
139
- d.emit({"a"=>1}, time)
140
- d.emit({"a"=>2}, time)
141
-
142
- d.expect_format %[{"a":1}\n]
143
- d.expect_format %[{"a":2}\n]
150
+ time = event_time("2011-01-02 13:14:15 UTC")
151
+ d.run(default_tag: "test") do
152
+ d.feed(time, {"a"=>1})
153
+ d.feed(time, {"a"=>2})
154
+ end
155
+ formatted = d.formatted
144
156
 
145
- d.run
157
+ assert_equal %[{"a":1}\n], formatted[0]
158
+ assert_equal %[{"a":2}\n], formatted[1]
146
159
  end
147
160
 
148
161
  def test_format_with_format_json_included_tag
149
162
  config = [CONFIG, 'format json', 'include_tag_key true'].join("\n")
150
163
  d = create_driver(config)
151
164
 
152
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
153
- d.emit({"a"=>1}, time)
154
- d.emit({"a"=>2}, time)
165
+ time = event_time("2011-01-02 13:14:15 UTC")
166
+ d.run(default_tag: "test") do
167
+ d.feed(time, {"a"=>1})
168
+ d.feed(time, {"a"=>2})
169
+ end
170
+ formatted = d.formatted
155
171
 
156
- d.expect_format %[{"a":1,"tag":"test"}\n]
157
- d.expect_format %[{"a":2,"tag":"test"}\n]
158
-
159
- d.run
172
+ assert_equal %[{"a":1,"tag":"test"}\n], formatted[0]
173
+ assert_equal %[{"a":2,"tag":"test"}\n], formatted[1]
160
174
  end
161
175
 
162
176
  def test_format_with_format_json_included_time
163
177
  config = [CONFIG, 'format json', 'include_time_key true'].join("\n")
164
178
  d = create_driver(config)
165
179
 
166
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
167
- d.emit({"a"=>1}, time)
168
- d.emit({"a"=>2}, time)
169
-
170
- d.expect_format %[{"a":1,"time":"2011-01-02T13:14:15Z"}\n]
171
- d.expect_format %[{"a":2,"time":"2011-01-02T13:14:15Z"}\n]
180
+ time = event_time("2011-01-02 13:14:15 UTC")
181
+ d.run(default_tag: "test") do
182
+ d.feed(time, {"a"=>1})
183
+ d.feed(time, {"a"=>2})
184
+ end
185
+ formatted = d.formatted
172
186
 
173
- d.run
187
+ assert_equal %[{"a":1,"time":"2011-01-02T13:14:15Z"}\n], formatted[0]
188
+ assert_equal %[{"a":2,"time":"2011-01-02T13:14:15Z"}\n], formatted[1]
174
189
  end
175
190
 
176
191
  def test_format_with_format_json_included_tag_and_time
177
192
  config = [CONFIG, 'format json', 'include_tag_key true', 'include_time_key true'].join("\n")
178
193
  d = create_driver(config)
179
194
 
180
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
181
- d.emit({"a"=>1}, time)
182
- d.emit({"a"=>2}, time)
183
-
184
- d.expect_format %[{"a":1,"tag":"test","time":"2011-01-02T13:14:15Z"}\n]
185
- d.expect_format %[{"a":2,"tag":"test","time":"2011-01-02T13:14:15Z"}\n]
195
+ time = event_time("2011-01-02 13:14:15 UTC")
196
+ d.run(default_tag: "test") do
197
+ d.feed(time, {"a"=>1})
198
+ d.feed(time, {"a"=>2})
199
+ end
200
+ formatted = d.formatted
186
201
 
187
- d.run
202
+ assert_equal %[{"a":1,"tag":"test","time":"2011-01-02T13:14:15Z"}\n], formatted[0]
203
+ assert_equal %[{"a":2,"tag":"test","time":"2011-01-02T13:14:15Z"}\n], formatted[1]
188
204
  end
189
205
 
190
206
  def test_chunk_to_write
191
207
  d = create_driver
192
208
 
193
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
194
- d.emit({"a"=>1}, time)
195
- d.emit({"a"=>2}, time)
209
+ time = event_time("2011-01-02 13:14:15 UTC")
210
+ d.run(default_tag: "test") do
211
+ d.feed(time, {"a"=>1})
212
+ d.feed(time, {"a"=>2})
213
+ end
196
214
 
197
- # AzureStorageOutputTest#write returns chunk.read
198
- data = d.run
215
+ # Stubbed #write and #emit_streams returns chunk.read result.
216
+ data = d.instance.contents
199
217
 
200
218
  assert_equal [%[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] +
201
219
  %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]],
@@ -216,7 +234,7 @@ class AzureStorageOutputTest < Test::Unit::TestCase
216
234
  ]
217
235
 
218
236
  def create_time_sliced_driver(conf = CONFIG_TIME_SLICE)
219
- d = Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::AzureStorageOutput) do
237
+ d = Fluent::Test::Driver::Output.new(Fluent::Plugin::AzureStorageOutput) do
220
238
  end.configure(conf)
221
239
  d
222
240
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-azurestorage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hidemasa Togashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-23 00:00:00.000000000 Z
11
+ date: 2017-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.12.0
19
+ version: 0.14.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '2'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 0.12.0
29
+ version: 0.14.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '2'
@@ -51,19 +51,19 @@ dependencies:
51
51
  - !ruby/object:Gem::Version
52
52
  version: 0.7.7
53
53
  - !ruby/object:Gem::Dependency
54
- name: fluent-mixin-config-placeholders
54
+ name: uuidtools
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: 0.3.0
59
+ version: 2.1.5
60
60
  type: :runtime
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: 0.3.0
66
+ version: 2.1.5
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: rake
69
69
  requirement: !ruby/object:Gem::Requirement