Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attach context #112

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Guardfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ group :sinatra do
all_on_start: true,
cmd: "RACK_ENV=#{env} bundle exec appraisal sinatra14 rspec" do
watch(%r{^spec/integration/sinatra_spec.rb$})
watch('lib/loga/context_manager.rb')
watch('spec/loga/context_manager_spec.rb')
end
end
end
Expand All @@ -34,6 +36,8 @@ group :rails do
end

watch(%r{^spec/integration/rails/.+_spec\.rb$})
watch('lib/loga/context_manager.rb')
watch('spec/loga/context_manager_spec.rb')
end
end
end
Expand All @@ -50,12 +54,16 @@ group :sidekiq do
'spec/loga/sidekiq_spec.rb',
]
end
watch('lib/loga/context_manager.rb')
watch('spec/loga/context_manager_spec.rb')
end
end

group :unit do
guard :rspec, cmd: 'bundle exec appraisal unit rspec' do
watch(%r{^spec/unit/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
watch('lib/loga/context_manager.rb')
watch('spec/loga/context_manager_spec.rb')
end
end
53 changes: 37 additions & 16 deletions lib/loga.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,53 @@
require 'loga/rack/request_id'
require 'loga/railtie' if defined?(Rails)
require 'loga/sidekiq'
require 'loga/context_manager'

module Loga
ConfigurationError = Class.new(StandardError)

def self.configuration
unless @configuration
raise ConfigurationError,
'Loga has not been configured. Configure with Loga.configure(options)'
class << self
def configuration
unless @configuration
raise ConfigurationError,
'Loga has not been configured. Configure with Loga.configure(options)'
end

@configuration
end

@configuration
end
def configure(options, framework_options = {})
raise ConfigurationError, 'Loga has already been configured' if @configuration

def self.configure(options, framework_options = {})
raise ConfigurationError, 'Loga has already been configured' if @configuration
@configuration ||= Configuration.new(options, framework_options)

@configuration ||= Configuration.new(options, framework_options)
Loga::Sidekiq.configure_logging
end

Loga::Sidekiq.configure_logging
end
def logger
configuration.logger
end

def self.logger
configuration.logger
end
def reset
@configuration = nil
end

def self.reset
@configuration = nil
def attach_context(payload)
current_context.attach_context(payload)
end

def clear_context
current_context.clear
end

def retrieve_context
current_context.retrieve_context
end

private

def current_context
ContextManager.current
end
end
end
72 changes: 30 additions & 42 deletions lib/loga/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,42 @@ class Configuration
Sinatra::NotFound
].freeze

attr_accessor :device, :filter_exceptions, :filter_parameters,
:host, :level, :service_version, :sync, :tags, :hide_pii
attr_reader :logger, :format, :service_name
attr_reader :device, :filter_exceptions, :filter_parameters, :format, :hide_pii,
:host, :level, :service_name, :service_version, :sync, :tags

# rubocop:disable Metrics/MethodLength
def initialize(user_options = {}, framework_options = {})
options = default_options.merge(framework_options)
.merge(environment_options)
.merge(user_options)

self.device = options[:device]
self.filter_exceptions = options[:filter_exceptions]
self.filter_parameters = options[:filter_parameters]
self.format = options[:format]
self.host = options[:host]
self.level = options[:level]
self.service_name = options[:service_name]
self.service_version = options[:service_version] || ServiceVersionStrategies.call
self.sync = options[:sync]
self.tags = options[:tags]
self.hide_pii = options[:hide_pii]
@device = options[:device]
@filter_exceptions = options[:filter_exceptions]
@filter_parameters = options[:filter_parameters]
@format = options[:format].to_s.to_sym
@hide_pii = options[:hide_pii]
@host = options[:host]
@level = options[:level]
@service_name = options[:service_name].to_s.strip
@service_version = options[:service_version] || ServiceVersionStrategies.call
@sync = options[:sync]
@tags = options[:tags]

validate

@logger = initialize_logger
end
# rubocop:enable Metrics/MethodLength

def format=(name)
@format = name.to_s.to_sym
def structured?
format == :gelf
end

def service_name=(name)
@service_name = name.to_s.strip
end
def logger
@logger ||= begin
device.sync = sync
new_logger = Logger.new(device)
new_logger.formatter = assign_formatter
new_logger.level = constantized_log_level

def structured?
format == :gelf
TaggedLogging.new(new_logger)
end
end

private
Expand Down Expand Up @@ -79,14 +77,6 @@ def environment_options
{ format: ENV['LOGA_FORMAT'].presence }.reject { |_, v| v.nil? }
end

def initialize_logger
device.sync = sync
logger = Logger.new(device)
logger.formatter = assign_formatter
logger.level = constantized_log_level
TaggedLogging.new(logger)
end

def constantized_log_level
Logger.const_get(level.to_s.upcase)
end
Expand All @@ -98,15 +88,13 @@ def hostname
end

def assign_formatter
if format == :gelf
Formatters::GELFFormatter.new(
service_name: service_name,
service_version: service_version,
host: host,
)
else
Formatters::SimpleFormatter.new
end
return Formatters::SimpleFormatter.new if format != :gelf

Formatters::GELFFormatter.new(
service_name: service_name,
service_version: service_version,
host: host,
)
end
end
end
31 changes: 31 additions & 0 deletions lib/loga/context_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'thread'

module Loga
class ContextManager
def self.current
Thread.current[:__loga_context_manager] ||= new
end

def initialize
@semaphore = Mutex.new

clear
end

def clear
@semaphore.synchronize do
@context = {}
end
end

def attach_context(payload)
@semaphore.synchronize do
@context.update(payload)
end
end

def retrieve_context
@semaphore.synchronize { @context }
end
end
end
11 changes: 7 additions & 4 deletions lib/loga/formatters/gelf_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@ def build_event(time, message)

event.timestamp ||= time
event.data ||= {}
event.data.tap do |hash|
hash.merge! compute_exception(event.exception)
hash.merge! compute_type(event.type)

event.data.tap do |data_hash|
data_hash.merge!(compute_exception(event.exception))
data_hash.merge!(compute_type(event.type))
# Overwrite hash with Loga's additional fields
hash.merge! loga_additional_fields
data_hash.merge!(loga_additional_fields)
data_hash.merge!(Loga.retrieve_context)
end

event
end

Expand Down
3 changes: 2 additions & 1 deletion lib/loga/rack/logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def set_data
data['request_ip'] = request.ip
data['user_agent'] = request.user_agent
data['controller'] = request.controller_action_name if request.controller_action_name
data['duration'] = duration_in_ms(started_at, Time.now)
data['duration'] = duration_in_ms(started_at)
end
# rubocop:enable Metrics/LineLength

Expand All @@ -55,6 +55,7 @@ def send_message
timestamp: started_at,
type: 'request',
)

logger.public_send(compute_level, event)
end

Expand Down
29 changes: 29 additions & 0 deletions spec/fixtures/rails32.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ def update
@id = params[:id]
render '/user'
end

def attach_context
Loga.attach_context(animal: 'monkey')

render json: { hello: :world }
end
end

class FakeMailer < ActionMailer::Base
Expand All @@ -68,13 +74,36 @@ def basic_mail
end
end

class FakeMailerWithContext < ActionMailer::Base
default from: 'notifications@example.com'

def self.send_email
Loga.attach_context(tshirt_size: 'XL')
Loga.attach_context(height: 155)
Loga.attach_context(weight: 122)

basic_mail.deliver
end

def basic_mail
mail(
to: 'user@example.com',
subject: 'Welcome to My Awesome Site',
body: 'Banana muffin',
content_type: 'text/html',
)
end
end

Dummy.routes.append do
get 'ok' => 'application#ok'
get 'error' => 'application#error'
get 'show' => 'application#show'
post 'users' => 'application#create'
get 'new' => 'application#new'
put 'users/:id' => 'application#update'

get 'attach_context' => 'application#attach_context'
end

Dummy.initialize!
29 changes: 29 additions & 0 deletions spec/fixtures/rails40.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ def update
@id = params[:id]
render '/user'
end

def attach_context
Loga.attach_context(animal: 'monkey')

render json: { hello: :world }
end
end

class FakeMailer < ActionMailer::Base
Expand All @@ -68,13 +74,36 @@ def basic_mail
end
end

class FakeMailerWithContext < ActionMailer::Base
default from: 'notifications@example.com'

def self.send_email
Loga.attach_context(tshirt_size: 'XL')
Loga.attach_context(height: 155)
Loga.attach_context(weight: 122)

basic_mail.deliver
end

def basic_mail
mail(
to: 'user@example.com',
subject: 'Welcome to My Awesome Site',
body: 'Banana muffin',
content_type: 'text/html',
)
end
end

Dummy.routes.append do
get 'ok' => 'application#ok'
get 'error' => 'application#error'
get 'show' => 'application#show'
post 'users' => 'application#create'
get 'new' => 'application#new'
put 'users/:id' => 'application#update'

get 'attach_context' => 'application#attach_context'
end

Dummy.initialize!
Loading