-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from epimorphics/dev
Merge changes to main for a first release
- Loading branch information
Showing
11 changed files
with
368 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |