fluentd 1.15.2 → 1.15.3
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/CHANGELOG.md +19 -0
- data/lib/fluent/command/fluentd.rb +0 -11
- data/lib/fluent/config/yaml_parser/loader.rb +18 -1
- data/lib/fluent/file_wrapper.rb +137 -0
- data/lib/fluent/oj_options.rb +1 -2
- data/lib/fluent/plugin/in_tail.rb +1 -6
- data/lib/fluent/plugin/out_file.rb +0 -4
- data/lib/fluent/supervisor.rb +30 -11
- data/lib/fluent/version.rb +1 -1
- data/test/command/test_fluentd.rb +31 -16
- data/test/plugin/test_in_tail.rb +105 -103
- data/test/plugin/test_out_file.rb +3 -2
- data/test/test_config.rb +57 -0
- data/test/{plugin/test_file_wrapper.rb → test_file_wrapper.rb} +2 -2
- data/test/test_log.rb +15 -5
- data/test/test_supervisor.rb +37 -15
- metadata +5 -7
- data/.github/workflows/issue-auto-closer.yml +0 -12
- data/.github/workflows/stale-actions.yml +0 -22
- data/lib/fluent/plugin/file_wrapper.rb +0 -131
@@ -5,6 +5,7 @@ require 'fileutils'
|
|
5
5
|
require 'time'
|
6
6
|
require 'timecop'
|
7
7
|
require 'zlib'
|
8
|
+
require 'fluent/file_wrapper'
|
8
9
|
|
9
10
|
class FileOutputTest < Test::Unit::TestCase
|
10
11
|
def setup
|
@@ -1016,7 +1017,7 @@ class FileOutputTest < Test::Unit::TestCase
|
|
1016
1017
|
|
1017
1018
|
test 'returns filepath with index which does not exist yet' do
|
1018
1019
|
5.times do |i|
|
1019
|
-
|
1020
|
+
Fluent::FileWrapper.open(File.join(@tmp, "exist_#{i}.log"), 'a'){|f| } # open(create) and close
|
1020
1021
|
end
|
1021
1022
|
@i.find_filepath_available(File.join(@tmp, "exist_**.log")) do |path|
|
1022
1023
|
assert_equal File.join(@tmp, "exist_5.log"), path
|
@@ -1025,7 +1026,7 @@ class FileOutputTest < Test::Unit::TestCase
|
|
1025
1026
|
|
1026
1027
|
test 'creates lock directory when with_lock is true to exclude operations of other worker process' do
|
1027
1028
|
5.times do |i|
|
1028
|
-
|
1029
|
+
Fluent::FileWrapper.open(File.join(@tmp, "exist_#{i}.log"), 'a')
|
1029
1030
|
end
|
1030
1031
|
Dir.mkdir(File.join(@tmp, "exist_5.log.lock"))
|
1031
1032
|
@i.find_filepath_available(File.join(@tmp, "exist_**.log"), with_lock: true) do |path|
|
data/test/test_config.rb
CHANGED
@@ -237,6 +237,63 @@ class ConfigTest < Test::Unit::TestCase
|
|
237
237
|
])
|
238
238
|
end
|
239
239
|
|
240
|
+
def test_included_glob
|
241
|
+
write_config "#{TMP_DIR}/config.yaml", <<-EOS
|
242
|
+
config:
|
243
|
+
- !include "include/*.yaml"
|
244
|
+
EOS
|
245
|
+
write_config "#{TMP_DIR}/include/02_source2.yaml", <<-EOS
|
246
|
+
- source:
|
247
|
+
$type: dummy
|
248
|
+
tag: tag.dummy
|
249
|
+
EOS
|
250
|
+
write_config "#{TMP_DIR}/include/01_source1.yaml", <<-EOS
|
251
|
+
- source:
|
252
|
+
$type: tcp
|
253
|
+
tag: tag.tcp
|
254
|
+
parse:
|
255
|
+
$arg:
|
256
|
+
- why.parse.section.doesnot.have.arg
|
257
|
+
- huh
|
258
|
+
$type: none
|
259
|
+
EOS
|
260
|
+
write_config "#{TMP_DIR}/include/03_match1.yaml", <<-EOS
|
261
|
+
- match:
|
262
|
+
$tag: tag.*
|
263
|
+
$type: stdout
|
264
|
+
buffer:
|
265
|
+
$type: memory
|
266
|
+
flush_interval: 1s
|
267
|
+
EOS
|
268
|
+
root_conf = read_config("#{TMP_DIR}/config.yaml", use_yaml: true)
|
269
|
+
tcp_source_conf = root_conf.elements.first
|
270
|
+
dummy_source_conf = root_conf.elements[1]
|
271
|
+
parse_tcp_conf = tcp_source_conf.elements.first
|
272
|
+
match_conf = root_conf.elements[2]
|
273
|
+
|
274
|
+
assert_equal(
|
275
|
+
[
|
276
|
+
'tcp',
|
277
|
+
'tag.tcp',
|
278
|
+
'none',
|
279
|
+
'why.parse.section.doesnot.have.arg,huh',
|
280
|
+
'dummy',
|
281
|
+
'tag.dummy',
|
282
|
+
'stdout',
|
283
|
+
'tag.*',
|
284
|
+
],
|
285
|
+
[
|
286
|
+
tcp_source_conf['@type'],
|
287
|
+
tcp_source_conf['tag'],
|
288
|
+
parse_tcp_conf['@type'],
|
289
|
+
parse_tcp_conf.arg,
|
290
|
+
dummy_source_conf['@type'],
|
291
|
+
dummy_source_conf['tag'],
|
292
|
+
match_conf['@type'],
|
293
|
+
match_conf.arg,
|
294
|
+
])
|
295
|
+
end
|
296
|
+
|
240
297
|
def test_check_not_fetchd
|
241
298
|
write_config "#{TMP_DIR}/config_test_not_fetched.yaml", <<-EOS
|
242
299
|
config:
|
data/test/test_log.rb
CHANGED
@@ -4,13 +4,16 @@ require 'fluent/engine'
|
|
4
4
|
require 'fluent/log'
|
5
5
|
require 'timecop'
|
6
6
|
require 'logger'
|
7
|
+
require 'securerandom'
|
7
8
|
|
8
9
|
class LogTest < Test::Unit::TestCase
|
9
|
-
|
10
|
+
def tmp_dir
|
11
|
+
File.join(File.dirname(__FILE__), "tmp", "log", "#{ENV['TEST_ENV_NUMBER']}", SecureRandom.hex(10))
|
12
|
+
end
|
10
13
|
|
11
14
|
def setup
|
12
|
-
|
13
|
-
FileUtils.mkdir_p(
|
15
|
+
@tmp_dir = tmp_dir
|
16
|
+
FileUtils.mkdir_p(@tmp_dir)
|
14
17
|
@log_device = Fluent::Test::DummyLogDevice.new
|
15
18
|
@timestamp = Time.parse("2016-04-21 02:58:41 +0000")
|
16
19
|
@timestamp_str = @timestamp.strftime("%Y-%m-%d %H:%M:%S %z")
|
@@ -21,6 +24,13 @@ class LogTest < Test::Unit::TestCase
|
|
21
24
|
@log_device.reset
|
22
25
|
Timecop.return
|
23
26
|
Thread.current[:last_repeated_stacktrace] = nil
|
27
|
+
begin
|
28
|
+
FileUtils.rm_rf(@tmp_dir)
|
29
|
+
rescue Errno::EACCES
|
30
|
+
# It may occur on Windows because of delete pending state due to delayed GC.
|
31
|
+
# Ruby 3.2 or later doesn't ignore Errno::EACCES:
|
32
|
+
# https://github.com/ruby/ruby/commit/983115cf3c8f75b1afbe3274f02c1529e1ce3a81
|
33
|
+
end
|
24
34
|
end
|
25
35
|
|
26
36
|
sub_test_case "log level" do
|
@@ -560,7 +570,7 @@ class LogTest < Test::Unit::TestCase
|
|
560
570
|
Timecop.freeze(@timestamp)
|
561
571
|
|
562
572
|
rotate_age, rotate_size, travel_term = expected
|
563
|
-
path = "#{
|
573
|
+
path = "#{@tmp_dir}/log-dev-io-#{rotate_size}-#{rotate_age}"
|
564
574
|
|
565
575
|
logdev = Fluent::LogDeviceIO.new(path, shift_age: rotate_age, shift_size: rotate_size)
|
566
576
|
logger = ServerEngine::DaemonLogger.new(logdev)
|
@@ -585,7 +595,7 @@ class LogTest < Test::Unit::TestCase
|
|
585
595
|
with_timezone('utc') do
|
586
596
|
rotate_age = 2
|
587
597
|
rotate_size = 100
|
588
|
-
path = "#{
|
598
|
+
path = "#{@tmp_dir}/log-dev-io-#{rotate_size}-#{rotate_age}"
|
589
599
|
path0 = path + '.0'
|
590
600
|
path1 = path + '.1'
|
591
601
|
|
data/test/test_supervisor.rb
CHANGED
@@ -2,12 +2,14 @@ require_relative 'helper'
|
|
2
2
|
require 'fluent/event_router'
|
3
3
|
require 'fluent/system_config'
|
4
4
|
require 'fluent/supervisor'
|
5
|
+
require 'fluent/file_wrapper'
|
5
6
|
require_relative 'test_plugin_classes'
|
6
7
|
|
7
8
|
require 'net/http'
|
8
9
|
require 'uri'
|
9
10
|
require 'fileutils'
|
10
11
|
require 'tempfile'
|
12
|
+
require 'securerandom'
|
11
13
|
|
12
14
|
if Fluent.windows?
|
13
15
|
require 'win32/event'
|
@@ -22,17 +24,29 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
+
def tmp_dir
|
28
|
+
File.join(File.dirname(__FILE__), "tmp", "supervisor#{ENV['TEST_ENV_NUMBER']}", SecureRandom.hex(10))
|
29
|
+
end
|
27
30
|
|
28
31
|
def setup
|
29
|
-
|
30
|
-
|
32
|
+
@tmp_dir = tmp_dir
|
33
|
+
@tmp_root_dir = File.join(@tmp_dir, 'root')
|
34
|
+
FileUtils.mkdir_p(@tmp_dir)
|
35
|
+
end
|
36
|
+
|
37
|
+
def teardown
|
38
|
+
begin
|
39
|
+
FileUtils.rm_rf(@tmp_dir)
|
40
|
+
rescue Errno::EACCES
|
41
|
+
# It may occur on Windows because of delete pending state due to delayed GC.
|
42
|
+
# Ruby 3.2 or later doesn't ignore Errno::EACCES:
|
43
|
+
# https://github.com/ruby/ruby/commit/983115cf3c8f75b1afbe3274f02c1529e1ce3a81
|
44
|
+
end
|
31
45
|
end
|
32
46
|
|
33
47
|
def write_config(path, data)
|
34
48
|
FileUtils.mkdir_p(File.dirname(path))
|
35
|
-
|
49
|
+
Fluent::FileWrapper.open(path, "w") {|f| f.write data }
|
36
50
|
end
|
37
51
|
|
38
52
|
|
@@ -48,7 +62,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
48
62
|
enable_get_dump true
|
49
63
|
process_name "process_name"
|
50
64
|
log_level info
|
51
|
-
root_dir #{
|
65
|
+
root_dir #{@tmp_root_dir}
|
52
66
|
<log>
|
53
67
|
format json
|
54
68
|
time_format %Y
|
@@ -76,7 +90,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
76
90
|
assert_equal true, sys_conf.enable_get_dump
|
77
91
|
assert_equal "process_name", sys_conf.process_name
|
78
92
|
assert_equal 2, sys_conf.log_level
|
79
|
-
assert_equal
|
93
|
+
assert_equal @tmp_root_dir, sys_conf.root_dir
|
80
94
|
assert_equal :json, sys_conf.log.format
|
81
95
|
assert_equal '%Y', sys_conf.log.time_format
|
82
96
|
counter_server = sys_conf.counter_server
|
@@ -116,7 +130,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
116
130
|
enable_get_dump: true
|
117
131
|
process_name: "process_name"
|
118
132
|
log_level: info
|
119
|
-
root_dir: !fluent/s "#{
|
133
|
+
root_dir: !fluent/s "#{@tmp_root_dir}"
|
120
134
|
log:
|
121
135
|
format: json
|
122
136
|
time_format: "%Y"
|
@@ -144,7 +158,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
144
158
|
true,
|
145
159
|
"process_name",
|
146
160
|
2,
|
147
|
-
|
161
|
+
@tmp_root_dir,
|
148
162
|
:json,
|
149
163
|
'%Y',
|
150
164
|
'127.0.0.1',
|
@@ -449,7 +463,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
449
463
|
end
|
450
464
|
|
451
465
|
def test_load_config
|
452
|
-
tmp_dir = "#{
|
466
|
+
tmp_dir = "#{@tmp_dir}/dir/test_load_config.conf"
|
453
467
|
conf_info_str = %[
|
454
468
|
<system>
|
455
469
|
log_level info
|
@@ -520,7 +534,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
520
534
|
end
|
521
535
|
|
522
536
|
def test_load_config_for_logger
|
523
|
-
tmp_dir = "#{
|
537
|
+
tmp_dir = "#{@tmp_dir}/dir/test_load_config_log.conf"
|
524
538
|
conf_info_str = %[
|
525
539
|
<system>
|
526
540
|
<log>
|
@@ -547,7 +561,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
547
561
|
end
|
548
562
|
|
549
563
|
def test_load_config_for_daemonize
|
550
|
-
tmp_dir = "#{
|
564
|
+
tmp_dir = "#{@tmp_dir}/dir/test_load_config.conf"
|
551
565
|
conf_info_str = %[
|
552
566
|
<system>
|
553
567
|
log_level info
|
@@ -644,7 +658,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
644
658
|
)
|
645
659
|
def test_logger_with_rotate_age_and_rotate_size(rotate_age)
|
646
660
|
opts = Fluent::Supervisor.default_options.merge(
|
647
|
-
log_path: "#{
|
661
|
+
log_path: "#{@tmp_dir}/test", log_rotate_age: rotate_age, log_rotate_size: 10
|
648
662
|
)
|
649
663
|
sv = Fluent::Supervisor.new(opts)
|
650
664
|
log = sv.instance_variable_get(:@log)
|
@@ -674,7 +688,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
674
688
|
file.puts(config)
|
675
689
|
file.flush
|
676
690
|
opts = Fluent::Supervisor.default_options.merge(
|
677
|
-
log_path: "#{
|
691
|
+
log_path: "#{@tmp_dir}/test.log", config_path: file.path
|
678
692
|
)
|
679
693
|
sv = Fluent::Supervisor.new(opts)
|
680
694
|
|
@@ -699,7 +713,7 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
699
713
|
file.puts(config)
|
700
714
|
file.flush
|
701
715
|
opts = Fluent::Supervisor.default_options.merge(
|
702
|
-
log_path: "#{
|
716
|
+
log_path: "#{@tmp_dir}/test.log", config_path: file.path, config_file_type: :yaml,
|
703
717
|
)
|
704
718
|
sv = Fluent::Supervisor.new(opts)
|
705
719
|
|
@@ -773,6 +787,14 @@ class SupervisorTest < ::Test::Unit::TestCase
|
|
773
787
|
end
|
774
788
|
end
|
775
789
|
|
790
|
+
def test_per_process_path
|
791
|
+
path = Fluent::Supervisor::LoggerInitializer.per_process_path("C:/tmp/test.log", :supervisor, 0)
|
792
|
+
assert_equal(path, "C:/tmp/test-supervisor-0.log")
|
793
|
+
|
794
|
+
path = Fluent::Supervisor::LoggerInitializer.per_process_path("C:/tmp/test.log", :worker, 1)
|
795
|
+
assert_equal(path, "C:/tmp/test-1.log")
|
796
|
+
end
|
797
|
+
|
776
798
|
def create_debug_dummy_logger
|
777
799
|
dl_opts = {}
|
778
800
|
dl_opts[:log_level] = ServerEngine::DaemonLogger::DEBUG
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluentd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.15.
|
4
|
+
version: 1.15.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -391,10 +391,8 @@ files:
|
|
391
391
|
- ".github/ISSUE_TEMPLATE/config.yml"
|
392
392
|
- ".github/ISSUE_TEMPLATE/feature_request.yaml"
|
393
393
|
- ".github/PULL_REQUEST_TEMPLATE.md"
|
394
|
-
- ".github/workflows/issue-auto-closer.yml"
|
395
394
|
- ".github/workflows/linux-test.yaml"
|
396
395
|
- ".github/workflows/macos-test.yaml"
|
397
|
-
- ".github/workflows/stale-actions.yml"
|
398
396
|
- ".github/workflows/windows-test.yaml"
|
399
397
|
- ".gitignore"
|
400
398
|
- ".gitlab-ci.yml"
|
@@ -529,6 +527,7 @@ files:
|
|
529
527
|
- lib/fluent/event.rb
|
530
528
|
- lib/fluent/event_router.rb
|
531
529
|
- lib/fluent/ext_monitor_require.rb
|
530
|
+
- lib/fluent/file_wrapper.rb
|
532
531
|
- lib/fluent/filter.rb
|
533
532
|
- lib/fluent/fluent_log_event_router.rb
|
534
533
|
- lib/fluent/formatter.rb
|
@@ -557,7 +556,6 @@ files:
|
|
557
556
|
- lib/fluent/plugin/compressable.rb
|
558
557
|
- lib/fluent/plugin/exec_util.rb
|
559
558
|
- lib/fluent/plugin/file_util.rb
|
560
|
-
- lib/fluent/plugin/file_wrapper.rb
|
561
559
|
- lib/fluent/plugin/filter.rb
|
562
560
|
- lib/fluent/plugin/filter_grep.rb
|
563
561
|
- lib/fluent/plugin/filter_parser.rb
|
@@ -790,7 +788,6 @@ files:
|
|
790
788
|
- test/plugin/test_buffer_memory_chunk.rb
|
791
789
|
- test/plugin/test_compressable.rb
|
792
790
|
- test/plugin/test_file_util.rb
|
793
|
-
- test/plugin/test_file_wrapper.rb
|
794
791
|
- test/plugin/test_filter.rb
|
795
792
|
- test/plugin/test_filter_grep.rb
|
796
793
|
- test/plugin/test_filter_parser.rb
|
@@ -922,6 +919,7 @@ files:
|
|
922
919
|
- test/test_event.rb
|
923
920
|
- test/test_event_router.rb
|
924
921
|
- test/test_event_time.rb
|
922
|
+
- test/test_file_wrapper.rb
|
925
923
|
- test/test_filter.rb
|
926
924
|
- test/test_fluent_log_event_router.rb
|
927
925
|
- test/test_formatter.rb
|
@@ -1033,7 +1031,6 @@ test_files:
|
|
1033
1031
|
- test/plugin/test_buffer_memory_chunk.rb
|
1034
1032
|
- test/plugin/test_compressable.rb
|
1035
1033
|
- test/plugin/test_file_util.rb
|
1036
|
-
- test/plugin/test_file_wrapper.rb
|
1037
1034
|
- test/plugin/test_filter.rb
|
1038
1035
|
- test/plugin/test_filter_grep.rb
|
1039
1036
|
- test/plugin/test_filter_parser.rb
|
@@ -1165,6 +1162,7 @@ test_files:
|
|
1165
1162
|
- test/test_event.rb
|
1166
1163
|
- test/test_event_router.rb
|
1167
1164
|
- test/test_event_time.rb
|
1165
|
+
- test/test_file_wrapper.rb
|
1168
1166
|
- test/test_filter.rb
|
1169
1167
|
- test/test_fluent_log_event_router.rb
|
1170
1168
|
- test/test_formatter.rb
|
@@ -1,12 +0,0 @@
|
|
1
|
-
name: Autocloser
|
2
|
-
on: [issues]
|
3
|
-
jobs:
|
4
|
-
autoclose:
|
5
|
-
runs-on: ubuntu-latest
|
6
|
-
steps:
|
7
|
-
- name: Autoclose issues that did not follow issue template
|
8
|
-
uses: roots/issue-closer-action@v1.1
|
9
|
-
with:
|
10
|
-
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
11
|
-
issue-close-message: "@${issue.user.login} this issue was automatically closed because it did not follow the issue template"
|
12
|
-
issue-pattern: "(.*Describe the bug.*)|(.*Is your feature request related to a problem.*)"
|
@@ -1,22 +0,0 @@
|
|
1
|
-
name: "Mark or close stale issues and PRs"
|
2
|
-
on:
|
3
|
-
schedule:
|
4
|
-
- cron: "00 10 * * *"
|
5
|
-
|
6
|
-
jobs:
|
7
|
-
stale:
|
8
|
-
runs-on: ubuntu-latest
|
9
|
-
steps:
|
10
|
-
- uses: actions/stale@v3
|
11
|
-
with:
|
12
|
-
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
13
|
-
days-before-stale: 90
|
14
|
-
days-before-close: 30
|
15
|
-
stale-issue-message: "This issue has been automatically marked as stale because it has been open 90 days with no activity. Remove stale label or comment or this issue will be closed in 30 days"
|
16
|
-
stale-pr-message: "This PR has been automatically marked as stale because it has been open 90 days with no activity. Remove stale label or comment or this PR will be closed in 30 days"
|
17
|
-
close-issue-message: "This issue was automatically closed because of stale in 30 days"
|
18
|
-
close-pr-message: "This PR was automatically closed because of stale in 30 days"
|
19
|
-
stale-pr-label: "stale"
|
20
|
-
stale-issue-label: "stale"
|
21
|
-
exempt-issue-labels: "bug,enhancement,feature request,pending,work_in_progress,v1,v2"
|
22
|
-
exempt-pr-labels: "bug,enhancement,feature request,pending,work_in_progress,v1,v2"
|
@@ -1,131 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Fluentd
|
3
|
-
#
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
# you may not use this file except in compliance with the License.
|
6
|
-
# You may obtain a copy of the License at
|
7
|
-
#
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
#
|
10
|
-
# Unless required by applicable law or agreed to in writing, software
|
11
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
-
# See the License for the specific language governing permissions and
|
14
|
-
# limitations under the License.
|
15
|
-
#
|
16
|
-
|
17
|
-
require 'fluent/win32api'
|
18
|
-
|
19
|
-
module Fluent
|
20
|
-
module FileWrapper
|
21
|
-
def self.open(path, mode='r')
|
22
|
-
io = WindowsFile.new(path, mode).io
|
23
|
-
if block_given?
|
24
|
-
v = yield io
|
25
|
-
io.close
|
26
|
-
v
|
27
|
-
else
|
28
|
-
io
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.stat(path)
|
33
|
-
f = WindowsFile.new(path)
|
34
|
-
s = f.stat
|
35
|
-
f.close
|
36
|
-
s
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class WindowsFile
|
41
|
-
include File::Constants
|
42
|
-
|
43
|
-
attr_reader :io
|
44
|
-
|
45
|
-
INVALID_HANDLE_VALUE = -1
|
46
|
-
|
47
|
-
def initialize(path, mode='r')
|
48
|
-
@path = path
|
49
|
-
@io = File.open(path, mode2flags(mode))
|
50
|
-
@file_handle = Win32API._get_osfhandle(@io.to_i)
|
51
|
-
@io.instance_variable_set(:@file_index, self.ino)
|
52
|
-
def @io.ino
|
53
|
-
@file_index
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def close
|
58
|
-
@io.close
|
59
|
-
@file_handle = INVALID_HANDLE_VALUE
|
60
|
-
end
|
61
|
-
|
62
|
-
# To keep backward compatibility, we continue to use GetFileInformationByHandle()
|
63
|
-
# to get file id.
|
64
|
-
# Note that Ruby's File.stat uses GetFileInformationByHandleEx() with FileIdInfo
|
65
|
-
# and returned value is different with above one, former one is 64 bit while
|
66
|
-
# later one is 128bit.
|
67
|
-
def ino
|
68
|
-
by_handle_file_information = '\0'*(4+8+8+8+4+4+4+4+4+4) #72bytes
|
69
|
-
|
70
|
-
unless Win32API.GetFileInformationByHandle(@file_handle, by_handle_file_information)
|
71
|
-
return 0
|
72
|
-
end
|
73
|
-
|
74
|
-
by_handle_file_information.unpack("I11Q1")[11] # fileindex
|
75
|
-
end
|
76
|
-
|
77
|
-
def stat
|
78
|
-
raise Errno::ENOENT if delete_pending
|
79
|
-
s = File.stat(@path)
|
80
|
-
s.instance_variable_set :@ino, self.ino
|
81
|
-
def s.ino; @ino; end
|
82
|
-
s
|
83
|
-
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
def mode2flags(mode)
|
88
|
-
# Always inject File::Constants::SHARE_DELETE
|
89
|
-
# https://github.com/fluent/fluentd/pull/3585#issuecomment-1101502617
|
90
|
-
# To enable SHARE_DELETE, BINARY is also required.
|
91
|
-
# https://bugs.ruby-lang.org/issues/11218
|
92
|
-
# https://github.com/ruby/ruby/blob/d6684f063bc53e3cab025bd39526eca3b480b5e7/win32/win32.c#L6332-L6345
|
93
|
-
flags = BINARY | SHARE_DELETE
|
94
|
-
case mode.delete("b")
|
95
|
-
when "r"
|
96
|
-
flags |= RDONLY
|
97
|
-
when "r+"
|
98
|
-
flags |= RDWR
|
99
|
-
when "w"
|
100
|
-
flags |= WRONLY | CREAT | TRUNC
|
101
|
-
when "w+"
|
102
|
-
flags |= RDWR | CREAT | TRUNC
|
103
|
-
when "a"
|
104
|
-
flags |= WRONLY | CREAT | APPEND
|
105
|
-
when "a+"
|
106
|
-
flags |= RDWR | CREAT | APPEND
|
107
|
-
else
|
108
|
-
raise Errno::EINVAL.new("Unsupported mode by Fluent::FileWrapper: #{mode}")
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# DeletePending is a Windows-specific file state that roughly means
|
113
|
-
# "this file is queued for deletion, so close any open handlers"
|
114
|
-
#
|
115
|
-
# This flag can be retrieved via GetFileInformationByHandleEx().
|
116
|
-
#
|
117
|
-
# https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex
|
118
|
-
#
|
119
|
-
def delete_pending
|
120
|
-
file_standard_info = 0x01
|
121
|
-
bufsize = 1024
|
122
|
-
buf = '\0' * bufsize
|
123
|
-
|
124
|
-
unless Win32API.GetFileInformationByHandleEx(@file_handle, file_standard_info, buf, bufsize)
|
125
|
-
return false
|
126
|
-
end
|
127
|
-
|
128
|
-
return buf.unpack("QQICC")[3] != 0
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end if Fluent.windows?
|