# encoding: utf-8
require "logstash/filters/base"
require "logstash/namespace"
require "tempfile"
# Parse user agent strings into structured data based on BrowserScope data
#
# UserAgent filter, adds information about user agent like family, operating
# system, version, and device
#
# Logstash releases ship with the regexes.yaml database made available from
# ua-parser with an Apache 2.0 license. For more details on ua-parser, see
# .
class LogStash::Filters::UserAgent < LogStash::Filters::Base
config_name "useragent"
# The field containing the user agent string. If this field is an
# array, only the first value will be used.
config :source, :validate => :string, :required => true
# The name of the field to assign user agent data into.
#
# If not specified user agent data will be stored in the root of the event.
config :target, :validate => :string
# `regexes.yaml` file to use
#
# If not specified, this will default to the `regexes.yaml` that ships
# with logstash.
#
# You can find the latest version of this here:
#
config :regexes, :validate => :string
# A string to prepend to all of the extracted keys
config :prefix, :validate => :string, :default => ''
public
def register
require 'user_agent_parser'
if @regexes.nil?
begin
@parser = UserAgentParser::Parser.new()
rescue Exception => e
begin
path = ::File.expand_path('../../../vendor/regexes.yaml', ::File.dirname(__FILE__))
@parser = UserAgentParser::Parser.new(:patterns_path => path)
rescue => ex
raise "Failed to cache, due to: #{ex}\n"
end
end
else
@logger.info("Using user agent regexes", :regexes => @regexes)
@parser = UserAgentParser::Parser.new(:patterns_path => @regexes)
end
end #def register
public
def filter(event)
return unless filter?(event)
ua_data = nil
useragent = event[@source]
useragent = useragent.first if useragent.is_a? Array
begin
ua_data = @parser.parse(useragent)
rescue Exception => e
@logger.error("Uknown error while parsing user agent data", :exception => e, :field => @source, :event => event)
end
if !ua_data.nil?
if @target.nil?
# default write to the root of the event
target = event
else
target = event[@target] ||= {}
end
# UserAgentParser outputs as US-ASCII.
target[@prefix + "name"] = ua_data.name.force_encoding(Encoding::UTF_8)
#OSX, Andriod and maybe iOS parse correctly, ua-agent parsing for Windows does not provide this level of detail
unless ua_data.os.nil?
target[@prefix + "os"] = ua_data.os.to_s.force_encoding(Encoding::UTF_8)
target[@prefix + "os_name"] = ua_data.os.name.to_s.force_encoding(Encoding::UTF_8)
target[@prefix + "os_major"] = ua_data.os.version.major.to_s.force_encoding(Encoding::UTF_8) unless ua_data.os.version.nil?
target[@prefix + "os_minor"] = ua_data.os.version.minor.to_s.force_encoding(Encoding::UTF_8) unless ua_data.os.version.nil?
end
target[@prefix + "device"] = ua_data.device.to_s.force_encoding(Encoding::UTF_8) if not ua_data.device.nil?
if not ua_data.version.nil?
ua_version = ua_data.version
target[@prefix + "major"] = ua_version.major.force_encoding(Encoding::UTF_8) if ua_version.major
target[@prefix + "minor"] = ua_version.minor.force_encoding(Encoding::UTF_8) if ua_version.minor
target[@prefix + "patch"] = ua_version.patch.force_encoding(Encoding::UTF_8) if ua_version.patch
target[@prefix + "build"] = ua_version.patch_minor.force_encoding(Encoding::UTF_8) if ua_version.patch_minor
end
filter_matched(event)
end
end # def filter
end # class LogStash::Filters::UserAgent