Skip to content

Commit

Permalink
Merge pull request #9 from epimorphics/dev
Browse files Browse the repository at this point in the history
Merge changes to main for a first release
  • Loading branch information
bogdanadrianmarc authored Jan 26, 2021
2 parents 6b20344 + 33bb909 commit ac6fa08
Show file tree
Hide file tree
Showing 11 changed files with 368 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gemspec
81 changes: 81 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
PATH
remote: .
specs:
json_rails_logger (0.1.0)
json
lograge
railties

GEM
remote: https://rubygems.org/
specs:
actionpack (6.1.1)
actionview (= 6.1.1)
activesupport (= 6.1.1)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionview (6.1.1)
activesupport (= 6.1.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activesupport (6.1.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
builder (3.2.4)
concurrent-ruby (1.1.8)
crass (1.0.6)
erubi (1.10.0)
i18n (1.8.7)
concurrent-ruby (~> 1.0)
json (2.5.1)
lograge (0.11.2)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.9.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
method_source (1.0.0)
minitest (5.14.3)
nokogiri (1.11.1-x86_64-darwin)
racc (~> 1.4)
racc (1.5.2)
rack (2.2.3)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.3.0)
loofah (~> 2.3)
railties (6.1.1)
actionpack (= 6.1.1)
activesupport (= 6.1.1)
method_source
rake (>= 0.8.7)
thor (~> 1.0)
rake (13.0.3)
request_store (1.5.0)
rack (>= 1.4)
thor (1.1.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
zeitwerk (2.4.2)

PLATFORMS
ruby

DEPENDENCIES
json_rails_logger!
rake

BUNDLED WITH
2.1.4
10 changes: 10 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

require 'rake/testtask'

Rake::TestTask.new(:test) do |t|
t.libs = ''
t.test_files = FileList['test/*_test.rb']
end

task default: :test
22 changes: 22 additions & 0 deletions json_rails_logger.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

require './lib/json_rails_logger/version'

Gem::Specification.new do |s|
s.name = 'json_rails_logger'
s.version = JsonRailsLogger::VERSION
s.date = '2021-01-06'
s.summary = 'JSON Rails Logger'
s.description = 'A custom rails logger that outputs JSON instead of raw text'
s.authors = ['Bogdan-Adrian Marc']
s.email = '[email protected]'
s.files = ['./lib/json_rails_logger.rb']
s.homepage = 'https://github.com/epimorphics/json-rails-logger'
s.license = 'MIT'

s.add_runtime_dependency 'json'
s.add_runtime_dependency 'lograge'
s.add_runtime_dependency 'railties'

s.add_development_dependency 'rake'
end
28 changes: 28 additions & 0 deletions lib/json_rails_logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

require 'logger'
require 'json'
require 'rails'
require 'lograge'

require_relative 'json_rails_logger/railtie' if defined?(Rails)

require_relative 'json_rails_logger/json_formatter.rb'
require_relative 'json_rails_logger/error.rb'
require_relative 'json_rails_logger/logger.rb'
require_relative 'json_rails_logger/version.rb'

# A custom rails logger that outputs json instead of raw text
module JsonRailsLogger
def self.setup(app)
return if enabled?(app)

raise JsonRailsLogger::LoggerSetupError,
'Please configure rails logger to use JsonRailsLogger'
end

def self.enabled?(app)
!app.config.logger.nil? &&
app.config.logger.class == JsonRailsLogger::Logger
end
end
6 changes: 6 additions & 0 deletions lib/json_rails_logger/error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

module JsonRailsLogger
class LoggerSetupError < StandardError
end
end
89 changes: 89 additions & 0 deletions lib/json_rails_logger/json_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# frozen_string_literal: true

module JsonRailsLogger
# This class is the json formatter for our logger
class JsonFormatter < ::Logger::Formatter
def call(severity, timestamp, _progname, raw_msg)
sev = process_severity(severity)
timestp = process_timestamp(timestamp)
msg = process_message(raw_msg)

payload = {
level: sev,
timestamp: timestp,
rails_environment: ::Rails.env
}

payload.merge!(msg)

"#{payload.to_json}\n"
end

private

def process_severity(severity)
{ 'FATAL' => 'ERROR' }[severity] || severity
end

def process_timestamp(timestamp)
format_datetime(timestamp)
end

def process_message(raw_msg)
msg = normalize_message(raw_msg)

return msg unless msg.is_a?(String)

return status_message(msg) if status_message?(msg)
return get_message(msg) if get_message?(msg)
return user_agent_message(msg) if user_agent_message?(msg)

{ message: msg.strip }
end

def normalize_message(raw_msg)
return raw_msg unless raw_msg.is_a?(String)

JSON.parse(raw_msg)
rescue JSON::ParserError
raw_msg
end

def status_message?(msg)
msg.is_a?(String) &&
msg.match(/Status [0-9]+/)
end

def status_message(msg)
status = msg.split(' ')[1]

{ status: status }
end

def get_message?(msg)
msg.is_a?(String) &&
msg.match(/GET http\S+/)
end

def get_message(msg)
splitted_msg = msg.split(' ')
method = splitted_msg[0]
path = splitted_msg[1]

{ method: method, path: path }
end

def user_agent_message?(msg)
msg.is_a?(String) &&
msg.match(/User-Agent: .+Accept: .+/m)
end

def user_agent_message(msg)
splitted_msg = msg.split("\n")
user_agent = splitted_msg[0]&.split('"')&.at(1)
accept = splitted_msg[1]&.split('"')&.at(1)

{ user_agent: user_agent, accept: accept }
end
end
end
17 changes: 17 additions & 0 deletions lib/json_rails_logger/logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module JsonRailsLogger
# The custom logger class that sets up our formatter
class Logger < ::Logger
# List of all the arguments with their default values:
# logdev, shift_age = 0, shift_size = 1_048_576, level: DEBUG,
# progname: nil, formatter: nil, datetime_format: nil,
# binmode: false, shift_period_suffix: '%Y%m%d'
def initialize(*args)
@formatter = JsonRailsLogger::JsonFormatter.new
@formatter.datetime_format = '%Y-%m-%d %H:%M:%S'

super(*args, formatter: @formatter)
end
end
end
19 changes: 19 additions & 0 deletions lib/json_rails_logger/railtie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module JsonRailsLogger
# This class is used to configure and setup lograge, as well as our gem
class Railtie < Rails::Railtie
config.lograge.formatter = Lograge::Formatters::Json.new
config.lograge.custom_options = lambda do |event|
{
exception: event.payload[:exception],
exception_object: event.payload[:exception_object]
}
end

config.after_initialize do |app|
JsonRailsLogger.setup(app) if JsonRailsLogger.enabled?(app)
Lograge.setup(app) if JsonRailsLogger.enabled?(app)
end
end
end
8 changes: 8 additions & 0 deletions lib/json_rails_logger/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

module JsonRailsLogger
MAJOR = 0
MINOR = 1
FIX = 0
VERSION = "#{MAJOR}.#{MINOR}.#{FIX}"
end
82 changes: 82 additions & 0 deletions test/formatter_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# frozen_string_literal: true

require 'minitest/autorun'

require './lib/json_rails_logger.rb'

describe 'JsonRailsLogger::JsonFormatter' do # rubocop:disable Metrics/BlockLength
let(:fixture) do
formatter = JsonRailsLogger::JsonFormatter.new
formatter.datetime_format = '%Y-%m-%d %H:%M:%S'
formatter
end

let(:timestamp) { DateTime.parse('2020-12-15T20:15:21') }
let(:progname) { 'progname' }

it 'should replace FATAL with ERROR for severity' do
message = '[Webpacker] Compilation error!'

log_output = fixture.call('FATAL', timestamp, progname, message)
_(log_output).must_be_kind_of(String)

json_output = JSON.parse(log_output)
_(json_output['level']).must_equal('ERROR')
end

it 'should parse status messages to json' do
message = 'Status 200'

log_output = fixture.call('INFO', timestamp, progname, message)
_(log_output).must_be_kind_of(String)

json_output = JSON.parse(log_output)
_(json_output['status']).must_equal('200')
end

it 'should parse method and path for requests' do
message = 'GET http://fsa-rp-test.epimorphics.net/'

log_output = fixture.call('INFO', timestamp, progname, message)
_(log_output).must_be_kind_of(String)

json_output = JSON.parse(log_output)
_(json_output['method']).must_equal('GET')
_(json_output['path']).must_equal('http://fsa-rp-test.epimorphics.net/')
end

it 'should parse user agent messages' do
message = "User-Agent: \"Faraday v1.3.0\"\nAccept: \"application/json\""

log_output = fixture.call('INFO', timestamp, progname, message)
_(log_output).must_be_kind_of(String)

json_output = JSON.parse(log_output)
_(json_output['user_agent']).must_equal('Faraday v1.3.0')
_(json_output['accept']).must_equal('application/json')
end

it 'should correctly format the timestamp' do
message = "[Webpacker] Everything's up-to-date. Nothing to do"

log_output = fixture.call('INFO', timestamp, progname, message)
_(log_output).must_be_kind_of(String)

json_output = JSON.parse(log_output)
_(json_output['timestamp']).must_equal('2020-12-15 20:15:21')
end

it 'should move json to top level if message is json object' do
json_fixture = {
method: 'GET',
path: 'http://fsa-rp-test.epimorphics.net/'
}

log_output = fixture.call('INFO', timestamp, progname, json_fixture)
_(log_output).must_be_kind_of(String)

json_output = JSON.parse(log_output)
_(json_output['method']).must_equal('GET')
_(json_output['path']).must_equal('http://fsa-rp-test.epimorphics.net/')
end
end

0 comments on commit ac6fa08

Please sign in to comment.