fluentd 1.13.2 → 1.14.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/windows-test.yaml +3 -3
  3. data/CHANGELOG.md +99 -0
  4. data/example/v0_12_filter.conf +2 -2
  5. data/lib/fluent/command/fluentd.rb +8 -0
  6. data/lib/fluent/compat/output.rb +9 -6
  7. data/lib/fluent/config/parser.rb +1 -1
  8. data/lib/fluent/config/v1_parser.rb +1 -1
  9. data/lib/fluent/event_router.rb +28 -1
  10. data/lib/fluent/plugin/bare_output.rb +49 -8
  11. data/lib/fluent/plugin/buffer.rb +84 -22
  12. data/lib/fluent/plugin/file_wrapper.rb +22 -0
  13. data/lib/fluent/plugin/filter.rb +35 -1
  14. data/lib/fluent/plugin/in_http.rb +21 -2
  15. data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
  16. data/lib/fluent/plugin/in_syslog.rb +13 -1
  17. data/lib/fluent/plugin/in_tail/position_file.rb +20 -18
  18. data/lib/fluent/plugin/in_tail.rb +46 -6
  19. data/lib/fluent/plugin/input.rb +39 -1
  20. data/lib/fluent/plugin/metrics.rb +119 -0
  21. data/lib/fluent/plugin/metrics_local.rb +96 -0
  22. data/lib/fluent/plugin/multi_output.rb +43 -6
  23. data/lib/fluent/plugin/out_copy.rb +1 -1
  24. data/lib/fluent/plugin/out_forward.rb +15 -7
  25. data/lib/fluent/plugin/output.rb +77 -36
  26. data/lib/fluent/plugin.rb +10 -1
  27. data/lib/fluent/plugin_helper/event_emitter.rb +8 -1
  28. data/lib/fluent/plugin_helper/metrics.rb +129 -0
  29. data/lib/fluent/plugin_helper/server.rb +4 -2
  30. data/lib/fluent/plugin_helper.rb +1 -0
  31. data/lib/fluent/root_agent.rb +6 -0
  32. data/lib/fluent/supervisor.rb +2 -0
  33. data/lib/fluent/system_config.rb +9 -1
  34. data/lib/fluent/version.rb +1 -1
  35. data/test/config/test_system_config.rb +6 -0
  36. data/test/plugin/in_tail/test_io_handler.rb +12 -4
  37. data/test/plugin/in_tail/test_position_file.rb +48 -8
  38. data/test/plugin/test_bare_output.rb +13 -0
  39. data/test/plugin/test_buffer.rb +8 -2
  40. data/test/plugin/test_file_wrapper.rb +11 -0
  41. data/test/plugin/test_filter.rb +11 -0
  42. data/test/plugin/test_in_http.rb +40 -0
  43. data/test/plugin/test_in_monitor_agent.rb +214 -8
  44. data/test/plugin/test_in_syslog.rb +35 -0
  45. data/test/plugin/test_in_tail.rb +72 -29
  46. data/test/plugin/test_input.rb +11 -0
  47. data/test/plugin/test_metrics.rb +294 -0
  48. data/test/plugin/test_metrics_local.rb +96 -0
  49. data/test/plugin/test_multi_output.rb +25 -1
  50. data/test/plugin/test_output.rb +16 -0
  51. data/test/plugin_helper/test_event_emitter.rb +29 -0
  52. data/test/plugin_helper/test_metrics.rb +137 -0
  53. data/test/test_plugin_classes.rb +102 -0
  54. data/test/test_root_agent.rb +30 -1
  55. metadata +11 -2
@@ -28,13 +28,47 @@ module Fluent
28
28
  include PluginLoggerMixin
29
29
  include PluginHelper::Mixin
30
30
 
31
- helpers_internal :event_emitter
31
+ helpers_internal :event_emitter, :metrics
32
32
 
33
33
  attr_reader :has_filter_with_time
34
34
 
35
35
  def initialize
36
36
  super
37
37
  @has_filter_with_time = has_filter_with_time?
38
+ @emit_records_metrics = nil
39
+ @emit_size_metrics = nil
40
+ @counter_mutex = Mutex.new
41
+ @enable_size_metrics = false
42
+ end
43
+
44
+ def emit_records
45
+ @emit_records_metrics.get
46
+ end
47
+
48
+ def emit_size
49
+ @emit_size_metrics.get
50
+ end
51
+
52
+ def configure(conf)
53
+ super
54
+
55
+ @emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "filter", name: "emit_records", help_text: "Number of count emit records")
56
+ @emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "filter", name: "emit_size", help_text: "Total size of emit events")
57
+ @enable_size_metrics = !!system_config.enable_size_metrics
58
+ end
59
+
60
+ def statistics
61
+ stats = {
62
+ 'emit_records' => @emit_records_metrics.get,
63
+ 'emit_size' => @emit_size_metrics.get,
64
+ }
65
+
66
+ { 'filter' => stats }
67
+ end
68
+
69
+ def measure_metrics(es)
70
+ @emit_records_metrics.add(es.size)
71
+ @emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
38
72
  end
39
73
 
40
74
  def filter(tag, time, record)
@@ -74,6 +74,8 @@ module Fluent::Plugin
74
74
  config_param :blocking_timeout, :time, default: 0.5
75
75
  desc 'Set a allow list of domains that can do CORS (Cross-Origin Resource Sharing)'
76
76
  config_param :cors_allow_origins, :array, default: nil
77
+ desc 'Tells browsers whether to expose the response to frontend when the credentials mode is "include".'
78
+ config_param :cors_allow_credentials, :bool, default: false
77
79
  desc 'Respond with empty gif image of 1x1 pixel.'
78
80
  config_param :respond_with_empty_img, :bool, default: false
79
81
  desc 'Respond status code with 204.'
@@ -112,6 +114,12 @@ module Fluent::Plugin
112
114
 
113
115
  super
114
116
 
117
+ if @cors_allow_credentials
118
+ if @cors_allow_origins.nil? || @cors_allow_origins.include?('*')
119
+ raise Fluent::ConfigError, "Cannot enable cors_allow_credentials without specific origins"
120
+ end
121
+ end
122
+
115
123
  m = if @parser_configs.first['@type'] == 'in_http'
116
124
  @parser_msgpack = parser_create(usage: 'parser_in_http_msgpack', type: 'msgpack')
117
125
  @parser_msgpack.time_key = nil
@@ -279,7 +287,10 @@ module Fluent::Plugin
279
287
  private
280
288
 
281
289
  def on_server_connect(conn)
282
- handler = Handler.new(conn, @km, method(:on_request), @body_size_limit, @format_name, log, @cors_allow_origins, @add_query_params)
290
+ handler = Handler.new(conn, @km, method(:on_request),
291
+ @body_size_limit, @format_name, log,
292
+ @cors_allow_origins, @cors_allow_credentials,
293
+ @add_query_params)
283
294
 
284
295
  conn.on(:data) do |data|
285
296
  handler.on_read(data)
@@ -356,7 +367,8 @@ module Fluent::Plugin
356
367
  class Handler
357
368
  attr_reader :content_type
358
369
 
359
- def initialize(io, km, callback, body_size_limit, format_name, log, cors_allow_origins, add_query_params)
370
+ def initialize(io, km, callback, body_size_limit, format_name, log,
371
+ cors_allow_origins, cors_allow_credentials, add_query_params)
360
372
  @io = io
361
373
  @km = km
362
374
  @callback = callback
@@ -365,6 +377,7 @@ module Fluent::Plugin
365
377
  @format_name = format_name
366
378
  @log = log
367
379
  @cors_allow_origins = cors_allow_origins
380
+ @cors_allow_credentials = cors_allow_credentials
368
381
  @idle = 0
369
382
  @add_query_params = add_query_params
370
383
  @km.add(self)
@@ -491,6 +504,9 @@ module Fluent::Plugin
491
504
  send_response_and_close(RES_200_STATUS, header, "")
492
505
  elsif include_cors_allow_origin
493
506
  header["Access-Control-Allow-Origin"] = @origin
507
+ if @cors_allow_credentials
508
+ header["Access-Control-Allow-Credentials"] = true
509
+ end
494
510
  send_response_and_close(RES_200_STATUS, header, "")
495
511
  else
496
512
  send_response_and_close(RES_403_STATUS, {}, "")
@@ -576,6 +592,9 @@ module Fluent::Plugin
576
592
  header['Access-Control-Allow-Origin'] = '*'
577
593
  elsif include_cors_allow_origin
578
594
  header['Access-Control-Allow-Origin'] = @origin
595
+ if @cors_allow_credentials
596
+ header["Access-Control-Allow-Credentials"] = true
597
+ end
579
598
  end
580
599
  end
581
600
 
@@ -238,7 +238,7 @@ module Fluent::Plugin
238
238
  'buffer_queue_length' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.queue.size },
239
239
  'buffer_timekeys' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.timekeys },
240
240
  'buffer_total_queued_size' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.stage_size + @buffer.queue_size },
241
- 'retry_count' => ->(){ instance_variable_defined?(:@num_errors) ? @num_errors : nil },
241
+ 'retry_count' => ->(){ respond_to?(:num_errors) ? num_errors : nil },
242
242
  }
243
243
 
244
244
  def all_plugins
@@ -335,7 +335,9 @@ module Fluent::Plugin
335
335
  }
336
336
 
337
337
  if pe.respond_to?(:statistics)
338
- obj.merge!(pe.statistics['output'] || {})
338
+ obj.merge!(pe.statistics.dig('output') || {})
339
+ obj.merge!(pe.statistics.dig('filter') || {})
340
+ obj.merge!(pe.statistics.dig('input') || {})
339
341
  end
340
342
 
341
343
  obj['retry'] = get_retry_info(pe.retry) if opts[:with_retry] && pe.instance_variable_defined?(:@retry)
@@ -89,6 +89,8 @@ module Fluent::Plugin
89
89
  config_param :source_hostname_key, :string, default: nil
90
90
  desc 'Try to resolve hostname from IP addresses or not.'
91
91
  config_param :resolve_hostname, :bool, default: nil
92
+ desc 'Check the remote connection is still available by sending a keepalive packet if this value is true.'
93
+ config_param :send_keepalive_packet, :bool, default: false
92
94
  desc 'The field name of source address of sender.'
93
95
  config_param :source_address_key, :string, default: nil
94
96
  desc 'The field name of the severity.'
@@ -143,6 +145,11 @@ module Fluent::Plugin
143
145
  end
144
146
 
145
147
  @_event_loop_run_timeout = @blocking_timeout
148
+
149
+ protocol = @protocol_type || @transport_config.protocol
150
+ if @send_keepalive_packet && protocol == :udp
151
+ raise Fluent::ConfigError, "send_keepalive_packet is available for tcp/tls"
152
+ end
146
153
  end
147
154
 
148
155
  def multi_workers_ready?
@@ -173,7 +180,12 @@ module Fluent::Plugin
173
180
 
174
181
  delimiter = octet_count_frame ? " " : @delimiter
175
182
  delimiter_size = delimiter.size
176
- server_create_connection(tls ? :in_syslog_tls_server : :in_syslog_tcp_server, @port, bind: @bind, resolve_name: @resolve_hostname) do |conn|
183
+ server_create_connection(
184
+ tls ? :in_syslog_tls_server : :in_syslog_tcp_server, @port,
185
+ bind: @bind,
186
+ resolve_name: @resolve_hostname,
187
+ send_keepalive_packet: @send_keepalive_packet
188
+ ) do |conn|
177
189
  conn.data do |data|
178
190
  buffer = conn.buffer
179
191
  buffer << data
@@ -22,19 +22,18 @@ module Fluent::Plugin
22
22
  UNWATCHED_POSITION = 0xffffffffffffffff
23
23
  POSITION_FILE_ENTRY_REGEX = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.freeze
24
24
 
25
- def self.load(file, follow_inodes, existing_paths, logger:)
26
- pf = new(file, follow_inodes, existing_paths, logger: logger)
27
- pf.load
25
+ def self.load(file, follow_inodes, existing_targets, logger:)
26
+ pf = new(file, follow_inodes, logger: logger)
27
+ pf.load(existing_targets)
28
28
  pf
29
29
  end
30
30
 
31
- def initialize(file, follow_inodes, existing_paths, logger: nil)
31
+ def initialize(file, follow_inodes, logger: nil)
32
32
  @file = file
33
33
  @logger = logger
34
34
  @file_mutex = Mutex.new
35
35
  @map = {}
36
36
  @follow_inodes = follow_inodes
37
- @existing_paths = existing_paths
38
37
  end
39
38
 
40
39
  def [](target_info)
@@ -60,8 +59,8 @@ module Fluent::Plugin
60
59
  end
61
60
  end
62
61
 
63
- def load
64
- compact
62
+ def load(existing_targets = nil)
63
+ compact(existing_targets)
65
64
 
66
65
  map = {}
67
66
  @file_mutex.synchronize do
@@ -118,9 +117,9 @@ module Fluent::Plugin
118
117
 
119
118
  private
120
119
 
121
- def compact
120
+ def compact(existing_targets = nil)
122
121
  @file_mutex.synchronize do
123
- entries = fetch_compacted_entries.values.map(&:to_entry_fmt)
122
+ entries = fetch_compacted_entries(existing_targets).values.map(&:to_entry_fmt)
124
123
 
125
124
  @file.pos = 0
126
125
  @file.truncate(0)
@@ -128,7 +127,7 @@ module Fluent::Plugin
128
127
  end
129
128
  end
130
129
 
131
- def fetch_compacted_entries
130
+ def fetch_compacted_entries(existing_targets = nil)
132
131
  entries = {}
133
132
 
134
133
  @file.pos = 0
@@ -151,23 +150,26 @@ module Fluent::Plugin
151
150
  end
152
151
 
153
152
  if @follow_inodes
154
- entries[ino] = Entry.new(path, pos, ino, file_pos + path.size + 1)
153
+ entries[ino] = Entry.new(path, pos, ino, file_pos + path.bytesize + 1)
155
154
  else
156
- entries[path] = Entry.new(path, pos, ino, file_pos + path.size + 1)
155
+ entries[path] = Entry.new(path, pos, ino, file_pos + path.bytesize + 1)
157
156
  end
158
157
  file_pos += line.size
159
158
  end
160
159
  end
161
160
 
162
- entries = remove_deleted_files_entries(entries, @existing_paths) if @follow_inodes
161
+ entries = remove_deleted_files_entries(entries, existing_targets)
163
162
  entries
164
163
  end
165
164
 
166
- def remove_deleted_files_entries(existent_entries, existing_paths)
167
- filtered_entries = existent_entries.select {|file_entry|
168
- existing_paths.key?(file_entry)
169
- }
170
- filtered_entries
165
+ def remove_deleted_files_entries(existent_entries, existing_targets)
166
+ if existing_targets
167
+ existent_entries.select { |path_or_ino|
168
+ existing_targets.key?(path_or_ino)
169
+ }
170
+ else
171
+ existent_entries
172
+ end
171
173
  end
172
174
  end
173
175
 
@@ -38,6 +38,7 @@ module Fluent::Plugin
38
38
  helpers :timer, :event_loop, :parser, :compat_parameters
39
39
 
40
40
  RESERVED_CHARS = ['/', '*', '%'].freeze
41
+ MetricsInfo = Struct.new(:opened, :closed, :rotated)
41
42
 
42
43
  class WatcherSetupError < StandardError
43
44
  def initialize(msg)
@@ -57,6 +58,7 @@ module Fluent::Plugin
57
58
  @pf = nil
58
59
  @ignore_list = []
59
60
  @shutdown_start_time = nil
61
+ @metrics = nil
60
62
  end
61
63
 
62
64
  desc 'The paths to read. Multiple paths can be specified, separated by comma.'
@@ -191,6 +193,10 @@ module Fluent::Plugin
191
193
  @read_bytes_limit_per_second = min_bytes
192
194
  end
193
195
  end
196
+ opened_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_opened_total", help_text: "Total number of opened files")
197
+ closed_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_closed_total", help_text: "Total number of closed files")
198
+ rotated_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_rotated_total", help_text: "Total number of rotated files")
199
+ @metrics = MetricsInfo.new(opened_file_metrics, closed_file_metrics, rotated_file_metrics)
194
200
  end
195
201
 
196
202
  def configure_tag
@@ -375,7 +381,7 @@ module Fluent::Plugin
375
381
 
376
382
  def setup_watcher(target_info, pe)
377
383
  line_buffer_timer_flusher = @multiline_mode ? TailWatcher::LineBufferTimerFlusher.new(log, @multiline_flush_interval, &method(:flush_buffer)) : nil
378
- tw = TailWatcher.new(target_info, pe, log, @read_from_head, @follow_inodes, method(:update_watcher), line_buffer_timer_flusher, method(:io_handler))
384
+ tw = TailWatcher.new(target_info, pe, log, @read_from_head, @follow_inodes, method(:update_watcher), line_buffer_timer_flusher, method(:io_handler), @metrics)
379
385
 
380
386
  if @enable_watch_timer
381
387
  tt = TimerTrigger.new(1, log) { tw.on_notify }
@@ -426,6 +432,7 @@ module Fluent::Plugin
426
432
 
427
433
  begin
428
434
  target_info = TargetInfo.new(target_info.path, Fluent::FileWrapper.stat(target_info.path).ino)
435
+ @tails.delete(target_info)
429
436
  @tails[target_info] = tw
430
437
  tw.on_notify
431
438
  rescue Errno::ENOENT, Errno::EACCES => e
@@ -491,10 +498,17 @@ module Fluent::Plugin
491
498
  new_position_entry = @pf[target_info]
492
499
 
493
500
  if new_position_entry.read_inode == 0
501
+ # When follow_inodes is true, it's not cleaned up by refresh_watcher.
502
+ # So it should be unwatched here explicitly.
503
+ rotated_tw.unwatched = true
504
+ # Make sure to delete old key, it has a different ino while the hash key is same.
505
+ @tails.delete(rotated_target_info)
494
506
  @tails[new_target_info] = setup_watcher(new_target_info, new_position_entry)
495
507
  @tails[new_target_info].on_notify
496
508
  end
497
509
  else
510
+ # Make sure to delete old key, it has a different ino while the hash key is same.
511
+ @tails.delete(rotated_target_info)
498
512
  @tails[new_target_info] = setup_watcher(new_target_info, pe)
499
513
  @tails[new_target_info].on_notify
500
514
  end
@@ -662,6 +676,19 @@ module Fluent::Plugin
662
676
  es
663
677
  end
664
678
 
679
+ def statistics
680
+ stats = super
681
+
682
+ stats = {
683
+ 'input' => stats["input"].merge({
684
+ 'opened_file_count' => @metrics.opened.get,
685
+ 'closed_file_count' => @metrics.closed.get,
686
+ 'rotated_file_count' => @metrics.rotated.get,
687
+ })
688
+ }
689
+ stats
690
+ end
691
+
665
692
  private
666
693
 
667
694
  def io_handler(watcher, path)
@@ -674,6 +701,7 @@ module Fluent::Plugin
674
701
  open_on_every_update: @open_on_every_update,
675
702
  from_encoding: @from_encoding,
676
703
  encoding: @encoding,
704
+ metrics: @metrics,
677
705
  &method(:receive_lines)
678
706
  )
679
707
  end
@@ -709,7 +737,7 @@ module Fluent::Plugin
709
737
  end
710
738
 
711
739
  class TailWatcher
712
- def initialize(target_info, pe, log, read_from_head, follow_inodes, update_watcher, line_buffer_timer_flusher, io_handler_build)
740
+ def initialize(target_info, pe, log, read_from_head, follow_inodes, update_watcher, line_buffer_timer_flusher, io_handler_build, metrics)
713
741
  @path = target_info.path
714
742
  @ino = target_info.ino
715
743
  @pe = pe || MemoryPositionEntry.new
@@ -721,6 +749,7 @@ module Fluent::Plugin
721
749
  @line_buffer_timer_flusher = line_buffer_timer_flusher
722
750
  @io_handler = nil
723
751
  @io_handler_build = io_handler_build
752
+ @metrics = metrics
724
753
  @watchers = []
725
754
  end
726
755
 
@@ -754,7 +783,7 @@ module Fluent::Plugin
754
783
  end
755
784
 
756
785
  def eof?
757
- @io_handler.eof?
786
+ @io_handler.nil? || @io_handler.eof?
758
787
  end
759
788
 
760
789
  def on_notify
@@ -831,7 +860,7 @@ module Fluent::Plugin
831
860
  # new watcher, and old watcher will be closed by stop_watcher in refresh_watchers method
832
861
  # don't want to swap state because we need latest read offset in pos file even after rotate_wait
833
862
  if stat
834
- target_info = TargetInfo.new(@path, stat)
863
+ target_info = TargetInfo.new(@path, stat.ino)
835
864
  @update_watcher.call(target_info, @pe)
836
865
  end
837
866
  else
@@ -847,6 +876,7 @@ module Fluent::Plugin
847
876
  @log.info "detected rotation of #{@path}"
848
877
  @io_handler = io_handler
849
878
  end
879
+ @metrics.rotated.inc
850
880
  end
851
881
  end
852
882
 
@@ -926,7 +956,7 @@ module Fluent::Plugin
926
956
 
927
957
  attr_accessor :shutdown_timeout
928
958
 
929
- def initialize(watcher, path:, read_lines_limit:, read_bytes_limit_per_second:, log:, open_on_every_update:, from_encoding: nil, encoding: nil, &receive_lines)
959
+ def initialize(watcher, path:, read_lines_limit:, read_bytes_limit_per_second:, log:, open_on_every_update:, from_encoding: nil, encoding: nil, metrics:, &receive_lines)
930
960
  @watcher = watcher
931
961
  @path = path
932
962
  @read_lines_limit = read_lines_limit
@@ -945,6 +975,7 @@ module Fluent::Plugin
945
975
  @shutdown_timeout = SHUTDOWN_TIMEOUT
946
976
  @shutdown_mutex = Mutex.new
947
977
  @eof = false
978
+ @metrics = metrics
948
979
 
949
980
  @log.info "following tail of #{@path}"
950
981
  end
@@ -964,6 +995,7 @@ module Fluent::Plugin
964
995
  if @io && !@io.closed?
965
996
  @io.close
966
997
  @io = nil
998
+ @metrics.closed.inc
967
999
  end
968
1000
  end
969
1001
 
@@ -1051,11 +1083,15 @@ module Fluent::Plugin
1051
1083
  def open
1052
1084
  io = Fluent::FileWrapper.open(@path)
1053
1085
  io.seek(@watcher.pe.read_pos + @fifo.bytesize)
1086
+ @metrics.opened.inc
1054
1087
  io
1055
1088
  rescue RangeError
1056
1089
  io.close if io
1057
1090
  raise WatcherSetupError, "seek error with #{@path}: file position = #{@watcher.pe.read_pos.to_s(16)}, reading bytesize = #{@fifo.bytesize.to_s(16)}"
1058
- rescue Errno::ENOENT, Errno::EACCES
1091
+ rescue Errno::EACCES => e
1092
+ @log.warn "#{e}"
1093
+ nil
1094
+ rescue Errno::ENOENT
1059
1095
  nil
1060
1096
  end
1061
1097
 
@@ -1100,6 +1136,10 @@ module Fluent::Plugin
1100
1136
  def opened?
1101
1137
  false
1102
1138
  end
1139
+
1140
+ def eof?
1141
+ true
1142
+ end
1103
1143
  end
1104
1144
 
1105
1145
  class RotateHandler
@@ -27,7 +27,45 @@ module Fluent
27
27
  include PluginLoggerMixin
28
28
  include PluginHelper::Mixin
29
29
 
30
- helpers_internal :event_emitter
30
+ helpers_internal :event_emitter, :metrics
31
+
32
+ def initialize
33
+ super
34
+ @emit_records_metrics = nil
35
+ @emit_size_metrics = nil
36
+ @counter_mutex = Mutex.new
37
+ @enable_size_metrics = false
38
+ end
39
+
40
+ def emit_records
41
+ @emit_records_metrics.get
42
+ end
43
+
44
+ def emit_size
45
+ @emit_size_metrics.get
46
+ end
47
+
48
+ def configure(conf)
49
+ super
50
+
51
+ @emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "emit_records", help_text: "Number of count emit records")
52
+ @emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "emit_size", help_text: "Total size of emit events")
53
+ @enable_size_metrics = !!system_config.enable_size_metrics
54
+ end
55
+
56
+ def statistics
57
+ stats = {
58
+ 'emit_records' => @emit_records_metrics.get,
59
+ 'emit_size' => @emit_size_metrics.get,
60
+ }
61
+
62
+ { 'input' => stats }
63
+ end
64
+
65
+ def metric_callback(es)
66
+ @emit_records_metrics.add(es.size)
67
+ @emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
68
+ end
31
69
 
32
70
  def multi_workers_ready?
33
71
  false
@@ -0,0 +1,119 @@
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 'socket'
18
+
19
+ require 'fluent/plugin/base'
20
+
21
+ require 'fluent/log'
22
+ require 'fluent/unique_id'
23
+ require 'fluent/plugin_id'
24
+
25
+ module Fluent
26
+ module Plugin
27
+ class Metrics < Base
28
+ include PluginId
29
+ include PluginLoggerMixin
30
+ include UniqueId::Mixin
31
+
32
+ DEFAULT_TYPE = 'local'
33
+
34
+ configured_in :metrics
35
+
36
+ config_param :default_labels, :hash, default: {agent: "Fluentd", hostname: "#{Socket.gethostname}"}
37
+ config_param :labels, :hash, default: {}
38
+
39
+ attr_reader :use_gauge_metric
40
+ attr_reader :has_methods_for_gauge, :has_methods_for_counter
41
+
42
+ def initialize
43
+ super
44
+
45
+ @has_methods_for_counter = false
46
+ @has_methods_for_gauge = false
47
+ @use_gauge_metric = false
48
+ end
49
+
50
+ def configure(conf)
51
+ super
52
+
53
+ if use_gauge_metric
54
+ @has_methods_for_gauge = has_methods_for_gauge?
55
+ else
56
+ @has_methods_for_counter = has_methods_for_counter?
57
+ end
58
+ end
59
+
60
+ # Some metrics should be counted by gauge.
61
+ # ref: https://prometheus.io/docs/concepts/metric_types/#gauge
62
+ def use_gauge_metric=(use_gauge_metric=false)
63
+ @use_gauge_metric = use_gauge_metric
64
+ end
65
+
66
+ def create(namespace:, subsystem:,name:,help_text:,labels: {})
67
+ # This API is for cmetrics type.
68
+ end
69
+
70
+ def get
71
+ raise NotImplementedError, "Implement this method in child class"
72
+ end
73
+
74
+ def inc
75
+ raise NotImplementedError, "Implement this method in child class"
76
+ end
77
+
78
+ def dec
79
+ raise NotImplementedError, "Implement this method in child class"
80
+ end
81
+
82
+ def add(value)
83
+ raise NotImplementedError, "Implement this method in child class"
84
+ end
85
+
86
+ def sub(value)
87
+ raise NotImplementedError, "Implement this method in child class"
88
+ end
89
+
90
+ def set(value)
91
+ raise NotImplementedError, "Implement this method in child class"
92
+ end
93
+
94
+ private
95
+
96
+ def has_methods_for_counter?
97
+ implemented_methods = self.class.instance_methods(false)
98
+
99
+ if [:get, :inc, :add].all? {|e| implemented_methods.include?(e)} &&
100
+ [:set].all?{|e| self.class.method_defined?(e)}
101
+ true
102
+ else
103
+ raise "BUG: metrics plugin on counter mode MUST implement `get`, `inc`, `add` methods. And aliased `set` methods should be aliased from another method"
104
+ end
105
+ end
106
+
107
+ def has_methods_for_gauge?
108
+ implemented_methods = self.class.instance_methods(false)
109
+
110
+ if [:get, :inc, :add].all? {|e| implemented_methods.include?(e)} &&
111
+ [:set, :dec, :sub].all?{|e| self.class.method_defined?(e)}
112
+ true
113
+ else
114
+ raise "BUG: metrics plugin on gauge mode MUST implement `get`, `inc`, and `add` methods. And `dec`, `sub`, and `set` methods should be aliased from other methods"
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end