Skip to content

Instantly share code, notes, and snippets.

@dblock
Created June 30, 2023 23:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dblock/6d861e6aed3c4a542045cb68680b8543 to your computer and use it in GitHub Desktop.
Save dblock/6d861e6aed3c4a542045cb68680b8543 to your computer and use it in GitHub Desktop.
Process logstash output.
require 'active_support'
if ARGV.length < 1
puts "usage: process [file]"
exit
end
class Request
attr_reader :conn
attr_reader :verb
attr_reader :version
attr_reader :path
attr_accessor :response
attr_reader :headers
attr_reader :logs
attr_reader :data
def initialize(conn, line)
@conn = conn
@verb, @path, @version = line.split(' ')
@headers = {}
@data = []
@logs = []
end
def successful?
response && response.status.to_i < 299
end
def failed?
response && response.status.to_i >= 300
end
def to_s
"#{verb} #{path} => #{response}"
end
def character
if successful?
'.'
elsif failed?
'x'
else
'_'
end
end
end
class Response
attr_reader :status
attr_reader :code
attr_reader :conn
attr_reader :version
attr_reader :headers
attr_reader :data
attr_reader :logs
def initialize(conn, line)
@conn = conn
@version, @status, @code = line.split(' ')
@headers = {}
@data = []
@logs = []
end
def to_s
"#{code} #{status}"
end
end
class Requests < Array
def successful
select(&:successful?)
end
def failed
select(&:failed?)
end
end
class Connection
attr_reader :id
attr_reader :requests
def initialize(id)
@id = id
@requests = Requests.new
end
def has_failures?
requests.any?(&:failed?)
end
def to_s
"#{id}: #{requests.map(&:character).join}"
end
end
class Connections < Hash
def with_failures
values.select(&:has_failures?)
end
end
connections = Connections.new
File.foreach(ARGV[0]) do |line|
timestamp, log = line.split(/\t/, 2)
next unless log
info, line = log[1..].split('] ', 2)
ts, level, source, cat, cluster = info.split("][")
conn, direction, rest = line.split(' ', 3)
source.strip!
if direction == '>>'
if ["POST", "GET", "DELETE", "PUT"].any? { |verb| rest.start_with?("#{verb} ") }
request = Request.new(conn, rest)
connections[conn] ||= Connection.new(conn)
connections[conn].requests << request
end
elsif direction == '<<'
if rest.start_with?("HTTP/1.1 ")
response = Response.new(conn, rest)
request = connections[conn]&.requests&.last
request.response = response if request
end
end
end
connections.each_pair do |_id, conn|
puts "#{conn}, total=#{conn.requests.count}, success=#{conn.requests.successful.count}, failure=#{conn.requests.failed.count}"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment