fluent-logger 0.8.0 → 0.9.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 +5 -5
- data/.travis.yml +4 -20
- data/ChangeLog +13 -1
- data/README.md +69 -2
- data/lib/fluent/logger/fluent_logger.rb +65 -6
- data/lib/fluent/logger/version.rb +1 -1
- data/spec/fluent_logger_spec.rb +108 -0
- data/spec/support/dummy_fluentd.rb +18 -2
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 00161d7e89f54b0c1627bb5b0ce36a7689405b84da319db5c4052e585c09f6a7
|
4
|
+
data.tar.gz: fc7229efbe7fb40412fe22080aa974156f49f7d5e4257c0071d82eb7359cca72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1066053b93cf4173b29e063659d7f80bb42b77a116b85b2a18f3d43cf19c54538359336b4b120d0ff43b65cc58110ae8a3357a52f417a0379ea09c306e27b808
|
7
|
+
data.tar.gz: 8c7839a41b615ccf34ebcd656c1719ba99245c39abe9981969b8cb7d1a9cf57ad972dfb8276ebffe4c8c132d95d2a1dfc1db5b354bfc720b4a7c569722688a33
|
data/.travis.yml
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
rvm:
|
2
|
-
- 2.
|
3
|
-
- 2.
|
4
|
-
- 2.
|
5
|
-
- 2.
|
6
|
-
- 2.5.3
|
7
|
-
- 2.6.0
|
2
|
+
- 2.4.10
|
3
|
+
- 2.5
|
4
|
+
- 2.6
|
5
|
+
- 2.7
|
8
6
|
- ruby-head
|
9
7
|
|
10
8
|
gemfile:
|
11
9
|
- Gemfile
|
12
|
-
- Gemfile.v0.12
|
13
10
|
|
14
11
|
before_install: gem update bundler
|
15
12
|
script: bundle exec rake spec
|
@@ -19,16 +16,3 @@ sudo: false
|
|
19
16
|
matrix:
|
20
17
|
allow_failures:
|
21
18
|
- rvm: ruby-head
|
22
|
-
exclude:
|
23
|
-
- rvm: 2.2
|
24
|
-
gemfile: Gemfile.v0.12
|
25
|
-
- rvm: 2.3.7
|
26
|
-
gemfile: Gemfile.v0.12
|
27
|
-
- rvm: 2.4.5
|
28
|
-
gemfile: Gemfile.v0.12
|
29
|
-
- rvm: 2.5.3
|
30
|
-
gemfile: Gemfile.v0.12
|
31
|
-
- rvm: 2.6.0
|
32
|
-
gemfile: Gemfile.v0.12
|
33
|
-
- rvm: ruby-head
|
34
|
-
gemfile: Gemfile.v0.12
|
data/ChangeLog
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
Release 0.9.0 - 2020/09/04
|
2
|
+
|
3
|
+
* FluentLogger supports TLS
|
4
|
+
|
5
|
+
Release 0.8.2 - 2019/08/21
|
6
|
+
|
7
|
+
* Block timeout during IO#write to avoid writing invalid bytes
|
8
|
+
* Fluent::Logger::EventTime#to_json returns String
|
9
|
+
|
10
|
+
Release 0.8.1 - 2019/05/30
|
11
|
+
|
12
|
+
* Improve non-blocking write handling
|
13
|
+
|
1
14
|
Release 0.8.0 - 2019/01/25
|
2
15
|
|
3
16
|
* Add use_nonblock and wait_writeable parameters
|
@@ -130,4 +143,3 @@ Release 0.2.0 - 2011/08/05
|
|
130
143
|
Release 0.1.0 - 2011/08/04
|
131
144
|
|
132
145
|
* First release
|
133
|
-
|
data/README.md
CHANGED
@@ -4,6 +4,22 @@
|
|
4
4
|
|
5
5
|
A structured event logger
|
6
6
|
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'fluent-logger'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle install
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install fluent-logger
|
22
|
+
|
7
23
|
## Examples
|
8
24
|
|
9
25
|
### Simple
|
@@ -27,6 +43,7 @@ require 'fluent-logger'
|
|
27
43
|
|
28
44
|
log = Fluent::Logger::FluentLogger.new(nil, :socket_path => "/tmp/fluent.sock")
|
29
45
|
unless log.post("myapp.access", {"agent" => "foo"})
|
46
|
+
# Passed records are stored into logger's internal buffer so don't re-post same event.
|
30
47
|
p log.last_error # You can get last error object via last_error method
|
31
48
|
end
|
32
49
|
|
@@ -49,15 +66,52 @@ log.post("access", {"agent" => "foo"})
|
|
49
66
|
require 'fluent-logger'
|
50
67
|
|
51
68
|
log = Fluent::Logger::FluentLogger.new(nil, :host => 'localhost', :port => 24224, :use_nonblock => true, :wait_writeable => false)
|
69
|
+
# When wait_writeable is false
|
52
70
|
begin
|
53
71
|
log.post("myapp.access", {"agent" => "foo"})
|
54
72
|
rescue IO::EAGAINWaitWritable => e
|
55
73
|
# wait code for avoding "Resource temporarily unavailable"
|
74
|
+
# Passed records are stored into logger's internal buffer so don't re-post same event.
|
75
|
+
end
|
76
|
+
|
77
|
+
# When wait_writeable is true
|
78
|
+
unless log.post("myapp.access", {"agent" => "foo"})
|
79
|
+
# same as other example
|
56
80
|
end
|
57
81
|
|
58
82
|
# output: myapp.access {"agent":"foo"}
|
59
83
|
```
|
60
84
|
|
85
|
+
### TLS setting
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'fluent-logger'
|
89
|
+
|
90
|
+
tls_opts = {
|
91
|
+
:ca => '/path/to/cacert.pem',
|
92
|
+
:cert => '/path/to/client-cert.pem',
|
93
|
+
:key => '/path/to/client-key.pem',
|
94
|
+
:key_passphrase => 'test'
|
95
|
+
}
|
96
|
+
log = Fluent::Logger::FluentLogger.new(nil, :host => 'localhost', :port => 24224, :tls_options => tls_opts)
|
97
|
+
```
|
98
|
+
|
99
|
+
`in_forward` config example:
|
100
|
+
|
101
|
+
```
|
102
|
+
<source>
|
103
|
+
@type forward
|
104
|
+
<transport tcp>
|
105
|
+
version TLS1_2
|
106
|
+
ca_path /path/to/cacert.pem
|
107
|
+
cert_path /path/to/server-cert.pem
|
108
|
+
private_key_path /path/to/server-key.pem
|
109
|
+
private_key_passphrase test
|
110
|
+
client_cert_auth true
|
111
|
+
</transport>
|
112
|
+
</source>
|
113
|
+
```
|
114
|
+
|
61
115
|
### Singleton
|
62
116
|
```ruby
|
63
117
|
require 'fluent-logger'
|
@@ -88,16 +142,29 @@ Use nano second event time instead of epoch. See also "Tips" section.
|
|
88
142
|
|
89
143
|
#### use_nonblock (Bool)
|
90
144
|
|
91
|
-
Use nonblocking write(`IO#write_nonblock`) instead of normal write(`IO#write`). If `Logger#post` stuck on your environment, specify `true`.
|
145
|
+
Use nonblocking write(`IO#write_nonblock`) instead of normal write(`IO#write`). If `Logger#post` stuck on your environment, specify `true`. Default: `false`
|
92
146
|
|
93
147
|
#### wait_writeable (Bool)
|
94
148
|
|
95
|
-
If `
|
149
|
+
If `false`, `Logger#post` raises an error when nonblocking write gets `EAGAIN` (i.e. `use_nonblock` must be `true`, otherwise this will have no effect). Default: `true`
|
96
150
|
|
97
151
|
#### buffer_overflow_handler (Proc)
|
98
152
|
|
99
153
|
Pass callback for handling buffer overflow with pending data. See "Buffer overflow" section.
|
100
154
|
|
155
|
+
#### tls_options (Hash)
|
156
|
+
|
157
|
+
Pass TLS related options.
|
158
|
+
|
159
|
+
- use_default_ca: Set `true` if you want to use default CA
|
160
|
+
- ca: CA file path
|
161
|
+
- cert: Certificate file path
|
162
|
+
- key: Private key file path
|
163
|
+
- key_passphrase: Private key passphrase
|
164
|
+
- version: TLS version. Default is `OpenSSL::SSL::TLS1_2_VERSION`
|
165
|
+
- ciphers: The list of cipher suites. Default is `ALL:!aNULL:!eNULL:!SSLv2`
|
166
|
+
- insecure: Set `true` when `in_forward` uses `insecure true`
|
167
|
+
|
101
168
|
### Standard ::Logger compatible interface
|
102
169
|
|
103
170
|
#### Example1
|
@@ -15,8 +15,10 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
+
require 'timeout'
|
18
19
|
require 'msgpack'
|
19
20
|
require 'socket'
|
21
|
+
require 'openssl'
|
20
22
|
require 'monitor'
|
21
23
|
require 'logger'
|
22
24
|
require 'json'
|
@@ -44,7 +46,7 @@ module Fluent
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def to_json(*args)
|
47
|
-
@sec
|
49
|
+
@sec.to_s
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
@@ -83,6 +85,7 @@ module Fluent
|
|
83
85
|
@socket_path = options[:socket_path]
|
84
86
|
@nanosecond_precision = options[:nanosecond_precision]
|
85
87
|
@use_nonblock = options[:use_nonblock]
|
88
|
+
@tls_options = options[:tls_options]
|
86
89
|
|
87
90
|
@factory = MessagePack::Factory.new
|
88
91
|
if @nanosecond_precision
|
@@ -126,7 +129,6 @@ module Fluent
|
|
126
129
|
end
|
127
130
|
|
128
131
|
attr_accessor :limit, :logger, :log_reconnect_error_threshold
|
129
|
-
attr_reader :last_error
|
130
132
|
|
131
133
|
def last_error
|
132
134
|
@last_error[Thread.current.object_id]
|
@@ -169,6 +171,33 @@ module Fluent
|
|
169
171
|
@con = UNIXSocket.new(@socket_path)
|
170
172
|
else
|
171
173
|
@con = TCPSocket.new(@host, @port)
|
174
|
+
if @tls_options
|
175
|
+
context = OpenSSL::SSL::SSLContext.new
|
176
|
+
if @tls_options[:insecure]
|
177
|
+
context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
178
|
+
else
|
179
|
+
context.set_params({})
|
180
|
+
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
181
|
+
cert_store = OpenSSL::X509::Store.new
|
182
|
+
if @tls_options[:use_default_ca]
|
183
|
+
cert_store.set_default_paths
|
184
|
+
end
|
185
|
+
if @tls_options[:ca]
|
186
|
+
cert_store.add_file(@tls_options[:ca])
|
187
|
+
end
|
188
|
+
|
189
|
+
context.cert = OpenSSL::X509::Certificate.new(File.read(@tls_options[:cert])) if @tls_options[:cert]
|
190
|
+
context.key = OpenSSL::PKey::read(File.read(@tls_options[:key]), @tls_options[:key_passphrase]) if @tls_options[:key]
|
191
|
+
context.ciphers = @tls_options[:ciphers] || "ALL:!aNULL:!eNULL:!SSLv2".freeze
|
192
|
+
context.cert_store = cert_store
|
193
|
+
end
|
194
|
+
set_tls_version(context)
|
195
|
+
|
196
|
+
@con = OpenSSL::SSL::SSLSocket.new(@con, context)
|
197
|
+
@con.sync_close = true
|
198
|
+
@con.connect
|
199
|
+
end
|
200
|
+
@con
|
172
201
|
end
|
173
202
|
end
|
174
203
|
|
@@ -186,6 +215,16 @@ module Fluent
|
|
186
215
|
|
187
216
|
private
|
188
217
|
|
218
|
+
def set_tls_version(context)
|
219
|
+
if context.respond_to?(:min_version=)
|
220
|
+
ver = @tls_options[:version] || OpenSSL::SSL::TLS1_2_VERSION
|
221
|
+
context.min_version = ver
|
222
|
+
context.max_version = ver
|
223
|
+
else
|
224
|
+
context.ssl_version = @tls_options[:version] || :'TLSv1_2'
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
189
228
|
def to_msgpack(msg)
|
190
229
|
@mon.synchronize {
|
191
230
|
res = begin
|
@@ -230,7 +269,11 @@ module Fluent
|
|
230
269
|
end
|
231
270
|
|
232
271
|
begin
|
233
|
-
send_data(@pending)
|
272
|
+
written = send_data(@pending)
|
273
|
+
if @pending.bytesize != written
|
274
|
+
raise "Actual written data size(#{written} bytes) is different from the received data size(#{@pending.bytesize} bytes)."
|
275
|
+
end
|
276
|
+
|
234
277
|
@pending = nil
|
235
278
|
true
|
236
279
|
rescue => e
|
@@ -255,9 +298,13 @@ module Fluent
|
|
255
298
|
connect!
|
256
299
|
end
|
257
300
|
if @use_nonblock
|
258
|
-
|
301
|
+
send_data_nonblock(data)
|
259
302
|
else
|
260
|
-
@con
|
303
|
+
_, ws = IO.select([], [@con])
|
304
|
+
Thread.handle_interrupt(::Timeout::Error => :never) do
|
305
|
+
# block timeout error during IO#write
|
306
|
+
ws.first.write(data)
|
307
|
+
end
|
261
308
|
end
|
262
309
|
#while true
|
263
310
|
# puts "sending #{data.length} bytes"
|
@@ -272,7 +319,19 @@ module Fluent
|
|
272
319
|
# end
|
273
320
|
# data = data[n..-1]
|
274
321
|
#end
|
275
|
-
|
322
|
+
end
|
323
|
+
|
324
|
+
def send_data_nonblock(data)
|
325
|
+
written = @con.write_nonblock(data)
|
326
|
+
remaining = data.bytesize - written
|
327
|
+
|
328
|
+
while remaining > 0
|
329
|
+
len = @con.write_nonblock(data.byteslice(written, remaining))
|
330
|
+
remaining -= len
|
331
|
+
written += len
|
332
|
+
end
|
333
|
+
|
334
|
+
written
|
276
335
|
end
|
277
336
|
|
278
337
|
def connect!
|
data/spec/fluent_logger_spec.rb
CHANGED
@@ -58,6 +58,57 @@ describe Fluent::Logger::FluentLogger do
|
|
58
58
|
@serverengine.shutdown
|
59
59
|
end
|
60
60
|
|
61
|
+
describe('testing interaction of use_nonblock and wait_writeable') do
|
62
|
+
before(:example) do
|
63
|
+
allow_any_instance_of(TCPSocket).to receive(:write_nonblock).and_raise(IO::EAGAINWaitWritable)
|
64
|
+
allow_any_instance_of(TCPSocket).to receive(:write) { |_, buf| buf.size }
|
65
|
+
end
|
66
|
+
|
67
|
+
context('use_nonblock is false') do
|
68
|
+
let(:block_config) { logger_config.merge(use_nonblock: false) }
|
69
|
+
|
70
|
+
it('post returns true when wait_writeable is false') {
|
71
|
+
cfg = block_config.merge(wait_writeable: false)
|
72
|
+
l = Fluent::Logger::FluentLogger.new('logger-test', cfg)
|
73
|
+
expect(l.post('hello', foo: 'bar')).to eq true
|
74
|
+
}
|
75
|
+
|
76
|
+
it('post returns true when wait_writeable is true') {
|
77
|
+
cfg = block_config.merge(wait_writeable: true)
|
78
|
+
l = Fluent::Logger::FluentLogger.new('logger-test', cfg)
|
79
|
+
expect(l.post('hello', {foo: 'bar'})).to eq true
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
context('use_nonblock is true') do
|
84
|
+
let(:nonblock_config) { logger_config.merge(use_nonblock: true) }
|
85
|
+
|
86
|
+
it('post raises IO::EAGAINWaitWritable when wait_writeable is false') {
|
87
|
+
cfg = nonblock_config.merge(wait_writeable: false)
|
88
|
+
l = Fluent::Logger::FluentLogger.new('logger-test', cfg)
|
89
|
+
expect { l.post('hello', foo: 'bar') }.to raise_error(IO::EAGAINWaitWritable)
|
90
|
+
}
|
91
|
+
|
92
|
+
it('post returns false when wait_writeable is true') {
|
93
|
+
cfg = nonblock_config.merge(wait_writeable: true)
|
94
|
+
l = Fluent::Logger::FluentLogger.new('logger-test', cfg)
|
95
|
+
expect(l.post('hello', {foo: 'bar'})).to eq false
|
96
|
+
}
|
97
|
+
|
98
|
+
context 'when write_nonblock returns the size less than received data' do
|
99
|
+
before do
|
100
|
+
allow_any_instance_of(TCPSocket).to receive(:write_nonblock).and_return(1) # write 1 bytes per call
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'buffering data and flush at closed time' do
|
104
|
+
logger = Fluent::Logger::FluentLogger.new('logger-test', nonblock_config)
|
105
|
+
expect(logger.post('hello', foo: 'bar')).to eq(true)
|
106
|
+
expect(logger.pending_bytesize).to eq(0)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
61
112
|
context('Post by CUI') do
|
62
113
|
it('post') {
|
63
114
|
args = %W(-h localhost -p #{fluentd.port} -t logger-test.tag -v a=b -v foo=bar)
|
@@ -86,6 +137,16 @@ describe Fluent::Logger::FluentLogger do
|
|
86
137
|
}
|
87
138
|
end
|
88
139
|
|
140
|
+
context 'when the message has object which does not have #to_msgpack method' do
|
141
|
+
it 'success with nanosecond' do
|
142
|
+
expect(logger_with_nanosec.pending_bytesize).to eq(0)
|
143
|
+
expect(logger_with_nanosec.post('tag', 'a' => Errno::ETIMEDOUT)).to eq(true)
|
144
|
+
fluentd.wait_transfer
|
145
|
+
expect(fluentd.queue.last).to eq(['logger-test.tag', { 'a' => 'Errno::ETIMEDOUT' }])
|
146
|
+
expect(logger_with_nanosec.pending_bytesize).to eq(0)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
89
150
|
it ('close after post') {
|
90
151
|
expect(logger).to be_connect
|
91
152
|
logger.close
|
@@ -312,4 +373,51 @@ describe Fluent::Logger::FluentLogger do
|
|
312
373
|
end
|
313
374
|
end
|
314
375
|
end
|
376
|
+
|
377
|
+
context "running fluentd with TLS" do
|
378
|
+
before(:all) do
|
379
|
+
@serverengine = DummyServerengine.new
|
380
|
+
@serverengine.startup
|
381
|
+
end
|
382
|
+
|
383
|
+
before(:each) do
|
384
|
+
fluentd.startup(true)
|
385
|
+
end
|
386
|
+
|
387
|
+
after(:each) do
|
388
|
+
fluentd.shutdown
|
389
|
+
end
|
390
|
+
|
391
|
+
after(:all) do
|
392
|
+
@serverengine.shutdown
|
393
|
+
end
|
394
|
+
|
395
|
+
let(:logger_config) {
|
396
|
+
{
|
397
|
+
:host => 'localhost',
|
398
|
+
:port => fluentd.port,
|
399
|
+
:logger => internal_logger,
|
400
|
+
:buffer_overflow_handler => buffer_overflow_handler,
|
401
|
+
:tls_options => {:insecure => true}
|
402
|
+
}
|
403
|
+
}
|
404
|
+
|
405
|
+
context('post') do
|
406
|
+
it ('success') {
|
407
|
+
expect(logger.pending_bytesize).to eq 0
|
408
|
+
expect(logger.post('tag', {'a' => 'b'})).to be true
|
409
|
+
fluentd.wait_transfer
|
410
|
+
expect(fluentd.queue.last).to eq ['logger-test.tag', {'a' => 'b'}]
|
411
|
+
expect(logger.pending_bytesize).to eq 0
|
412
|
+
}
|
413
|
+
|
414
|
+
it ('success with nanosecond') {
|
415
|
+
expect(logger_with_nanosec.pending_bytesize).to eq 0
|
416
|
+
expect(logger_with_nanosec.post('tag', {'a' => 'b'})).to be true
|
417
|
+
fluentd.wait_transfer
|
418
|
+
expect(fluentd.queue.last).to eq ['logger-test.tag', {'a' => 'b'}]
|
419
|
+
expect(fluentd.output.emits.first[1]).to be_a_kind_of(Fluent::EventTime)
|
420
|
+
}
|
421
|
+
end
|
422
|
+
end
|
315
423
|
end
|
@@ -51,8 +51,22 @@ class DummyFluentd
|
|
51
51
|
queue
|
52
52
|
end
|
53
53
|
|
54
|
-
def startup
|
55
|
-
|
54
|
+
def startup(with_tls = false)
|
55
|
+
if with_tls
|
56
|
+
config = Fluent::Config.parse(<<EOF, '(logger-spec)', '(logger-spec-dir)', true)
|
57
|
+
<source>
|
58
|
+
type forward
|
59
|
+
port #{port}
|
60
|
+
<transport tls>
|
61
|
+
insecure true
|
62
|
+
</transport>
|
63
|
+
</source>
|
64
|
+
<match logger-test.**>
|
65
|
+
type test
|
66
|
+
</match>
|
67
|
+
EOF
|
68
|
+
else
|
69
|
+
config = Fluent::Config.parse(<<EOF, '(logger-spec)', '(logger-spec-dir)', true)
|
56
70
|
<source>
|
57
71
|
type forward
|
58
72
|
port #{port}
|
@@ -61,6 +75,8 @@ class DummyFluentd
|
|
61
75
|
type test
|
62
76
|
</match>
|
63
77
|
EOF
|
78
|
+
end
|
79
|
+
|
64
80
|
Fluent::Test.setup
|
65
81
|
Fluent::Engine.run_configure(config)
|
66
82
|
@coolio_default_loop = nil
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-logger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -161,8 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
161
|
- !ruby/object:Gem::Version
|
162
162
|
version: '0'
|
163
163
|
requirements: []
|
164
|
-
|
165
|
-
rubygems_version: 2.6.14.1
|
164
|
+
rubygems_version: 3.1.2
|
166
165
|
signing_key:
|
167
166
|
specification_version: 4
|
168
167
|
summary: fluent logger for ruby
|