Class: Fluent::GeoIP

Inherits:
Object
  • Object
show all
Defined in:
lib/fluent/plugin/geoip.rb

Constant Summary collapse

BACKEND_LIBRARIES =
[:geoip, :geoip2_compat, :geoip2_c]
REGEXP_PLACEHOLDER_SINGLE =
/^\$\{(?<geoip_key>-?[^\[]+)\[['"](?<record_key>-?[^'"]+)['"]\]\}$/
REGEXP_PLACEHOLDER_SCAN =
/['"]?(\$\{[^\}]+?\})['"]?/
GEOIP_KEYS =
%w(city latitude longitude country_code3 country_code country_name dma_code area_code region)
GEOIP2_COMPAT_KEYS =
%w(city country_code country_name latitude longitude postal_code region region_name)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(plugin, conf) ⇒ GeoIP

Returns a new instance of GeoIP.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/fluent/plugin/geoip.rb', line 17

def initialize(plugin, conf)
  @map = {}
  plugin.geoip_lookup_key = plugin.geoip_lookup_key.split(/\s*,\s*/)
  @geoip_lookup_key = plugin.geoip_lookup_key
  @skip_adding_null_record = plugin.skip_adding_null_record
  @log = plugin.log

  # enable_key_* format (legacy format)
  conf.keys.select{|k| k =~ /^enable_key_/}.each do |key|
    geoip_key = key.sub('enable_key_','')
    raise Fluent::ConfigError, "geoip: unsupported key #{geoip_key}" unless GEOIP_KEYS.include?(geoip_key)
    @geoip_lookup_key.zip(conf[key].split(/\s*,\s*/)).each do |lookup_field,record_key|
      if record_key.nil?
        raise Fluent::ConfigError, "geoip: missing value found at '#{key} #{lookup_field}'"
      end
      @map[record_key] = "${#{geoip_key}['#{lookup_field}']}"
    end
  end
  if conf.keys.select{|k| k =~ /^enable_key_/}.size > 0
    log.warn "geoip: 'enable_key_*' config format is obsoleted. use <record></record> directive for now."
    log.warn "geoip: for further details referable to https://github.com/y-ken/fluent-plugin-geoip"
  end

  # <record></record> directive
  conf.elements.select { |element| element.name == 'record' }.each { |element|
    element.each_pair { |k, v|
      element.has_key?(k) # to suppress unread configuration warning
      v = v[1..v.size-2] if quoted_value?(v)
      @map[k] = v
      validate_json = Proc.new {
        begin
          dummy_text = Yajl::Encoder.encode('dummy_text')
          Yajl::Parser.parse(v.gsub(REGEXP_PLACEHOLDER_SCAN, dummy_text))
        rescue Yajl::ParseError => e
          raise Fluent::ConfigError, "geoip: failed to parse '#{v}' as json."
        end
      }
      validate_json.call if json?(v.tr('\'"\\', ''))
    }
  }
  @placeholder_keys = @map.values.join.scan(REGEXP_PLACEHOLDER_SCAN).map{ |placeholder| placeholder[0] }.uniq
  @placeholder_keys.each do |key|
    geoip_key = key.match(REGEXP_PLACEHOLDER_SINGLE)[:geoip_key]
    case plugin.backend_library
    when :geoip
      raise Fluent::ConfigError, "#{plugin.backend_library}: unsupported key #{geoip_key}" unless GEOIP_KEYS.include?(geoip_key)
    when :geoip2_compat
      raise Fluent::ConfigError, "#{plugin.backend_library}: unsupported key #{geoip_key}" unless GEOIP2_COMPAT_KEYS.include?(geoip_key)
    when :geoip2_c
      # Nothing to do.
      # We cannot define supported key(s) before we fetch values from GeoIP2 database
      # because geoip2_c can fetch any fields in GeoIP2 database.
    end
  end

  if plugin.is_a?(Fluent::BufferedOutput)
    @placeholder_expander = PlaceholderExpander.new
    unless have_tag_option?(plugin)
      raise Fluent::ConfigError, "geoip: required at least one option of 'tag', 'remove_tag_prefix', 'remove_tag_suffix', 'add_tag_prefix', 'add_tag_suffix'."
    end
  end

  @geoip = load_database(plugin)
end

Instance Attribute Details

#logObject (readonly)

Returns the value of attribute log.



15
16
17
# File 'lib/fluent/plugin/geoip.rb', line 15

def log
  @log
end

Instance Method Details

#add_geoip_field(record) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/fluent/plugin/geoip.rb', line 82

def add_geoip_field(record)
  placeholder = create_placeholder(geolocate(get_address(record)))
  return record if @skip_adding_null_record && placeholder.values.first.nil?
  @map.each do |record_key, value|
    if value.match(REGEXP_PLACEHOLDER_SINGLE)
      rewrited = placeholder[value]
    elsif json?(value)
      rewrited = value.gsub(REGEXP_PLACEHOLDER_SCAN) {|match|
        match = match[1..match.size-2] if quoted_value?(match)
        Yajl::Encoder.encode(placeholder[match])
      }
      rewrited = parse_json(rewrited)
    else
      rewrited = value.gsub(REGEXP_PLACEHOLDER_SCAN, placeholder)
    end
    record[record_key] = rewrited
  end
  return record
end