Skip to content

consistent structured logging for amaysim ruby applications

License

Notifications You must be signed in to change notification settings

amaysim-au/amaysim-logger-ruby

Repository files navigation

amaysim_logger gem

Purpose

To have a gem that can be used across different ruby based applications and achieve a consistent way of logging application events.

Usage

  1. Add to Gemfile:
gem 'amaysim_logger', git: '[email protected]:amaysim-au/amaysim-logger-ruby.git'
  1. Add a logger initialiser to config/initialiser
Rails.logger = AmaysimLogger

Note: Its a good idea to wrap this in an env var to allow developers to use traditional logs locally:

unless ENV['DISABLE_AMAYSIM_LOGGER'] == 'true'
  Rails.logger = AmaysimLogger
end
  1. Run bundle install
  2. In the application log as per usual with Rails.logger

Rails.logger.info('foo') # .info, .debug, .warn, .error, .unknown
# {
#   "log_timestamp": "2017-02-01 12:43:08 +1100 AEDT",
#   "msg": "foo",
#   "log_level": "info"
# }

Rails.logger.info(msg: 'foo')
# {
#   "log_timestamp": "2017-02-01 12:43:42 +1100 AEDT",
#   "msg": "foo",
#   "log_level": "info"
# }

Rails.logger.error(msg: 'foo', exception: e)
# {
#   "log_timestamp": "2017-02-01 12:43:42 +1100 AEDT",
#   "msg": "foo",
#   "log_level": "info"
#   "exception_message": "some exception message",
#   "exception_class": "SomeException",
#   "exception_backtrace": "<backtrace line separated>"
# }

Rails.logger.debug(msg: 'bar', other: :attribute)
# {
#   "log_timestamp": "2017-02-01 12:44:15 +1100 AEDT",
#   "msg": "bar",
#   "log_level": "info",
#   "other": "attribute"
# }

# if a block is given, the log will automatically include start and end time with duration.
Rails.logger.warn(msg: 'baz', other: :attribute) { sleep 1.45 }
# {
#   "duration": 1.45457,
#   "end_time": "2017-02-01 12:45:02 +1100 AEDT",
#   "log_timestamp": "2017-02-01 12:45:00 +1100 AEDT",
#   "msg": "baz",
#   "log_level": "info",
#   "other": "attribute",
#   "start_time": "2017-02-01 12:45:00 +1100 AEDT"
# }

Keyword Filtering

Keyword filtering is straightforward.

AmaysimLogger.filtered_keywords = %w( password email first_name )

AmaysimLogger.info(password: 'foo', key: 'value')
# {"password":"[MASKED]","key":"value"}

AmaysimLogger.debug(soap_response)
# ..... <email>[MASKED]</email> ......

Rails

It should Just Work:tm:.

You automatically get logs for each request on each action:

{
  "correlation_id": "f79fd4bb-c591-4335-a90a-74994bfe5d9f",
  "duration": 0.576696,
  "end_time": "2017-02-01 12:38:00 +1100 AEDT",
  "endpoint": "http://amaysim.com.au/",
  "ip": "::1",
  "log_timestamp": "2017-02-01 12:37:59 +1100 AEDT",
  "msg": "Web request",
  "log_level": "info",
  "request_id": "04780cc0-fead-448d-907a-381089eb221b",
  "start_time": "2017-02-01 12:37:59 +1100 AEDT",
  "user_agent": "Mozilla/5.0"
}

There is also a method available for all controllers add_to_log_context. It can be used when you need to some common attributes that are shared across multiple log entries during the request.

class ApplicationController < ActionController::Base
  def index
    add_to_log_context(foo: :bar)
    logger.info('foo')
    logger.info(baz: :bar)
    log_request do
      sleep 1.5
    end
  end
end

Then you will have log entries like this:

{
  "correlation_id": "568652a8-2d54-4540-940d-ca48836ec70f",
  "endpoint": "http://amaysim.com.au/",
  "foo": "bar",
  "ip": "::1",
  "log_timestamp": "2017-02-01 12:47:08 +1100 AEDT",
  "msg": "foo",,
  "log_level": "info",
  "request_id": "6a2a792b-4a96-4179-adf2-106c1028df7c",
  "user_agent": "Mozilla/5.0"
}
{
  "baz": "bar",
  "correlation_id": "568652a8-2d54-4540-940d-ca48836ec70f",
  "endpoint": "http://amaysim.com.au/",
  "foo": "bar",
  "ip": "::1",
  "log_timestamp": "2017-02-01 12:47:08 +1100 AEDT",
  "msg": null,,
  "log_level": "info",
  "request_id": "6a2a792b-4a96-4179-adf2-106c1028df7c",
  "user_agent": "Mozilla/5.0"
}
{
  "correlation_id": "568652a8-2d54-4540-940d-ca48836ec70f",
  "duration": 1.503092,
  "end_time": "2017-02-01 12:47:10 +1100 AEDT",
  "endpoint": "http://amaysim.com.au/",
  "foo": "bar",
  "ip": "::1",
  "log_timestamp": "2017-02-01 12:47:08 +1100 AEDT",
  "msg": "Web request",
  "log_level": "info",
  "request_id": "6a2a792b-4a96-4179-adf2-106c1028df7c",
  "start_time": "2017-02-01 12:47:08 +1100 AEDT",
  "user_agent": "Mozilla/5.0"
}
{
  "correlation_id": "568652a8-2d54-4540-940d-ca48836ec70f",
  "duration": 2.223704,
  "end_time": "2017-02-01 12:47:10 +1100 AEDT",
  "endpoint": "http://amaysim.com.au/",
  "ip": "::1",
  "log_timestamp": "2017-02-01 12:47:08 +1100 AEDT",
  "msg": "Web request",
  "log_level": "info",
  "request_id": "6a2a792b-4a96-4179-adf2-106c1028df7c",
  "start_time": "2017-02-01 12:47:08 +1100 AEDT",
  "user_agent": "Mozilla/5.0"
}

From the log we can see:

  • We automatically have request logs.
  • You can explicitly call controller request anytime during any controller action yourself.
  • The logs automatically include some request metadata like ip, user agent, endpoint etc.

Common log entries for all controllers

To add common log params across all controllers then Add something like this to an intialiser

  ActiveSupport.on_load(:action_controller) do
    AmaysimLogger.add_to_log_context(app: 'your app name', foo: 'foo')
  end

Configuration

By default the ActiveRecord, ActiveJob, ActionController and ActionView logs are disabled. These can be re-enabled in an initializer.

# /config/initializers/amaysim_logger.rb

Rails.application.configure do
  config.amaysim_logger.disable_action_view_logs = false
  config.amaysim_logger.disable_action_controller_logs = false
  config.amaysim_logger.disable_active_record_logs = false
  config.amaysim_logger.disable_active_job_logs = false
end

Correlation ID

Correlation ID is automatically generated if Correlation-ID header is not set. Otherwise the correlation id provided in the HTTP header will be used.

Please ensure the correlation id is passed to downstream systems so we can link a request through multiple microservices

Header key: "Correlation-ID"
Header value: eg. "568652a8-2d54-4540-940d-ca48836ec70f"

Running tests

  • locally:
bundle install
bundle exec rake
  • with docker:
docker-compose run test

About

consistent structured logging for amaysim ruby applications

Resources

License

Stars

Watchers

Forks

Packages

No packages published