fluentd 1.15.2 → 1.15.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- File.open(File.join(@tmp, "exist_#{i}.log"), 'a'){|f| } # open(create) and close
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
- File.open(File.join(@tmp, "exist_#{i}.log"), 'a')
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:
@@ -1,5 +1,5 @@
1
- require_relative '../helper'
2
- require 'fluent/plugin/file_wrapper'
1
+ require_relative 'helper'
2
+ require 'fluent/file_wrapper'
3
3
 
4
4
  class FileWrapperTest < Test::Unit::TestCase
5
5
  TMP_DIR = File.dirname(__FILE__) + "/../tmp/file_wrapper#{ENV['TEST_ENV_NUMBER']}"
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
- TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/tmp/log/#{ENV['TEST_ENV_NUMBER']}")
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
- FileUtils.rm_rf(TMP_DIR)
13
- FileUtils.mkdir_p(TMP_DIR)
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 = "#{TMP_DIR}/log-dev-io-#{rotate_size}-#{rotate_age}"
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 = "#{TMP_DIR}/log-dev-io-#{rotate_size}-#{rotate_age}"
598
+ path = "#{@tmp_dir}/log-dev-io-#{rotate_size}-#{rotate_age}"
589
599
  path0 = path + '.0'
590
600
  path1 = path + '.1'
591
601
 
@@ -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
- TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/tmp/supervisor#{ENV['TEST_ENV_NUMBER']}")
26
- TMP_ROOT_DIR = File.join(TMP_DIR, 'root')
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
- FileUtils.rm_rf(TMP_DIR)
30
- FileUtils.mkdir_p(TMP_DIR)
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
- File.open(path, "w") {|f| f.write data }
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 #{TMP_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 TMP_ROOT_DIR, sys_conf.root_dir
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 "#{TMP_ROOT_DIR}"
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
- TMP_ROOT_DIR,
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 = "#{TMP_DIR}/dir/test_load_config.conf"
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 = "#{TMP_DIR}/dir/test_load_config_log.conf"
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 = "#{TMP_DIR}/dir/test_load_config.conf"
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: "#{TMP_DIR}/test", log_rotate_age: rotate_age, log_rotate_size: 10
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: "#{TMP_DIR}/test.log", config_path: file.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: "#{TMP_DIR}/test.log", config_path: file.path, config_file_type: :yaml,
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.2
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-08-22 00:00:00.000000000 Z
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?