Skip to content
This repository has been archived by the owner on Dec 8, 2020. It is now read-only.

Commit

Permalink
Automatically capture user context
Browse files Browse the repository at this point in the history
  • Loading branch information
binarylogic committed Feb 23, 2017
1 parent 9076b47 commit cd6e66d
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 21 deletions.
2 changes: 1 addition & 1 deletion lib/timber/contexts/custom.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def initialize(attributes)
end

def as_json(_options = {})
{Timber::Object.try(type, :to_sym) => data}
{Timber::Util::Object.try(type, :to_sym) => data}
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/timber/contexts/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def initialize(attributes)
end

def as_json(_options = {})
{id: Timber::Object.try(id, :to_s), name: name}
{id: Timber::Util::Object.try(id, :to_s), name: name}
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/timber/contexts/system.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def initialize(attributes)
end

def as_json(_options = {})
{pid: Timber::Object.try(pid, :to_s)}
{pid: Timber::Util::Object.try(pid, :to_s)}
end
end
end
Expand Down
5 changes: 3 additions & 2 deletions lib/timber/contexts/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ module Contexts
class User < Context
@keyspace = :user

attr_reader :id, :name
attr_reader :id, :name, :email

def initialize(attributes)
@id = attributes[:id]
@name = attributes[:name]
@email = attributes[:email]
end

def as_json(_options = {})
{id: Timber::Object.try(id, :to_s), name: name}
{id: Timber::Util::Object.try(id, :to_s), name: name, email: email}
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/timber/events/custom.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def initialize(attributes)
end

def to_hash
{Timber::Object.try(type, :to_sym) => data}
{Timber::Util::Object.try(type, :to_sym) => data}
end
alias to_h to_hash

Expand Down
2 changes: 2 additions & 0 deletions lib/timber/probes.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "timber/probes/action_controller_log_subscriber"
require "timber/probes/action_controller_user_context"
require "timber/probes/action_dispatch_debug_exceptions"
require "timber/probes/action_view_log_subscriber"
require "timber/probes/active_record_log_subscriber"
Expand All @@ -11,6 +12,7 @@ module Timber
module Probes
def self.insert!
ActionControllerLogSubscriber.insert!
ActionControllerUserContext.insert!
ActionDispatchDebugExceptions.insert!
ActionViewLogSubscriber.insert!
ActiveRecordLogSubscriber.insert!
Expand Down
46 changes: 46 additions & 0 deletions lib/timber/probes/action_controller_user_context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Timber
module Probes
# Responsible for automatically tracking controller call and http response events
# for applications that use `ActionController`.
class ActionControllerUserContext < Probe
module AroundFilter
def self.included(klass)
klass.class_eval do
around_filter :_timber_capture_user_context

private
def _timber_capture_user_context
if respond_to?(:current_user, true)
id = Timber::Util::Object.try(current_user, :id)
name = Timber::Util::Object.try(current_user, :name)
if !name
first_name = Timber::Util::Object.try(current_user, :first_name)
last_name = Timber::Util::Object.try(current_user, :last_name)
if first_name || last_name
name = [first_name, last_name].compact.join(" ")
end
end
email = Timber::Util::Object.try(current_user, :email)
user_context = Timber::Contexts::User.new(:id => id, :name => name, :email => email)
Timber::CurrentContext.with(user_context) do
yield
end
end
end
end
end
end

def initialize
require "action_controller"
rescue LoadError => e
raise RequirementNotMetError.new(e.message)
end

def insert!
return true if ActionController::Base.include?(AroundFilter)
ActionController::Base.include(AroundFilter)
end
end
end
end
16 changes: 9 additions & 7 deletions lib/timber/probes/active_record_log_subscriber/log_subscriber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ class LogSubscriber < ::ActiveRecord::LogSubscriber #:nodoc:
def sql(event)
super(event)

payload = event.payload
event = Events::SQLQuery.new(
sql: payload[:sql],
time_ms: event.duration,
message: @message
)
if @message
payload = event.payload
event = Events::SQLQuery.new(
sql: payload[:sql],
time_ms: event.duration,
message: @message
)

logger.debug event
logger.debug event
end
end

private
Expand Down
16 changes: 9 additions & 7 deletions lib/timber/util/object.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
module Timber
# @private
module Object
module Util
# @private
def self.try(object, method)
if object == nil
nil
else
object.send(method) rescue object
module Object
# @private
def self.try(object, method)
if object == nil
nil
else
object.send(method) rescue object
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/support/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Defualt the rails logger to nothing, each test shoould be
# responsible for setting up the logger
logger = ::Logger.new(nil)
logger = ::Logger.new(STDOUT)
Rails.logger = logger

class RailsApp < Rails::Application
Expand Down
55 changes: 55 additions & 0 deletions spec/timber/probes/action_controller_user_context_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require "spec_helper"

describe Timber::Probes::ActionControllerUserContext do
describe described_class::AroundFilter do
let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
let(:io) { StringIO.new }
let(:logger) do
logger = Timber::Logger.new(io)
logger.level = ::Logger::WARN
logger
end

around(:each) do |example|
class UserContextController < ActionController::Base
layout nil

def index
logger.error "test"
render json: {}
end

def method_for_action(action_name)
action_name
end

private
def current_user
@current_user ||= begin
user_struct = Struct.new(:id, :name, :email)
user_struct.new(1, "Ben Johnson", "hi@timber.io")
end
end
end

::RailsApp.routes.draw do
get 'user_context' => 'user_context#index'
end

old_logger = ::ActionController::Base.logger
::ActionController::Base.logger = logger

Timecop.freeze(time) { example.run }

Object.send(:remove_const, :UserContextController)
::ActionController::Base.logger = old_logger
end

describe "#index" do
it "should capture the user context" do
dispatch_rails_request("/user_context")
expect(io.string).to include("\"user\":{\"id\":\"1\",\"name\":\"Ben Johnson\",\"email\":\"hi@timber.io\"}")
end
end
end
end

0 comments on commit cd6e66d

Please sign in to comment.