class LogStash::Filters::GeoIP

Add GeoIP fields from Maxmind database

GeoIP filter, adds information about the geographical location of IP addresses.

Starting at version 1.3.0 of logstash, a [geoip] field is created if the GeoIP lookup returns a latitude and longitude. The field is stored in [GeoJSON](geojson.org/geojson-spec.html) format. Additionally, the default Elasticsearch template provided with the [elasticsearch output](../outputs/elasticsearch.html) maps the [geoip] field to a [geo_point](www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-geo-point-type.html).

As this field is a geo_point and it is still valid GeoJSON, you get the awesomeness of Elasticsearch's geospatial query, facet and filter functions and the flexibility of having GeoJSON for all other applications (like Kibana's [bettermap panel](github.com/elasticsearch/kibana/tree/master/src/app/panels/bettermap)).

Logstash releases ship with the GeoLiteCity database made available from Maxmind with a CCA-ShareAlike 3.0 license. For more details on GeoLite, see <www.maxmind.com/en/geolite>.

Public Instance Methods

filter(event) click to toggle source
# File lib/logstash/filters/geoip.rb, line 107
def filter(event)
  return unless filter?(event)
  geo_data = nil

  begin
    ip = event[@source]
    ip = ip.first if ip.is_a? Array
    geo_data = @geoip.send(@geoip_type, ip)
  rescue SocketError => e
    @logger.error("IP Field contained invalid IP address or hostname", :field => @field, :event => event)
  rescue Exception => e
    @logger.error("Unknown error while looking up GeoIP data", :exception => e, :field => @field, :event => event)
  end

  return if geo_data.nil?

  geo_data_hash = geo_data.to_hash
  geo_data_hash.delete(:request)
  event[@target] = {} if event[@target].nil?
  geo_data_hash.each do |key, value|
    next if value.nil? || (value.is_a?(String) && value.empty?)
    if @fields.nil? || @fields.empty?
      # no fields requested, so add all geoip hash items to
      # the event's fields.
      # convert key to string (normally a Symbol)
      event[@target][key.to_s] = value
    elsif @fields.include?(key.to_s)
      # Check if the key is in our fields array
      # convert key to string (normally a Symbol)
      event[@target][key.to_s] = value
    end
  end # geo_data_hash.each
  if event[@target].key?('latitude') && event[@target].key?('longitude')
    # If we have latitude and longitude values, add the location field as GeoJSON array
    event[@target]['location'] = [ event[@target]["longitude"].to_f, event[@target]["latitude"].to_f ]
  end
  filter_matched(event)
end
register() click to toggle source
# File lib/logstash/filters/geoip.rb, line 64
def register
  require "geoip"
  if @database.nil?
    if __FILE__ =~ /^(jar:)?file:\/.+!.+/
      begin
        # Running from a jar, assume GeoLiteCity.dat is at the root.
        jar_path = [__FILE__.split("!").first, "/GeoLiteCity.dat"].join("!")
        tmp_file = Tempfile.new('logstash-geoip')
        tmp_file.write(File.read(jar_path))
        tmp_file.close # this file is reaped when ruby exits
        @database = tmp_file.path
      rescue => ex
        raise "Failed to cache, due to: #{ex}\n#{ex.backtrace}"
      end
    else
      if File.exists?("GeoLiteCity.dat")
        @database = "GeoLiteCity.dat"
      elsif File.exists?("vendor/geoip/GeoLiteCity.dat")
        @database = "vendor/geoip/GeoLiteCity.dat"
      else
        raise "You must specify 'database => ...' in your geoip filter"
      end
    end
  end
  @logger.info("Using geoip database", :path => @database)
  @geoip = ::GeoIP.new(@database)

  @geoip_type = case @geoip.database_type
  when GeoIP::GEOIP_CITY_EDITION_REV0, GeoIP::GEOIP_CITY_EDITION_REV1
    :city
  when GeoIP::GEOIP_COUNTRY_EDITION
    :country
  when GeoIP::GEOIP_ASNUM_EDITION
    :asn
  when GeoIP::GEOIP_ISP_EDITION, GeoIP::GEOIP_ORG_EDITION
    :isp
  else
    raise RuntimeException.new "This GeoIP database is not currently supported"
  end

end