#!/usr/bin/env ruby ############################################################################# # This example script shows custom parsing of API requests from a log file. ############################################################################# require "log_stats" require "log_stats/config" require "json" require "uri" parse_time = Proc.new { |line| line[/\b20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d/] } def endpoint_pattern(url) Regexp.new('^' + url.gsub(%r{/:id/}, '/[^/]+/').gsub(%r{/:id$}, '/[^/]+') + '$') end ENDPOINTS = [ 'restapi.example.(se|no|dk)/api/user/:id/password', 'restapi.example.(se|no|dk)/api/user/:id/password', 'restapi.example.(se|no|dk)/api/user/find/(userName|email)/:id', 'restapi.example.(se|no|dk)/api/order/extUserId/:id', 'restapi.example.(se|no|dk)/api/voucher/.+' ].map do |url| {url: url, pattern: endpoint_pattern(url)} end def endpoint(api_call) uri = URI(api_call[:url]) # NOTE: don't include digits in the path as then we will have too many unique paths path_without_digits = uri.path.gsub(%r{/\d+}, '/:id') url = uri.host + path_without_digits if endpoint = ENDPOINTS.detect { |endpoint| endpoint[:pattern].match(url) } endpoint[:url] else url end end def response_time_95(item) -1 * item[:fields][:response_time][:percentiles][0.95] end custom_config = { events: { requests: { # NOTE: matches Heroku router lines. Also matches Papertrails slightly modified lines. line_pattern: /\s(heroku\/router)|(heroku\[router\]:)\s/, fields: [ {name: :time, parse: parse_time}, {name: :method}, {name: :host}, {name: :path}, {name: :status, numeric: true}, {name: :code, optional: true}, {name: :service, numeric: true} ], top_list_limit: 100, apdex: {tolerating: 500, frustrated: 2000}, apdex_goal: 0.9, stats: false # Skip listing stats per request path to keep output size manageable }, api_calls: { # 2017-02-19T06:21:25.522274+00:00 app[worker.2]: [WARN] [Vac::Request] Slow response time for url=http://sumore02.example.dk/api/search/categories/160145/assets/ method=get status=304 size= response_time=141 line_pattern: /\s\[Vac::Request\] Slow response time\s/, fields: [ {name: :time, parse: parse_time}, {name: :url}, {name: :method}, {name: :response_time, numeric: true, events: true} ], group_by: { hostname: { id: Proc.new { |api_call| URI(api_call[:url]).host }, sort_by: method(:response_time_95) }, endpoint: { id: method(:endpoint), sort_by: method(:response_time_95) } # NOTE: Grouping by HTTP method doesn't really add much at the moment, so commenting out for now # method: { # id: Proc.new { |api_call| URI(api_call[:method]) } # } }, events: false, # Skip listing all individual API calls to keep output size manageable limit: 5 } }, verbose: true } config = LogStats::Config.default_config. merge(custom_config). merge(LogStats::Config.env_config) log_file_data = LogStats::Logger.elapsed(config, "Reading file") { ARGF.readlines } data = LogStats.get_data(log_file_data, config) puts JSON.pretty_generate(data)