class Fluent::AnalyzeConfigFilter
Fluentd filter plugin to analyze configuration usage.
For documentation on inspecting parsed configuration elements, see www.rubydoc.info/github/fluent/fluentd/Fluent/Config/Element
Public Instance Methods
configure(conf)
click to toggle source
Calls superclass method
# File lib/fluent/plugin/filter_analyze_config.rb, line 214 def configure(conf) super @log.info('analyze_config plugin: Starting to configure the plugin.') if File.file?(@google_fluentd_config_path) && File.file?(@google_fluentd_baseline_config_path) @log.info( 'analyze_config plugin: google-fluentd configuration file found at' \ " #{@google_fluentd_config_path}. " \ 'google-fluentd baseline configuration file found at' \ " #{@google_fluentd_baseline_config_path}. " \ 'google-fluentd Analyzing configuration.') utils = Common::Utils.new(@log) platform = utils.detect_platform(true) project_id = utils.get_project_id(platform, nil) vm_id = utils.get_vm_id(platform, nil) zone = utils.get_location(platform, nil, true) # All metadata parameters must now be set. utils.check_required_metadata_variables( platform, project_id, zone, vm_id) # Retrieve monitored resource. # Fail over to retrieve monitored resource via the legacy path if we # fail to get it from Metadata Agent. resource = utils.determine_agent_level_monitored_resource_via_legacy( platform, nil, false, vm_id, zone) unless Monitoring::MonitoringRegistryFactory.supports_monitoring_type( @monitoring_type) @log.warn( "analyze_config plugin: monitoring_type #{@monitoring_type} is " \ 'unknown; there will be no metrics.') end @registry = Monitoring::MonitoringRegistryFactory.create( @monitoring_type, project_id, resource, @gcm_service_address) # Export metrics every 60 seconds. timer_execute(:export_config_analysis_metrics, 60) { @registry.export } @log.info('analyze_config plugin: Registering counters.') enabled_plugins_counter = @registry.counter( :enabled_plugins, [:plugin_name, :is_default_plugin, :has_default_config, :has_ruby_snippet], 'Enabled plugins', 'agent.googleapis.com/agent/internal/logging/config', 'GAUGE') @log.info( 'analyze_config plugin: registered enable_plugins counter. ' \ "#{enabled_plugins_counter}") plugin_config_counter = @registry.counter( :plugin_config, [:plugin_name, :param, :is_present, :has_default_config], 'Configuration parameter usage for plugins relevant to Google Cloud.', 'agent.googleapis.com/agent/internal/logging/config', 'GAUGE') @log.info('analyze_config plugin: registered plugin_config counter. ' \ "#{plugin_config_counter}") config_bool_values_counter = @registry.counter( :config_bool_values, [:plugin_name, :param, :value], 'Values for bool parameters in Google Cloud plugins', 'agent.googleapis.com/agent/internal/logging/config', 'GAUGE') @log.info('analyze_config plugin: registered config_bool_values ' \ "counter. #{config_bool_values_counter}") config = parse_config(@google_fluentd_config_path) @log.debug( 'analyze_config plugin: successfully parsed google-fluentd' \ " configuration file at #{@google_fluentd_config_path}. #{config}") baseline_config = parse_config(@google_fluentd_baseline_config_path) @log.debug( 'analyze_config plugin: successfully parsed google-fluentd' \ ' baseline configuration file at' \ " #{@google_fluentd_baseline_config_path}: #{baseline_config}") # Create hash of all baseline elements by their plugin names. baseline_elements = Hash[baseline_config.elements.collect do |e| [default_plugin_name(e), e] end] baseline_google_element = baseline_config.elements.find do |e| e['@type'] == 'google_cloud' end # Look at each top-level config element and see whether it # matches the baseline value. # # Note on custom configurations: If the plugin has a custom # value (e.g. if a tail plugin has pos_file # /var/lib/google-fluentd/pos/my-custom-value.pos), then the # default_plugin_name (e.g. source/tail/my-custom-value) won't # be a key in baseline_elements below, so it won't be # used. Instead it will use the custom_plugin_name # (e.g. source/tail). config.elements.each do |e| plugin_name = default_plugin_name(e) if baseline_elements.key?(plugin_name) is_default_plugin = true has_default_config = (baseline_elements[plugin_name] == e) else plugin_name = custom_plugin_name(e) is_default_plugin = false has_default_config = false end enabled_plugins_counter.increment( labels: { plugin_name: plugin_name, is_default_plugin: is_default_plugin, has_default_config: has_default_config, has_ruby_snippet: embedded_ruby?(e) }, by: 1) # Additional metric for Google plugins (google_cloud and # detect_exceptions). next unless GOOGLE_PLUGIN_PARAMS.key?(e['@type']) GOOGLE_PLUGIN_PARAMS[e['@type']].each do |p| plugin_config_counter.increment( labels: { plugin_name: e['@type'], param: p, is_present: e.key?(p), has_default_config: (e.key?(p) && baseline_google_element.key?(p) && e[p] == baseline_google_element[p]) }, by: 1) next unless e.key?(p) && %w(true false).include?(e[p]) config_bool_values_counter.increment( labels: { plugin_name: e['@type'], param: p, value: e[p] == 'true' }, by: 1) end end @log.info( 'analyze_config plugin: Successfully finished analyzing config.') else @log.info( 'analyze_config plugin: google-fluentd configuration file does not ' \ "exist at #{@google_fluentd_config_path} or google-fluentd " \ 'baseline configuration file does not exist at' \ " #{@google_fluentd_baseline_config_path}. Skipping configuration " \ 'analysis.') end rescue => e # Do not crash the agent due to configuration analysis failures. @log.warn( 'analyze_config plugin: Failed to optionally analyze the ' \ "google-fluentd configuration file. Proceeding anyway. Error: #{e}. " \ "Trace: #{e.backtrace}") end
custom_plugin_name(e)
click to toggle source
Returns a name for identifying plugins not in our default config. This should not contain arbitrary user-supplied data.
# File lib/fluent/plugin/filter_analyze_config.rb, line 199 def custom_plugin_name(e) if KNOWN_PLUGINS.key?(e.name) && KNOWN_PLUGINS[e.name].include?(e['@type']) "#{e.name}/#{e['@type']}" else e.name.to_s end end
default_plugin_name(e)
click to toggle source
Returns a name for identifying plugins we ship by default.
# File lib/fluent/plugin/filter_analyze_config.rb, line 186 def default_plugin_name(e) case e['@type'] when 'syslog' "#{e.name}/syslog/#{e['protocol_type']}" when 'tail' "#{e.name}/tail/#{File.basename(e['pos_file'], '.pos')}" else "#{e.name}/#{e['@type']}" end end
embedded_ruby?(e)
click to toggle source
# File lib/fluent/plugin/filter_analyze_config.rb, line 208 def embedded_ruby?(e) (e.arg.include?('#{') || e.any? { |_, v| v.include?('#{') } || e.elements.any? { |ee| embedded_ruby?(ee) }) end
filter(tag, time, record)
click to toggle source
rubocop:disable Lint/UnusedMethodArgument
# File lib/fluent/plugin/filter_analyze_config.rb, line 379 def filter(tag, time, record) # Skip the actual filtering process. record end
parse_config(path)
click to toggle source
# File lib/fluent/plugin/filter_analyze_config.rb, line 170 def parse_config(path) data = File.open(path, 'r', &:read) fname = File.basename(path) basepath = File.dirname(path) eval_context = Kernel.binding # Override instance_eval so that LiteralParser does not actually # evaluate the embedded Ruby, but instead just returns the # source string. See # https://github.com/fluent/fluentd/blob/master/lib/fluent/config/literal_parser.rb def eval_context.instance_eval(code) code end Fluent::Config::V1Parser.parse(data, fname, basepath, eval_context) end
shutdown()
click to toggle source
Calls superclass method
# File lib/fluent/plugin/filter_analyze_config.rb, line 371 def shutdown super # Export metrics on shutdown. This is a best-effort attempt, and it might # fail, for instance if there was a recent write to the same time series. @registry.export unless @registry.nil? end
start()
click to toggle source
rubocop:enable Style/HashSyntax
Calls superclass method
# File lib/fluent/plugin/filter_analyze_config.rb, line 162 def start super @log = $log # rubocop:disable Style/GlobalVars @log.info( 'analyze_config plugin: Started the plugin to analyze configuration.') end