Skip to content

Commit

Permalink
Merge pull request #15 from epimorphics/dev
Browse files Browse the repository at this point in the history
Release 0.2.0
  • Loading branch information
bogdanadrianmarc authored Feb 1, 2021
2 parents ac6fa08 + b9e9de8 commit 2f88a58
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 11 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Changelog for the JSON Rails Logger gem

## 0.2.0 - 2021-02-01 (Bogdan)

- Platform related fields are now grouped together inside the 'rails' field
- Request ID is now present in every log message
- MIT license file added to the repo
- Added readme file with usage instructions

## 0.1.0 - 2021-01-26 (Bogdan)

- This is an initial release, contains a simple JSON Rails Logger
with some customisation applied to it
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
json_rails_logger (0.1.0)
json_rails_logger (0.2.0)
json
lograge
railties
Expand Down
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Epimorphics Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,25 @@
# json-rails-logger
A custom rails logger that outputs json instead of raw text

A custom rails logger that outputs JSON instead of raw text. As an extra the
logger also saves the `request_id` for any log message, in the returned JSON
object. The formatter has a couple of changes that are down to preference, but
these don't affect the functionality in any way. This gem can be used with any
Rails app using the installation steps below.

## Installation

In your Rails app, add this to your gemfile:\
`gem 'json_rails_logger', git: '[email protected]:epimorphics/json-rails-logger.git'`

And this to your environment config:\
`config.logger = JsonRailsLogger::Logger.new(STDOUT)`

## Running the tests

Tests are located in the `./test/` folder.

After cloning the repo, first execute:\
`bundle install`

To run the tests, use:\
`rake test`
5 changes: 5 additions & 0 deletions lib/json_rails_logger/constants.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

module JsonRailsLogger
REQUEST_ID = :request_id
end
40 changes: 34 additions & 6 deletions lib/json_rails_logger/json_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
module JsonRailsLogger
# This class is the json formatter for our logger
class JsonFormatter < ::Logger::Formatter
COMMON_KEYS = %w[
method path status duration
].freeze

def call(severity, timestamp, _progname, raw_msg)
sev = process_severity(severity)
timestp = process_timestamp(timestamp)
msg = process_message(raw_msg)
new_msg = format_message(msg)

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

payload.merge!(msg)
payload.merge!(x_request_id.to_h)
payload.merge!(new_msg.to_h)

"#{payload.to_json}\n"
end
Expand All @@ -29,6 +32,11 @@ def process_timestamp(timestamp)
format_datetime(timestamp)
end

def x_request_id
x_request_id = Thread.current[JsonRailsLogger::REQUEST_ID]
{ 'x-request-id': x_request_id } if x_request_id
end

def process_message(raw_msg)
msg = normalize_message(raw_msg)

Expand All @@ -41,6 +49,26 @@ def process_message(raw_msg)
{ message: msg.strip }
end

def format_message(msg)
new_msg = { rails: { environment: ::Rails.env } }

return msg.merge(new_msg) if string_message_field?(msg)

split_msg = msg.partition { |k, _v| COMMON_KEYS.include?(k.to_s) }
.map(&:to_h)

new_msg.merge!(split_msg[0])
new_msg[:rails].merge!(split_msg[1])

new_msg
end

def string_message_field?(msg)
msg.is_a?(Hash) &&
msg.length == 1 &&
msg.fetch(:message, nil).is_a?(String)
end

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

Expand Down
7 changes: 7 additions & 0 deletions lib/json_rails_logger/railtie.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require_relative 'request_id_middleware'

module JsonRailsLogger
# This class is used to configure and setup lograge, as well as our gem
class Railtie < Rails::Railtie
Expand All @@ -15,5 +17,10 @@ class Railtie < Rails::Railtie
JsonRailsLogger.setup(app) if JsonRailsLogger.enabled?(app)
Lograge.setup(app) if JsonRailsLogger.enabled?(app)
end

initializer 'railtie.configure_rails_initialization' do |app|
app.middleware.insert_after(ActionDispatch::RequestId,
JsonRailsLogger::RequestIdMiddleware)
end
end
end
21 changes: 21 additions & 0 deletions lib/json_rails_logger/request_id_middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require_relative 'constants'

module JsonRailsLogger
# Middleware that saves the request_id into a constant
# and clears it after usage in the formatter
class RequestIdMiddleware
def initialize(app)
@app = app
end

def call(env)
request_id = env['action_dispatch.request_id']
Thread.current[JsonRailsLogger::REQUEST_ID] = request_id
@app.call(env)
ensure
Thread.current[JsonRailsLogger::REQUEST_ID] = nil
end
end
end
2 changes: 1 addition & 1 deletion lib/json_rails_logger/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module JsonRailsLogger
MAJOR = 0
MINOR = 1
MINOR = 2
FIX = 0
VERSION = "#{MAJOR}.#{MINOR}.#{FIX}"
end
4 changes: 2 additions & 2 deletions test/formatter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
_(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')
_(json_output['rails']['user_agent']).must_equal('Faraday v1.3.0')
_(json_output['rails']['accept']).must_equal('application/json')
end

it 'should correctly format the timestamp' do
Expand Down

0 comments on commit 2f88a58

Please sign in to comment.