logstash-logger 0.26.1 → 1.0.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 +4 -4
- data/.github/workflows/tests.yml +51 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +85 -105
- data/Appraisals +6 -18
- data/CHANGELOG.md +28 -0
- data/Gemfile +0 -1
- data/README.md +12 -833
- data/docs/buffering.md +70 -0
- data/docs/customization.md +86 -0
- data/docs/outputs.md +42 -0
- data/docs/rails.md +344 -0
- data/docs/ssl.md +90 -0
- data/docs/troubleshooting.md +84 -0
- data/docs/usage.md +148 -0
- data/gemfiles/{rails_4.2.gemfile → rails_7.2.gemfile} +1 -2
- data/gemfiles/{rails_5.0.gemfile → rails_8.0.gemfile} +1 -2
- data/gemfiles/{rails_4.0.gemfile → rails_8.1.gemfile} +1 -2
- data/lib/logstash-logger/buffer.rb +0 -1
- data/lib/logstash-logger/configuration.rb +1 -2
- data/lib/logstash-logger/device/aws_stream.rb +1 -1
- data/lib/logstash-logger/device/base.rb +2 -2
- data/lib/logstash-logger/device/connectable.rb +3 -11
- data/lib/logstash-logger/device/file.rb +21 -4
- data/lib/logstash-logger/device/http.rb +33 -0
- data/lib/logstash-logger/device/kafka.rb +153 -36
- data/lib/logstash-logger/device/redis.rb +8 -1
- data/lib/logstash-logger/device/tcp.rb +1 -5
- data/lib/logstash-logger/device.rb +24 -2
- data/lib/logstash-logger/formatter/base.rb +54 -9
- data/lib/logstash-logger/formatter/cee_syslog.rb +1 -1
- data/lib/logstash-logger/formatter/json.rb +13 -0
- data/lib/logstash-logger/formatter/json_lines.rb +13 -0
- data/lib/logstash-logger/formatter.rb +14 -6
- data/lib/logstash-logger/logger.rb +6 -19
- data/lib/logstash-logger/multi_logger.rb +2 -1
- data/lib/logstash-logger/railtie.rb +1 -1
- data/lib/logstash-logger/tagged_logging.rb +3 -1
- data/lib/logstash-logger/version.rb +1 -1
- data/logstash-logger.gemspec +9 -1
- data/spec/device/file_spec.rb +65 -0
- data/spec/device/http_spec.rb +11 -0
- data/spec/device/kafka_spec.rb +337 -14
- data/spec/device_spec.rb +13 -0
- data/spec/formatter/base_spec.rb +46 -1
- data/spec/formatter/cee_syslog_spec.rb +3 -3
- data/spec/formatter/json_lines_spec.rb +23 -0
- data/spec/formatter/json_spec.rb +49 -0
- data/spec/formatter_spec.rb +19 -2
- data/spec/logger_spec.rb +5 -5
- data/spec/multi_logger_spec.rb +16 -0
- data/spec/spec_helper.rb +2 -5
- data/spec/tagged_logging_spec.rb +15 -0
- metadata +89 -16
- data/.travis.yml +0 -26
- data/gemfiles/rails_3.2.gemfile +0 -9
- data/gemfiles/rails_4.1.gemfile +0 -9
- data/gemfiles/rails_5.1.gemfile +0 -9
|
@@ -20,6 +20,7 @@ module LogStashLogger
|
|
|
20
20
|
autoload :Stderr, 'logstash-logger/device/stderr'
|
|
21
21
|
autoload :Balancer, 'logstash-logger/device/balancer'
|
|
22
22
|
autoload :MultiDelegator, 'logstash-logger/device/multi_delegator'
|
|
23
|
+
autoload :HTTP, 'logstash-logger/device/http'
|
|
23
24
|
|
|
24
25
|
def self.new(opts)
|
|
25
26
|
opts = opts.dup
|
|
@@ -27,7 +28,7 @@ module LogStashLogger
|
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def self.build_device(opts)
|
|
30
|
-
if parsed_uri_opts = parse_uri_config(opts)
|
|
31
|
+
if (parsed_uri_opts = parse_uri_config(opts))
|
|
31
32
|
opts.delete(:uri)
|
|
32
33
|
opts.merge!(parsed_uri_opts)
|
|
33
34
|
end
|
|
@@ -38,10 +39,30 @@ module LogStashLogger
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def self.parse_uri_config(opts)
|
|
41
|
-
if uri = opts[:uri]
|
|
42
|
+
if (uri = opts[:uri])
|
|
42
43
|
require 'uri'
|
|
43
44
|
parsed = ::URI.parse(uri)
|
|
44
45
|
{type: parsed.scheme, host: parsed.host, port: parsed.port, path: parsed.path}
|
|
46
|
+
.merge(parse_uri_query(parsed.query))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.parse_uri_query(query)
|
|
51
|
+
return {} unless query && !query.empty?
|
|
52
|
+
::URI.decode_www_form(query).each_with_object({}) do |(key, value), acc|
|
|
53
|
+
next unless key
|
|
54
|
+
key = key.to_s.strip
|
|
55
|
+
next if key.empty?
|
|
56
|
+
acc[key.to_sym] = cast_uri_value(key, value)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.cast_uri_value(key, value)
|
|
61
|
+
case key
|
|
62
|
+
when 'buffer_max_items'
|
|
63
|
+
value.to_i
|
|
64
|
+
else
|
|
65
|
+
value
|
|
45
66
|
end
|
|
46
67
|
end
|
|
47
68
|
|
|
@@ -60,6 +81,7 @@ module LogStashLogger
|
|
|
60
81
|
when :stderr then Stderr
|
|
61
82
|
when :multi_delegator then MultiDelegator
|
|
62
83
|
when :balancer then Balancer
|
|
84
|
+
when :http then HTTP
|
|
63
85
|
else fail ArgumentError, 'Invalid device type'
|
|
64
86
|
end
|
|
65
87
|
end
|
|
@@ -4,19 +4,28 @@ require 'time'
|
|
|
4
4
|
|
|
5
5
|
module LogStashLogger
|
|
6
6
|
module Formatter
|
|
7
|
-
HOST =
|
|
7
|
+
HOST = {
|
|
8
|
+
'hostname' => ::Socket.gethostname,
|
|
9
|
+
'ip' => Socket.ip_address_list.reject(&:ipv4_loopback?).reject(&:ipv6_loopback?).map(&:ip_address)
|
|
10
|
+
}.freeze
|
|
8
11
|
|
|
9
12
|
class Base < ::Logger::Formatter
|
|
13
|
+
FAILED_TO_FORMAT_MSG = 'Failed to format log event'
|
|
14
|
+
attr_accessor :error_logger
|
|
10
15
|
include ::LogStashLogger::TaggedLogging::Formatter
|
|
11
16
|
|
|
12
|
-
def initialize(customize_event: nil)
|
|
17
|
+
def initialize(customize_event: nil, error_logger: LogStashLogger.configuration.default_error_logger)
|
|
13
18
|
@customize_event = customize_event
|
|
19
|
+
@error_logger = error_logger
|
|
14
20
|
super()
|
|
15
21
|
end
|
|
16
22
|
|
|
17
23
|
def call(severity, time, _progname, message)
|
|
18
24
|
event = build_event(message, severity, time)
|
|
19
25
|
format_event(event) unless event.cancelled?
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
log_error(e)
|
|
28
|
+
FAILED_TO_FORMAT_MSG
|
|
20
29
|
end
|
|
21
30
|
|
|
22
31
|
private
|
|
@@ -31,15 +40,24 @@ module LogStashLogger
|
|
|
31
40
|
when LogStash::Event
|
|
32
41
|
data.clone
|
|
33
42
|
when Hash
|
|
34
|
-
event_data =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
event_data = { '@timestamp'.freeze => time }
|
|
44
|
+
data.each do |key, value|
|
|
45
|
+
case key
|
|
46
|
+
when :message, 'message'
|
|
47
|
+
event_data['message'.freeze] = value
|
|
48
|
+
when :tags, 'tags'
|
|
49
|
+
event_data['tags'.freeze] = value
|
|
50
|
+
when :source, 'source'
|
|
51
|
+
event_data['source'.freeze] = value
|
|
52
|
+
when :type, 'type'
|
|
53
|
+
event_data['type'.freeze] = value
|
|
54
|
+
else
|
|
55
|
+
event_data[key] = value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
40
58
|
LogStash::Event.new(event_data)
|
|
41
59
|
else
|
|
42
|
-
LogStash::Event.new("
|
|
60
|
+
LogStash::Event.new("@timestamp".freeze => time, "message".freeze => msg2str(data))
|
|
43
61
|
end
|
|
44
62
|
|
|
45
63
|
event['severity'.freeze] ||= severity
|
|
@@ -68,6 +86,33 @@ module LogStashLogger
|
|
|
68
86
|
def format_event(event)
|
|
69
87
|
event
|
|
70
88
|
end
|
|
89
|
+
|
|
90
|
+
def force_utf8_encoding(event)
|
|
91
|
+
original_message = event.instance_variable_get(:@data)['message']
|
|
92
|
+
event.message = original_message.dup.force_encoding(Encoding::UTF_8).scrub
|
|
93
|
+
event
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Check if the message has encoding issues that would cause JSON serialization problems.
|
|
97
|
+
# This is needed because some Ruby implementations (e.g., JRuby) may not raise
|
|
98
|
+
# exceptions during JSON encoding but produce malformed output instead.
|
|
99
|
+
def message_has_encoding_issue?(event)
|
|
100
|
+
message = event.instance_variable_get(:@data)['message']
|
|
101
|
+
return false unless message.is_a?(String)
|
|
102
|
+
|
|
103
|
+
# Check if already valid UTF-8
|
|
104
|
+
return false if message.encoding == Encoding::UTF_8 && message.valid_encoding?
|
|
105
|
+
|
|
106
|
+
# Try to encode to UTF-8 to detect issues
|
|
107
|
+
message.encode(Encoding::UTF_8)
|
|
108
|
+
false
|
|
109
|
+
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
|
|
110
|
+
true
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def log_error(e)
|
|
114
|
+
error_logger.error "[#{self.class}] #{e.class} - #{e.message}"
|
|
115
|
+
end
|
|
71
116
|
end
|
|
72
117
|
end
|
|
73
118
|
end
|
|
@@ -4,7 +4,20 @@ module LogStashLogger
|
|
|
4
4
|
private
|
|
5
5
|
|
|
6
6
|
def format_event(event)
|
|
7
|
+
# Proactively check for encoding issues to handle cross-platform differences.
|
|
8
|
+
# Some Ruby implementations (e.g., JRuby) may not raise exceptions during
|
|
9
|
+
# JSON encoding but produce malformed output instead.
|
|
10
|
+
if message_has_encoding_issue?(event)
|
|
11
|
+
log_error(Encoding::InvalidByteSequenceError.new("Invalid encoding in message"))
|
|
12
|
+
return force_utf8_encoding(event).to_json
|
|
13
|
+
end
|
|
14
|
+
|
|
7
15
|
event.to_json
|
|
16
|
+
rescue Encoding::UndefinedConversionError,
|
|
17
|
+
Encoding::InvalidByteSequenceError,
|
|
18
|
+
JSON::GeneratorError => e
|
|
19
|
+
log_error(e)
|
|
20
|
+
force_utf8_encoding(event).to_json
|
|
8
21
|
end
|
|
9
22
|
end
|
|
10
23
|
end
|
|
@@ -4,7 +4,20 @@ module LogStashLogger
|
|
|
4
4
|
private
|
|
5
5
|
|
|
6
6
|
def format_event(event)
|
|
7
|
+
# Proactively check for encoding issues to handle cross-platform differences.
|
|
8
|
+
# Some Ruby implementations (e.g., JRuby) may not raise exceptions during
|
|
9
|
+
# JSON encoding but produce malformed output instead.
|
|
10
|
+
if message_has_encoding_issue?(event)
|
|
11
|
+
log_error(Encoding::InvalidByteSequenceError.new("Invalid encoding in message"))
|
|
12
|
+
return "#{force_utf8_encoding(event).to_json}\n"
|
|
13
|
+
end
|
|
14
|
+
|
|
7
15
|
"#{event.to_json}\n"
|
|
16
|
+
rescue Encoding::UndefinedConversionError,
|
|
17
|
+
Encoding::InvalidByteSequenceError,
|
|
18
|
+
JSON::GeneratorError => e
|
|
19
|
+
log_error(e)
|
|
20
|
+
"#{force_utf8_encoding(event).to_json}\n"
|
|
8
21
|
end
|
|
9
22
|
end
|
|
10
23
|
end
|
|
@@ -10,20 +10,20 @@ module LogStashLogger
|
|
|
10
10
|
autoload :Cee, 'logstash-logger/formatter/cee'
|
|
11
11
|
autoload :CeeSyslog, 'logstash-logger/formatter/cee_syslog'
|
|
12
12
|
|
|
13
|
-
def self.new(formatter_type, customize_event: nil)
|
|
14
|
-
build_formatter(formatter_type, customize_event)
|
|
13
|
+
def self.new(formatter_type, customize_event: nil, error_logger: LogStashLogger.configuration.default_error_logger)
|
|
14
|
+
build_formatter(formatter_type, customize_event, error_logger)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def self.build_formatter(formatter_type, customize_event)
|
|
17
|
+
def self.build_formatter(formatter_type, customize_event, error_logger)
|
|
18
18
|
formatter_type ||= DEFAULT_FORMATTER
|
|
19
19
|
|
|
20
20
|
formatter = if custom_formatter_instance?(formatter_type)
|
|
21
21
|
formatter_type
|
|
22
22
|
elsif custom_formatter_class?(formatter_type)
|
|
23
|
-
formatter_type
|
|
23
|
+
initialize_custom_formatter_klass(formatter_type, customize_event)
|
|
24
24
|
else
|
|
25
|
-
formatter_klass(formatter_type).new(customize_event: customize_event)
|
|
26
|
-
|
|
25
|
+
formatter_klass(formatter_type).new(customize_event: customize_event, error_logger: error_logger)
|
|
26
|
+
end
|
|
27
27
|
|
|
28
28
|
formatter.send(:extend, ::LogStashLogger::TaggedLogging::Formatter)
|
|
29
29
|
formatter
|
|
@@ -47,5 +47,13 @@ module LogStashLogger
|
|
|
47
47
|
def self.custom_formatter_class?(formatter_type)
|
|
48
48
|
formatter_type.is_a?(Class) && formatter_type.method_defined?(:call)
|
|
49
49
|
end
|
|
50
|
+
|
|
51
|
+
def self.initialize_custom_formatter_klass(formatter_klass, customize_event)
|
|
52
|
+
if formatter_klass.instance_method(:initialize).parameters.include?([:key, :customize_event])
|
|
53
|
+
formatter_klass.new(customize_event: customize_event)
|
|
54
|
+
else
|
|
55
|
+
formatter_klass.new
|
|
56
|
+
end
|
|
57
|
+
end
|
|
50
58
|
end
|
|
51
59
|
end
|
|
@@ -26,31 +26,19 @@ module LogStashLogger
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
protected
|
|
30
29
|
|
|
31
30
|
def self.extract_opts(*args)
|
|
32
|
-
args.
|
|
33
|
-
|
|
34
|
-
if args.length > 1
|
|
35
|
-
if args.all?{|arg| arg.is_a?(Hash)}
|
|
36
|
-
# Deprecated array of hashes
|
|
37
|
-
warn "[LogStashLogger] Passing an array of hashes to the constructor is deprecated. Please replace with an options hash: { type: :multi_delegator, outputs: [...] }"
|
|
38
|
-
{ type: :multi_delegator, outputs: args }
|
|
39
|
-
else
|
|
40
|
-
# Deprecated host/port/type constructor
|
|
41
|
-
warn "[LogStashLogger] The (host, port, type) constructor is deprecated. Please use an options hash instead."
|
|
42
|
-
host, port, type = *args
|
|
43
|
-
{ host: host, port: port, type: type }
|
|
44
|
-
end
|
|
45
|
-
elsif Hash === args[0]
|
|
31
|
+
if args.length == 1 && args[0].is_a?(Hash)
|
|
46
32
|
args[0]
|
|
47
33
|
else
|
|
48
|
-
fail ArgumentError, "Invalid LogStashLogger options"
|
|
34
|
+
fail ArgumentError, "Invalid LogStashLogger options. Expected a single options hash."
|
|
49
35
|
end
|
|
50
36
|
end
|
|
51
37
|
|
|
52
38
|
def self.build_logger(opts)
|
|
53
|
-
formatter = Formatter.new(opts.delete(:formatter),
|
|
39
|
+
formatter = Formatter.new(opts.delete(:formatter),
|
|
40
|
+
customize_event: opts.delete(:customize_event),
|
|
41
|
+
error_logger: opts.fetch(:error_logger, LogStashLogger.configuration.default_error_logger))
|
|
54
42
|
|
|
55
43
|
logger_type = opts[:type].to_s.to_sym
|
|
56
44
|
logger = case logger_type
|
|
@@ -60,13 +48,12 @@ module LogStashLogger
|
|
|
60
48
|
build_syslog_logger(opts)
|
|
61
49
|
else
|
|
62
50
|
build_default_logger(opts)
|
|
63
|
-
|
|
51
|
+
end
|
|
64
52
|
|
|
65
53
|
logger.formatter = formatter if formatter
|
|
66
54
|
logger
|
|
67
55
|
end
|
|
68
56
|
|
|
69
|
-
private
|
|
70
57
|
|
|
71
58
|
def self.build_default_logger(opts)
|
|
72
59
|
logger_class = opts.delete(:logger_class) || ::Logger
|
|
@@ -70,7 +70,7 @@ module LogStashLogger
|
|
|
70
70
|
def method_missing(name, *args, &block)
|
|
71
71
|
@loggers.each do |logger|
|
|
72
72
|
if logger.respond_to?(name)
|
|
73
|
-
logger.send(name, args, &block)
|
|
73
|
+
logger.send(name, *args, &block)
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
76
|
end
|
|
@@ -101,6 +101,7 @@ module LogStashLogger
|
|
|
101
101
|
logger.add(severity, message, progname, &block)
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
|
+
alias log add
|
|
104
105
|
|
|
105
106
|
def <<(msg)
|
|
106
107
|
@loggers.each do |logger|
|
|
@@ -25,7 +25,7 @@ module LogStashLogger
|
|
|
25
25
|
# Convert logger options to OrderedOptions if regular Hash
|
|
26
26
|
logger_options = ActiveSupport::OrderedOptions.new.merge(logger_options)
|
|
27
27
|
|
|
28
|
-
if parsed_uri_options = LogStashLogger::Device.parse_uri_config(logger_options)
|
|
28
|
+
if (parsed_uri_options = LogStashLogger::Device.parse_uri_config(logger_options))
|
|
29
29
|
logger_options.delete(:uri)
|
|
30
30
|
logger_options.merge!(parsed_uri_options)
|
|
31
31
|
end
|
|
@@ -19,7 +19,9 @@ module LogStashLogger
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def push_tags(*tags)
|
|
22
|
-
tags.flatten.reject{ |t| t.
|
|
22
|
+
non_empty_tags = tags.flatten.compact.reject { |t| t.respond_to?(:empty?) && t.empty? }
|
|
23
|
+
|
|
24
|
+
non_empty_tags.tap do |new_tags|
|
|
23
25
|
current_tags.concat new_tags
|
|
24
26
|
end
|
|
25
27
|
end
|
data/logstash-logger.gemspec
CHANGED
|
@@ -22,12 +22,14 @@ Gem::Specification.new do |gem|
|
|
|
22
22
|
|
|
23
23
|
gem.add_development_dependency 'rails'
|
|
24
24
|
gem.add_development_dependency 'redis'
|
|
25
|
-
gem.add_development_dependency '
|
|
25
|
+
gem.add_development_dependency 'ruby-kafka'
|
|
26
26
|
gem.add_development_dependency 'aws-sdk-kinesis'
|
|
27
27
|
gem.add_development_dependency 'aws-sdk-firehose'
|
|
28
28
|
|
|
29
29
|
if defined?(JRUBY_VERSION)
|
|
30
30
|
gem.add_development_dependency 'SyslogLogger'
|
|
31
|
+
else
|
|
32
|
+
gem.add_development_dependency 'syslog'
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
gem.add_development_dependency 'rspec', '>= 3'
|
|
@@ -36,4 +38,10 @@ Gem::Specification.new do |gem|
|
|
|
36
38
|
gem.add_development_dependency 'wwtd'
|
|
37
39
|
gem.add_development_dependency 'appraisal'
|
|
38
40
|
gem.add_development_dependency 'rubocop'
|
|
41
|
+
gem.add_development_dependency 'rubocop-performance'
|
|
42
|
+
gem.add_development_dependency 'rubocop-rails'
|
|
43
|
+
gem.add_development_dependency 'rubocop-rake'
|
|
44
|
+
gem.add_development_dependency 'rubocop-rspec'
|
|
45
|
+
|
|
46
|
+
gem.required_ruby_version = '>= 3.2'
|
|
39
47
|
end
|
data/spec/device/file_spec.rb
CHANGED
|
@@ -7,6 +7,71 @@ describe LogStashLogger::Device::File do
|
|
|
7
7
|
expect(file_device.to_io).to be_a ::File
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
context "with shift options" do
|
|
11
|
+
let(:shift_file) do
|
|
12
|
+
temp = Tempfile.new('logstash_shift')
|
|
13
|
+
temp.close
|
|
14
|
+
temp
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let(:shift_device) do
|
|
18
|
+
LogStashLogger::Device.new(
|
|
19
|
+
type: :file,
|
|
20
|
+
path: shift_file.path,
|
|
21
|
+
shift_age: 1,
|
|
22
|
+
shift_size: 1
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
let(:period_file) do
|
|
27
|
+
temp = Tempfile.new('logstash_period_shift')
|
|
28
|
+
temp.close
|
|
29
|
+
temp
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
let(:period_device) do
|
|
33
|
+
LogStashLogger::Device.new(
|
|
34
|
+
type: :file,
|
|
35
|
+
path: period_file.path,
|
|
36
|
+
shift_age: 'daily',
|
|
37
|
+
shift_period_suffix: '%Y-%m-%d'
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
after do
|
|
42
|
+
shift_device.close
|
|
43
|
+
::Dir.glob("#{shift_file.path}*").each do |path|
|
|
44
|
+
::File.delete(path) if ::File.exist?(path)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
period_device.close
|
|
48
|
+
::Dir.glob("#{period_file.path}*").each do |path|
|
|
49
|
+
::File.delete(path) if ::File.exist?(path)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "wraps a Logger::LogDevice for rotation" do
|
|
54
|
+
expect(shift_device.io).to be_a ::Logger::LogDevice
|
|
55
|
+
expect(shift_device.to_io).to be_a ::File
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "rotates when shift_size is exceeded" do
|
|
59
|
+
shift_device.write("a" * 10)
|
|
60
|
+
shift_device.write("b" * 10)
|
|
61
|
+
shift_device.close
|
|
62
|
+
|
|
63
|
+
expect(::File.exist?("#{shift_file.path}.0")).to be(true)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "uses shift_period_suffix for time-based rotation" do
|
|
67
|
+
period_device.write("a")
|
|
68
|
+
period_device.io.send(:shift_log_period, Time.new(2026, 1, 22))
|
|
69
|
+
period_device.close
|
|
70
|
+
|
|
71
|
+
expect(::File.exist?("#{period_file.path}.2026-01-22")).to be(true)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
10
75
|
context "when path is not specified" do
|
|
11
76
|
it "raises an exception" do
|
|
12
77
|
expect { described_class.new }.to raise_error(ArgumentError)
|