timber 2.0.24 → 2.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/CHANGELOG +3 -0
- data/README.md +314 -59
- data/bin/timber +11 -2
- data/lib/timber.rb +2 -7
- data/lib/timber/cli.rb +16 -28
- data/lib/timber/cli/api.rb +80 -14
- data/lib/timber/cli/api/application.rb +30 -0
- data/lib/timber/cli/config_file.rb +66 -0
- data/lib/timber/cli/file_helper.rb +43 -0
- data/lib/timber/cli/installer.rb +58 -0
- data/lib/timber/cli/installers.rb +37 -0
- data/lib/timber/cli/installers/other.rb +47 -0
- data/lib/timber/cli/installers/rails.rb +255 -0
- data/lib/timber/cli/installers/root.rb +189 -0
- data/lib/timber/cli/io.rb +97 -0
- data/lib/timber/cli/io/ansi.rb +22 -0
- data/lib/timber/cli/io/messages.rb +213 -0
- data/lib/timber/cli/os_helper.rb +53 -0
- data/lib/timber/config.rb +97 -43
- data/lib/timber/config/integrations.rb +63 -0
- data/lib/timber/config/integrations/rack.rb +74 -0
- data/lib/timber/context.rb +13 -10
- data/lib/timber/contexts.rb +1 -0
- data/lib/timber/contexts/custom.rb +16 -3
- data/lib/timber/contexts/http.rb +10 -3
- data/lib/timber/contexts/organization.rb +4 -0
- data/lib/timber/contexts/release.rb +46 -0
- data/lib/timber/contexts/runtime.rb +7 -1
- data/lib/timber/contexts/session.rb +8 -1
- data/lib/timber/contexts/system.rb +5 -1
- data/lib/timber/contexts/user.rb +9 -2
- data/lib/timber/current_context.rb +43 -11
- data/lib/timber/events/controller_call.rb +4 -0
- data/lib/timber/events/custom.rb +13 -5
- data/lib/timber/events/exception.rb +4 -0
- data/lib/timber/events/http_client_request.rb +4 -0
- data/lib/timber/events/http_client_response.rb +4 -0
- data/lib/timber/events/http_server_request.rb +5 -0
- data/lib/timber/events/http_server_response.rb +15 -3
- data/lib/timber/events/sql_query.rb +3 -0
- data/lib/timber/events/template_render.rb +3 -0
- data/lib/timber/integration.rb +40 -0
- data/lib/timber/integrations.rb +21 -14
- data/lib/timber/integrations/action_controller.rb +18 -0
- data/lib/timber/integrations/action_controller/log_subscriber.rb +2 -0
- data/lib/timber/integrations/action_controller/log_subscriber/timber_log_subscriber.rb +6 -0
- data/lib/timber/integrations/action_dispatch.rb +23 -0
- data/lib/timber/integrations/action_dispatch/debug_exceptions.rb +2 -0
- data/lib/timber/integrations/action_view.rb +18 -0
- data/lib/timber/integrations/action_view/log_subscriber.rb +2 -0
- data/lib/timber/integrations/action_view/log_subscriber/timber_log_subscriber.rb +10 -0
- data/lib/timber/integrations/active_record.rb +18 -0
- data/lib/timber/integrations/active_record/log_subscriber.rb +2 -0
- data/lib/timber/integrations/active_record/log_subscriber/timber_log_subscriber.rb +8 -0
- data/lib/timber/integrations/rack.rb +12 -2
- data/lib/timber/integrations/rack/exception_event.rb +38 -5
- data/lib/timber/integrations/rack/http_context.rb +4 -6
- data/lib/timber/integrations/rack/http_events.rb +177 -27
- data/lib/timber/integrations/rack/middleware.rb +28 -0
- data/lib/timber/integrations/rack/session_context.rb +5 -6
- data/lib/timber/integrations/rack/user_context.rb +90 -43
- data/lib/timber/integrations/rails.rb +22 -0
- data/lib/timber/integrations/rails/rack_logger.rb +2 -0
- data/lib/timber/integrator.rb +18 -3
- data/lib/timber/log_devices/http.rb +107 -99
- data/lib/timber/log_devices/http/dropping_sized_queue.rb +26 -0
- data/lib/timber/log_devices/http/flushable_sized_queue.rb +42 -0
- data/lib/timber/log_entry.rb +14 -2
- data/lib/timber/logger.rb +51 -36
- data/lib/timber/overrides.rb +2 -0
- data/lib/timber/overrides/active_support_3_tagged_logging.rb +103 -0
- data/lib/timber/overrides/active_support_tagged_logging.rb +53 -90
- data/lib/timber/timer.rb +21 -0
- data/lib/timber/util/hash.rb +1 -1
- data/lib/timber/util/http_event.rb +16 -3
- data/lib/timber/version.rb +1 -1
- data/spec/support/timber.rb +2 -3
- data/spec/timber/cli/installers/rails_spec.rb +160 -0
- data/spec/timber/cli/installers/root_spec.rb +100 -0
- data/spec/timber/config_spec.rb +28 -0
- data/spec/timber/current_context_spec.rb +61 -12
- data/spec/timber/events/custom_spec.rb +13 -2
- data/spec/timber/events/exception_spec.rb +15 -0
- data/spec/timber/events/http_server_request_spec.rb +3 -3
- data/spec/timber/integrations/rack/http_events_spec.rb +101 -0
- data/spec/timber/log_devices/http_spec.rb +20 -4
- data/spec/timber/log_entry_spec.rb +2 -1
- data/spec/timber/logger_spec.rb +8 -8
- metadata +40 -9
- data/benchmarks/rails.rb +0 -122
- data/lib/timber/cli/application.rb +0 -28
- data/lib/timber/cli/install.rb +0 -196
- data/lib/timber/cli/io_helper.rb +0 -65
- data/lib/timber/cli/messages.rb +0 -180
- data/lib/timber/integrations/active_support/tagged_logging.rb +0 -71
@@ -4,6 +4,8 @@
|
|
4
4
|
require "active_record"
|
5
5
|
require "active_record/log_subscriber"
|
6
6
|
|
7
|
+
require "timber/integrator"
|
8
|
+
|
7
9
|
module Timber
|
8
10
|
module Integrations
|
9
11
|
module ActiveRecord
|
@@ -16,6 +18,8 @@ module Timber
|
|
16
18
|
# @private
|
17
19
|
class TimberLogSubscriber < ::ActiveRecord::LogSubscriber
|
18
20
|
def sql(event)
|
21
|
+
return true if silence?
|
22
|
+
|
19
23
|
r = super(event)
|
20
24
|
|
21
25
|
if @message
|
@@ -38,6 +42,10 @@ module Timber
|
|
38
42
|
def debug(message)
|
39
43
|
@message = message
|
40
44
|
end
|
45
|
+
|
46
|
+
def silence?
|
47
|
+
ActiveRecord.silence?
|
48
|
+
end
|
41
49
|
end
|
42
50
|
end
|
43
51
|
end
|
@@ -7,10 +7,20 @@ require "timber/integrations/rack/user_context"
|
|
7
7
|
module Timber
|
8
8
|
module Integrations
|
9
9
|
module Rack
|
10
|
-
#
|
10
|
+
# Enable / disable all Rack middlewares with a single setting.
|
11
|
+
def self.enabled=(value)
|
12
|
+
ExceptionEvent.enabled = value
|
13
|
+
HTTPContext.enabled = value
|
14
|
+
HTTPEvents.enabled = value
|
15
|
+
SessionContext.enabled = value
|
16
|
+
UserContext.enabled = value
|
17
|
+
end
|
18
|
+
|
19
|
+
# All enabled middlewares. The order is relevant. Middlewares that set
|
11
20
|
# context are added first so that context is included in subsequent log lines.
|
12
21
|
def self.middlewares
|
13
|
-
@middlewares ||= [HTTPContext, SessionContext, UserContext,
|
22
|
+
@middlewares ||= [HTTPContext, SessionContext, UserContext,
|
23
|
+
HTTPEvents, ExceptionEvent].select(&:enabled?)
|
14
24
|
end
|
15
25
|
end
|
16
26
|
end
|
@@ -1,10 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require "action_dispatch/middleware/exception_wrapper"
|
3
|
+
rescue Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
require "timber/integrations/rack/middleware"
|
7
|
+
|
1
8
|
module Timber
|
2
9
|
module Integrations
|
3
10
|
module Rack
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
11
|
+
# A Rack middleware that is reponsible for capturing exceptions events
|
12
|
+
# {Timber::Events::Exception}.
|
13
|
+
class ExceptionEvent < Middleware
|
14
|
+
# We determine this when the app loads to avoid the overhead on a per request basis.
|
15
|
+
EXCEPTION_WRAPPER_TAKES_CLEANER = if Gem.loaded_specs["rails"]
|
16
|
+
Gem.loaded_specs["rails"].version >= Gem::Version.new('5.0.0')
|
17
|
+
else
|
18
|
+
false
|
8
19
|
end
|
9
20
|
|
10
21
|
def call(env)
|
@@ -12,16 +23,38 @@ module Timber
|
|
12
23
|
status, headers, body = @app.call(env)
|
13
24
|
rescue Exception => exception
|
14
25
|
Config.instance.logger.fatal do
|
26
|
+
backtrace = extract_backtrace(env, exception)
|
27
|
+
|
15
28
|
Events::Exception.new(
|
16
29
|
name: exception.class.name,
|
17
30
|
exception_message: exception.message,
|
18
|
-
backtrace:
|
31
|
+
backtrace: backtrace
|
19
32
|
)
|
20
33
|
end
|
21
34
|
|
22
35
|
raise exception
|
23
36
|
end
|
24
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
# Rails provides a backtrace cleaner, so we use it here.
|
41
|
+
def extract_backtrace(env, exception)
|
42
|
+
if defined?(::ActionDispatch::ExceptionWrapper)
|
43
|
+
wrapper = if EXCEPTION_WRAPPER_TAKES_CLEANER
|
44
|
+
request = Util::Request.new(env)
|
45
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
46
|
+
::ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception)
|
47
|
+
else
|
48
|
+
::ActionDispatch::ExceptionWrapper.new(env, exception)
|
49
|
+
end
|
50
|
+
|
51
|
+
trace = wrapper.application_trace
|
52
|
+
trace = wrapper.framework_trace if trace.empty?
|
53
|
+
trace
|
54
|
+
else
|
55
|
+
exception.backtrace
|
56
|
+
end
|
57
|
+
end
|
25
58
|
end
|
26
59
|
end
|
27
60
|
end
|
@@ -1,12 +1,10 @@
|
|
1
|
+
require "timber/integrations/rack/middleware"
|
2
|
+
|
1
3
|
module Timber
|
2
4
|
module Integrations
|
3
5
|
module Rack
|
4
|
-
#
|
5
|
-
class HTTPContext
|
6
|
-
def initialize(app)
|
7
|
-
@app = app
|
8
|
-
end
|
9
|
-
|
6
|
+
# A Rack middleware that is reponsible for adding the HTTP context {Timber::Contexts::HTTP}.
|
7
|
+
class HTTPContext < Middleware
|
10
8
|
def call(env)
|
11
9
|
request = Util::Request.new(env)
|
12
10
|
context = Contexts::HTTP.new(
|
@@ -1,43 +1,193 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
require "timber/integrations/rack/middleware"
|
4
|
+
|
1
5
|
module Timber
|
2
6
|
module Integrations
|
3
7
|
module Rack
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
+
# A Rack middleware that is reponsible for capturing and logging HTTP server requests and
|
9
|
+
# response events. The {Events::HTTPServerRequest} and {Events::HTTPServerResponse} events
|
10
|
+
# respectively.
|
11
|
+
class HTTPEvents < Middleware
|
12
|
+
class << self
|
13
|
+
# Allows you to capture the HTTP request body, default is off (false).
|
14
|
+
#
|
15
|
+
# Capturing HTTP bodies can be extremely helpful when debugging issues,
|
16
|
+
# but please proceed with caution:
|
17
|
+
#
|
18
|
+
# 1. Capturing HTTP bodies can use quite a bit of data (this can be mitigated, see below)
|
19
|
+
# 2. The {Events::ControllerCall} event captures the parsed parmaters sent to
|
20
|
+
# the controller. This is a parsed representation of the body, which is usually more
|
21
|
+
# helpful and redundant to the body captured here.
|
22
|
+
#
|
23
|
+
# If you opt to capture bodies, you can also truncate the size to reduce the data
|
24
|
+
# captured. See {Events::HTTPServerRequest}.
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# Timber::Integrations::Rack::HTTPEvents.capture_request_body = true
|
28
|
+
def capture_request_body=(value)
|
29
|
+
@capture_request_body = value
|
30
|
+
end
|
31
|
+
|
32
|
+
# Accessor method for {#capture_request_body=}
|
33
|
+
def capture_request_body?
|
34
|
+
@capture_request_body == true
|
35
|
+
end
|
36
|
+
|
37
|
+
# Just like {#capture_request_body=} but for the {Events::HTTPServerResponse} event.
|
38
|
+
# Please see {#capture_request_body=} for more details. The documentation there also
|
39
|
+
# applies here.
|
40
|
+
def capture_response_body=(value)
|
41
|
+
@capture_response_body = value
|
42
|
+
end
|
43
|
+
|
44
|
+
# Accessor method for {#capture_response_body=}
|
45
|
+
def capture_response_body?
|
46
|
+
@capture_response_body == true
|
47
|
+
end
|
48
|
+
|
49
|
+
# Collapse both the HTTP request and response events into a single log line event.
|
50
|
+
# While we don't recommend this, it can help to reduce log volume if desired.
|
51
|
+
# The reason we don't recommend this, is because the logging service you use should
|
52
|
+
# not be so expensive that you need to strip out useful logs. It should also provide
|
53
|
+
# the tools necessary to properly search your logs and reduce noise. Such as viewing
|
54
|
+
# logs for a specific request.
|
55
|
+
#
|
56
|
+
# To provide an example. This setting turns this:
|
57
|
+
#
|
58
|
+
# Started GET "/" for 127.0.0.1 at 2012-03-10 14:28:14 +0100
|
59
|
+
# Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
|
60
|
+
#
|
61
|
+
# Into this:
|
62
|
+
#
|
63
|
+
# Get "/" sent 200 OK in 79ms
|
64
|
+
#
|
65
|
+
# The single event is still a {Timber::Events::HTTPServerResponse} event. Because
|
66
|
+
# we capture HTTP context, you still get the HTTP details, but you will not get
|
67
|
+
# all of the request details that the {Timber::Events::HTTPServerRequest} event would
|
68
|
+
# provide.
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# Timber::Integrations::Rack::HTTPEvents.collapse_into_single_event = true
|
72
|
+
def collapse_into_single_event=(value)
|
73
|
+
@collapse_into_single_event = value
|
74
|
+
end
|
75
|
+
|
76
|
+
# Accessor method for {#collapse_into_single_event=}.
|
77
|
+
def collapse_into_single_event?
|
78
|
+
@collapse_into_single_event == true
|
79
|
+
end
|
80
|
+
|
81
|
+
# This setting allows you to silence requests based on any conditions you desire.
|
82
|
+
# We require a block because it gives you complete control over how you want to
|
83
|
+
# silence requests. The first parameter being the traditional Rack env hash, the
|
84
|
+
# second being a [Rack Request](http://www.rubydoc.info/gems/rack/Rack/Request) object.
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
# Integrations::Rack::HTTPEvents.silence_request = lambda do |rack_env, rack_request|
|
88
|
+
# rack_request.path == "/_health"
|
89
|
+
# end
|
90
|
+
def silence_request=(proc)
|
91
|
+
if proc && !proc.is_a?(Proc)
|
92
|
+
raise ArgumentError.new("The value passed to #silence_request must be a Proc")
|
93
|
+
end
|
94
|
+
|
95
|
+
@silence_request = proc
|
96
|
+
end
|
97
|
+
|
98
|
+
# Accessor method for {#silence_request=}
|
99
|
+
def silence_request
|
100
|
+
@silence_request
|
101
|
+
end
|
8
102
|
end
|
9
103
|
|
10
104
|
def call(env)
|
11
|
-
start = Time.now
|
12
105
|
request = Util::Request.new(env)
|
13
106
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
107
|
+
if silenced?(env, request)
|
108
|
+
Config.instance.logger.silence do
|
109
|
+
@app.call(env)
|
110
|
+
end
|
111
|
+
|
112
|
+
elsif collapse_into_single_event?
|
113
|
+
start = Time.now
|
114
|
+
|
115
|
+
status, headers, body = @app.call(env)
|
116
|
+
|
117
|
+
Config.instance.logger.info do
|
118
|
+
http_context_key = Contexts::HTTP.keyspace
|
119
|
+
http_context = CurrentContext.fetch(http_context_key)
|
120
|
+
time_ms = (Time.now - start) * 1000.0
|
121
|
+
|
122
|
+
Events::HTTPServerResponse.new(
|
123
|
+
headers: headers,
|
124
|
+
http_context: http_context,
|
125
|
+
request_id: request.request_id,
|
126
|
+
status: status,
|
127
|
+
time_ms: time_ms
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
[status, headers, body]
|
132
|
+
|
133
|
+
else
|
134
|
+
start = Time.now
|
135
|
+
|
136
|
+
Config.instance.logger.info do
|
137
|
+
event_body = capture_request_body? ? request.body_content : nil
|
138
|
+
|
139
|
+
Events::HTTPServerRequest.new(
|
140
|
+
body: event_body,
|
141
|
+
headers: request.headers,
|
142
|
+
host: request.host,
|
143
|
+
method: request.request_method,
|
144
|
+
path: request.path,
|
145
|
+
port: request.port,
|
146
|
+
query_string: request.query_string,
|
147
|
+
request_id: request.request_id, # we insert this middleware after ActionDispatch::RequestId
|
148
|
+
scheme: request.scheme
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
status, headers, body = @app.call(env)
|
153
|
+
|
154
|
+
Config.instance.logger.info do
|
155
|
+
time_ms = (Time.now - start) * 1000.0
|
156
|
+
event_body = capture_response_body? ? body : nil
|
157
|
+
|
158
|
+
Events::HTTPServerResponse.new(
|
159
|
+
body: event_body,
|
160
|
+
headers: headers,
|
161
|
+
request_id: request.request_id,
|
162
|
+
status: status,
|
163
|
+
time_ms: time_ms
|
164
|
+
)
|
165
|
+
end
|
166
|
+
|
167
|
+
[status, headers, body]
|
25
168
|
end
|
169
|
+
end
|
26
170
|
|
27
|
-
|
171
|
+
private
|
172
|
+
def capture_request_body?
|
173
|
+
self.class.capture_request_body?
|
174
|
+
end
|
28
175
|
|
29
|
-
|
30
|
-
|
31
|
-
Events::HTTPServerResponse.new(
|
32
|
-
headers: headers,
|
33
|
-
request_id: request.request_id,
|
34
|
-
status: status,
|
35
|
-
time_ms: time_ms
|
36
|
-
)
|
176
|
+
def capture_response_body?
|
177
|
+
self.class.capture_response_body?
|
37
178
|
end
|
38
179
|
|
39
|
-
|
40
|
-
|
180
|
+
def collapse_into_single_event?
|
181
|
+
self.class.collapse_into_single_event?
|
182
|
+
end
|
183
|
+
|
184
|
+
def silenced?(env, request)
|
185
|
+
if !self.class.silence_request.nil?
|
186
|
+
self.class.silence_request.call(env, request)
|
187
|
+
else
|
188
|
+
false
|
189
|
+
end
|
190
|
+
end
|
41
191
|
end
|
42
192
|
end
|
43
193
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Timber
|
2
|
+
module Integrations
|
3
|
+
module Rack
|
4
|
+
# Base class that all Timber Rack middlewares extend. See the class level methods for
|
5
|
+
# configuration options.
|
6
|
+
class Middleware
|
7
|
+
class << self
|
8
|
+
# Easily enable / disable specific middlewares.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Timber::Integrations::Rack::UserContext.enabled = false
|
12
|
+
def enabled=(value)
|
13
|
+
@enabled = value
|
14
|
+
end
|
15
|
+
|
16
|
+
# Accessor method for {#enabled=}.
|
17
|
+
def enabled?
|
18
|
+
@enabled != false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(app)
|
23
|
+
@app = app
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
|
+
require "timber/integrations/rack/middleware"
|
2
|
+
|
1
3
|
module Timber
|
2
4
|
module Integrations
|
3
5
|
module Rack
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
@app = app
|
8
|
-
end
|
9
|
-
|
6
|
+
# A Rack middleware that is responsible for adding the Session context
|
7
|
+
# {Timber::Contexts::Session}.
|
8
|
+
class SessionContext < Middleware
|
10
9
|
def call(env)
|
11
10
|
id = get_session_id(env)
|
12
11
|
if id
|
@@ -1,72 +1,119 @@
|
|
1
|
+
require "timber/integrations/rack/middleware"
|
2
|
+
|
1
3
|
module Timber
|
2
4
|
module Integrations
|
3
5
|
module Rack
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
# This is a Rack middleware responsible for setting the user context.
|
7
|
+
# See {Timber::Contexts::User} for more information on the user context.
|
8
|
+
#
|
9
|
+
# We use a Rack middleware because we want to set the user context as early as
|
10
|
+
# possible, and before the initial incoming request log line:
|
11
|
+
#
|
12
|
+
# Started GET /welcome
|
13
|
+
#
|
14
|
+
# The above log line is logged in a request middleware, before it reaches
|
15
|
+
# the controller.
|
16
|
+
#
|
17
|
+
# If, for example, we set the user context in a controller, the log line above
|
18
|
+
# will not have the user context attached. This is because it is logged before
|
19
|
+
# the controller is executed. This is not ideal, and it's why we take a middleware
|
20
|
+
# approach here. If for some reason you cannot identify the user at the middleware
|
21
|
+
# level then setting it in the controller is perfectly fine, just be aware of the
|
22
|
+
# above downside.
|
23
|
+
#
|
24
|
+
# ## Authentication frameworks automatically detected:
|
25
|
+
#
|
26
|
+
# If you use any of the following authentication frameworks, Timber will
|
27
|
+
# automatically set the user context for you.
|
28
|
+
#
|
29
|
+
# * Devise, or any Warden based authentication strategy
|
30
|
+
# * Omniauth
|
31
|
+
# * Clearance
|
32
|
+
#
|
33
|
+
# Or, you can use your own custom authentication, see the {.custom_user_context}
|
34
|
+
# class method for more details.
|
35
|
+
#
|
36
|
+
# @note This middleware is automatically inserted for frameworks we support.
|
37
|
+
# Such as Rails. See {Timber::Frameworks} for a comprehensive list.
|
38
|
+
class UserContext < Middleware
|
39
|
+
class << self
|
40
|
+
# The custom user context allows you to hook in and set your own custom
|
41
|
+
# user context. This is used in situations where either:
|
42
|
+
#
|
43
|
+
# 1. Timber does not automatically support your authentication strategy (see module level docs)
|
44
|
+
# 2. You need to customize your authentication beyond Timber's defaults.
|
45
|
+
#
|
46
|
+
# @example Setting your own custom user context
|
47
|
+
# Timber::Integrations::Rack::UserContext.custom_user_hash = lambda do |rack_env|
|
48
|
+
# rach_env['my_custom_key'].user
|
49
|
+
# end
|
50
|
+
def custom_user_hash=(proc)
|
51
|
+
if proc && !proc.is_a?(Proc)
|
52
|
+
raise ArgumentError.new("The value passed to #custom_user_hash must be a Proc")
|
53
|
+
end
|
54
|
+
|
55
|
+
@custom_user_hash = proc
|
56
|
+
end
|
57
|
+
|
58
|
+
# Accessor method for {#custom_user_hash=}.
|
59
|
+
def custom_user_hash
|
60
|
+
@custom_user_hash
|
61
|
+
end
|
8
62
|
end
|
9
63
|
|
10
64
|
def call(env)
|
11
|
-
debug { "#{self.class.name} - Starting user context" }
|
12
65
|
user_hash = get_user_hash(env)
|
13
66
|
if user_hash
|
14
|
-
debug { "#{self.class.name} - User hash found: #{user_hash.inspect}" }
|
15
67
|
context = Contexts::User.new(user_hash)
|
16
68
|
CurrentContext.with(context) do
|
17
69
|
@app.call(env)
|
18
70
|
end
|
19
71
|
else
|
20
|
-
debug { "#{self.class.name} - User hash not found" }
|
21
72
|
@app.call(env)
|
22
73
|
end
|
23
74
|
end
|
24
75
|
|
25
76
|
private
|
26
77
|
def get_user_hash(env)
|
27
|
-
|
28
|
-
|
78
|
+
# The order is relevant here. The 'warden' key can be set, but
|
79
|
+
# not return a user, in which case the user data might be in the omniauth
|
80
|
+
# data.
|
81
|
+
if self.class.custom_user_hash.is_a?(Proc)
|
82
|
+
debug { "Obtaining user context from the custom user hash" }
|
83
|
+
self.class.custom_user_hash.call(env)
|
84
|
+
elsif (auth_hash = env['omniauth.auth'])
|
85
|
+
debug { "Obtaining user context from the omniauth auth hash" }
|
86
|
+
get_omniauth_user_hash(auth_hash)
|
87
|
+
elsif env[:clearance] && env[:clearance].signed_in?
|
88
|
+
debug { "Obtaining user context from the clearance user" }
|
89
|
+
user = env[:clearance].current_user
|
90
|
+
get_user_object_hash(user)
|
91
|
+
elsif env['warden'] && (user = env['warden'].user)
|
92
|
+
debug { "Obtaining user context from the warden user" }
|
93
|
+
get_user_object_hash(user)
|
94
|
+
else
|
95
|
+
debug { "Could not locate any user data" }
|
29
96
|
nil
|
97
|
+
end
|
30
98
|
end
|
31
99
|
|
32
|
-
def get_omniauth_user_hash(
|
33
|
-
|
34
|
-
debug { "#{self.class.name} - Omniauth hash present #{env['omniauth.auth'].inspect}" }
|
35
|
-
auth_hash = env['omniauth.auth']
|
36
|
-
info = auth_hash['info']
|
100
|
+
def get_omniauth_user_hash(auth_hash)
|
101
|
+
info = auth_hash['info']
|
37
102
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
else
|
44
|
-
debug { "#{self.class.name} - Omniauth hash not present" }
|
45
|
-
nil
|
46
|
-
end
|
103
|
+
{
|
104
|
+
id: auth_hash['uid'],
|
105
|
+
name: info['name'],
|
106
|
+
email: info['email']
|
107
|
+
}
|
47
108
|
end
|
48
109
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
if user
|
54
|
-
debug { "#{self.class.name} - Warden user object #{env['warden'].user.inspect}" }
|
55
|
-
id = try_user_id(user)
|
56
|
-
name = try_user_name(user)
|
57
|
-
email = try_user_email(user)
|
110
|
+
def get_user_object_hash(user)
|
111
|
+
id = try_user_id(user)
|
112
|
+
name = try_user_name(user)
|
113
|
+
email = try_user_email(user)
|
58
114
|
|
59
|
-
|
60
|
-
|
61
|
-
{id: id, name: name, email: email}
|
62
|
-
else
|
63
|
-
debug { "#{self.class.name} - No warden user attributes were present" }
|
64
|
-
nil
|
65
|
-
end
|
66
|
-
else
|
67
|
-
debug { "#{self.class.name} - Warden user object not present, not logged in" }
|
68
|
-
nil
|
69
|
-
end
|
115
|
+
if id || name || email
|
116
|
+
{id: id, name: name, email: email}
|
70
117
|
else
|
71
118
|
nil
|
72
119
|
end
|