timberio 1.0.0.beta1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -2
- data/.rspec +2 -0
- data/.yardopts +6 -0
- data/Appraisals +4 -0
- data/Gemfile +9 -1
- data/LICENSE.md +15 -0
- data/README.md +170 -8
- data/circle.yml +11 -8
- data/lib/timber/config.rb +11 -18
- data/lib/timber/context.rb +9 -68
- data/lib/timber/contexts/custom.rb +27 -0
- data/lib/timber/contexts/http.rb +28 -0
- data/lib/timber/contexts/organization.rb +24 -22
- data/lib/timber/contexts/user.rb +25 -28
- data/lib/timber/contexts.rb +7 -20
- data/lib/timber/current_context.rb +26 -41
- data/lib/timber/event.rb +13 -0
- data/lib/timber/events/controller_call.rb +40 -0
- data/lib/timber/events/custom.rb +42 -0
- data/lib/timber/events/exception.rb +35 -0
- data/lib/timber/events/http_request.rb +50 -0
- data/lib/timber/events/http_response.rb +36 -0
- data/lib/timber/events/sql_query.rb +26 -0
- data/lib/timber/events/template_render.rb +26 -0
- data/lib/timber/events.rb +37 -0
- data/lib/timber/frameworks/rails.rb +3 -14
- data/lib/timber/frameworks.rb +8 -10
- data/lib/timber/log_devices/http.rb +72 -13
- data/lib/timber/log_devices.rb +8 -4
- data/lib/timber/log_entry.rb +59 -0
- data/lib/timber/logger.rb +136 -13
- data/lib/timber/probe.rb +6 -4
- data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +64 -0
- data/lib/timber/probes/action_controller_log_subscriber.rb +20 -0
- data/lib/timber/probes/action_dispatch_debug_exceptions.rb +59 -35
- data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +62 -0
- data/lib/timber/probes/action_view_log_subscriber.rb +20 -0
- data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +72 -0
- data/lib/timber/probes/active_record_log_subscriber.rb +20 -0
- data/lib/timber/probes/rack_http_context.rb +51 -0
- data/lib/timber/probes/rails_rack_logger.rb +76 -0
- data/lib/timber/probes.rb +13 -16
- data/lib/timber/util/active_support_log_subscriber.rb +33 -0
- data/lib/timber/util/hash.rb +14 -0
- data/lib/timber/util.rb +8 -0
- data/lib/timber/version.rb +2 -2
- data/lib/timber.rb +6 -11
- data/spec/spec_helper.rb +7 -4
- data/spec/support/rails.rb +9 -5
- data/spec/support/timber.rb +1 -20
- data/spec/timber/events_spec.rb +55 -0
- data/spec/timber/log_devices/http_spec.rb +62 -0
- data/spec/timber/logger_spec.rb +68 -0
- data/spec/timber/probes/action_controller_log_subscriber_spec.rb +70 -0
- data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +24 -18
- data/spec/timber/probes/action_view_log_subscriber_spec.rb +61 -0
- data/spec/timber/probes/active_record_log_subscriber_spec.rb +49 -0
- data/spec/timber/probes/rack_http_context_spec.rb +54 -0
- data/spec/timber/probes/rails_rack_logger_spec.rb +46 -0
- data/timberio.gemspec +2 -0
- metadata +62 -123
- data/.codeclimate.yml +0 -34
- data/LICENSE +0 -38
- data/Rakefile +0 -4
- data/TODO +0 -4
- data/benchmark/README.md +0 -26
- data/benchmark/rails_request.rb +0 -68
- data/benchmark/support/rails.rb +0 -69
- data/docs/installation/rails_on_heroku.md +0 -31
- data/docs/installation/rails_over_http.md +0 -22
- data/gemfiles/rails_3.0.X.gemfile +0 -25
- data/gemfiles/rails_3.1.X.gemfile +0 -25
- data/gemfiles/rails_3.2.X.gemfile +0 -25
- data/gemfiles/rails_4.0.X.gemfile +0 -26
- data/gemfiles/rails_4.1.X.gemfile +0 -26
- data/gemfiles/rails_4.2.X.gemfile +0 -26
- data/gemfiles/rails_5.0.X.gemfile +0 -26
- data/gemfiles/rails_edge.gemfile +0 -27
- data/lib/timber/api_settings.rb +0 -17
- data/lib/timber/bootstrap.rb +0 -45
- data/lib/timber/context_snapshot.rb +0 -64
- data/lib/timber/contexts/dynamic_values.rb +0 -59
- data/lib/timber/contexts/exception.rb +0 -40
- data/lib/timber/contexts/http_request.rb +0 -22
- data/lib/timber/contexts/http_requests/action_controller_specific.rb +0 -48
- data/lib/timber/contexts/http_requests/rack/params.rb +0 -26
- data/lib/timber/contexts/http_requests/rack.rb +0 -105
- data/lib/timber/contexts/http_response.rb +0 -19
- data/lib/timber/contexts/http_responses/action_controller.rb +0 -76
- data/lib/timber/contexts/logger.rb +0 -33
- data/lib/timber/contexts/organizations/action_controller.rb +0 -34
- data/lib/timber/contexts/server.rb +0 -21
- data/lib/timber/contexts/servers/heroku_specific.rb +0 -48
- data/lib/timber/contexts/sql_queries/active_record.rb +0 -30
- data/lib/timber/contexts/sql_queries/active_record_specific/binds.rb +0 -37
- data/lib/timber/contexts/sql_queries/active_record_specific.rb +0 -59
- data/lib/timber/contexts/sql_query.rb +0 -18
- data/lib/timber/contexts/template_render.rb +0 -17
- data/lib/timber/contexts/template_renders/action_view.rb +0 -29
- data/lib/timber/contexts/template_renders/action_view_specific.rb +0 -51
- data/lib/timber/contexts/users/action_controller.rb +0 -34
- data/lib/timber/current_line_indexes.rb +0 -35
- data/lib/timber/internal_logger.rb +0 -35
- data/lib/timber/log_device.rb +0 -40
- data/lib/timber/log_devices/heroku_logplex/hybrid_formatter.rb +0 -14
- data/lib/timber/log_devices/heroku_logplex.rb +0 -14
- data/lib/timber/log_devices/http/log_pile.rb +0 -86
- data/lib/timber/log_devices/http/log_truck/delivery.rb +0 -116
- data/lib/timber/log_devices/http/log_truck.rb +0 -87
- data/lib/timber/log_devices/io/formatter.rb +0 -46
- data/lib/timber/log_devices/io/hybrid_formatter.rb +0 -41
- data/lib/timber/log_devices/io/hybrid_hidden_formatter.rb +0 -36
- data/lib/timber/log_devices/io/json_formatter.rb +0 -11
- data/lib/timber/log_devices/io/logfmt_formatter.rb +0 -11
- data/lib/timber/log_devices/io.rb +0 -41
- data/lib/timber/log_line.rb +0 -33
- data/lib/timber/macros/compactor.rb +0 -16
- data/lib/timber/macros/date_formatter.rb +0 -9
- data/lib/timber/macros/deep_merger.rb +0 -11
- data/lib/timber/macros/logfmt_encoder.rb +0 -77
- data/lib/timber/macros.rb +0 -4
- data/lib/timber/patterns/delegated_singleton.rb +0 -21
- data/lib/timber/patterns/to_json.rb +0 -22
- data/lib/timber/patterns/to_logfmt.rb +0 -9
- data/lib/timber/patterns.rb +0 -3
- data/lib/timber/probes/action_controller_base.rb +0 -31
- data/lib/timber/probes/active_support_log_subscriber/action_controller.rb +0 -15
- data/lib/timber/probes/active_support_log_subscriber/action_view.rb +0 -26
- data/lib/timber/probes/active_support_log_subscriber/active_record.rb +0 -13
- data/lib/timber/probes/active_support_log_subscriber.rb +0 -62
- data/lib/timber/probes/heroku.rb +0 -30
- data/lib/timber/probes/logger.rb +0 -31
- data/lib/timber/probes/rack.rb +0 -36
- data/lib/timber/probes/server.rb +0 -18
- data/spec/timber/bootstrap_spec.rb +0 -31
- data/spec/timber/context_snapshot_spec.rb +0 -10
- data/spec/timber/context_spec.rb +0 -4
- data/spec/timber/contexts/exception_spec.rb +0 -34
- data/spec/timber/contexts/organizations/action_controller_spec.rb +0 -49
- data/spec/timber/contexts/users/action_controller_spec.rb +0 -65
- data/spec/timber/current_line_indexes_spec.rb +0 -40
- data/spec/timber/frameworks/rails_spec.rb +0 -9
- data/spec/timber/log_devices/heroku_logplex_spec.rb +0 -45
- data/spec/timber/log_devices/http/log_truck/delivery_spec.rb +0 -66
- data/spec/timber/log_devices/http/log_truck_spec.rb +0 -65
- data/spec/timber/log_devices/io/hybrid_hidden_formatter_spec.rb +0 -28
- data/spec/timber/log_line_spec.rb +0 -49
- data/spec/timber/macros/compactor_spec.rb +0 -19
- data/spec/timber/macros/logfmt_encoder_spec.rb +0 -89
- data/spec/timber/patterns/to_json_spec.rb +0 -40
- data/spec/timber/probes/action_controller_base_spec.rb +0 -43
- data/spec/timber/probes/action_controller_log_subscriber/action_controller_spec.rb +0 -35
- data/spec/timber/probes/action_controller_log_subscriber/action_view_spec.rb +0 -44
- data/spec/timber/probes/action_controller_log_subscriber/active_record_spec.rb +0 -26
- data/spec/timber/probes/logger_spec.rb +0 -20
- data/spec/timber/probes/rack_spec.rb +0 -26
@@ -1,58 +1,43 @@
|
|
1
|
-
require "
|
1
|
+
require "singleton"
|
2
2
|
|
3
3
|
module Timber
|
4
|
-
# Holds the current context in
|
5
|
-
#
|
4
|
+
# Holds the current context in a thread safe memory storage. This context is
|
5
|
+
# appended to every log line. Think of context as join data between your log lines,
|
6
|
+
# allowing you to relate them and filter them appropriately.
|
6
7
|
class CurrentContext
|
7
|
-
|
8
|
-
STACK_KEYNAME = :stack.freeze
|
9
|
-
PRECISION = 8.freeze
|
8
|
+
include Singleton
|
10
9
|
|
11
|
-
|
10
|
+
THREAD_NAMESPACE = :_timber_current_context.freeze
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
class << self
|
13
|
+
# Convenience method for {#with}.
|
14
|
+
#
|
15
|
+
# @example Adding a context
|
16
|
+
# custom_context = Timber::Contexts::Custom.new(type: :keyspace, data: %{my: "data"})
|
17
|
+
# Timber::CurrentContext.with(custom_context) do
|
18
|
+
# # ... anything logged here will have the context ...
|
19
|
+
# end
|
20
|
+
def with(*args, &block)
|
21
|
+
instance.with(*args, &block)
|
18
22
|
end
|
19
|
-
block_given? ? yield : self
|
20
|
-
ensure
|
21
|
-
remove(*contexts) if block_given?
|
22
23
|
end
|
23
24
|
|
24
|
-
#
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# Ensure we clear the cacke when the stack changes
|
32
|
-
contexts.each do |context|
|
33
|
-
CurrentLineIndexes.context_removed(context)
|
34
|
-
stack.delete(context)
|
35
|
-
end
|
36
|
-
self
|
25
|
+
# Adds a context to the current stack.
|
26
|
+
def with(data)
|
27
|
+
key = data.keyspace
|
28
|
+
hash[key] = data
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
hash.delete(key)
|
37
32
|
end
|
38
33
|
|
39
|
-
# Used to efficiently clone the context
|
40
34
|
def snapshot
|
41
|
-
|
42
|
-
Timber::ContextSnapshot.new
|
43
|
-
end
|
44
|
-
|
45
|
-
def valid_stack
|
46
|
-
stack.select(&:valid?)
|
35
|
+
hash.clone
|
47
36
|
end
|
48
37
|
|
49
38
|
private
|
50
|
-
def
|
51
|
-
storage[STACK_KEYNAME] ||= []
|
52
|
-
end
|
53
|
-
|
54
|
-
def storage
|
39
|
+
def hash
|
55
40
|
Thread.current[THREAD_NAMESPACE] ||= {}
|
56
41
|
end
|
57
42
|
end
|
58
|
-
end
|
43
|
+
end
|
data/lib/timber/event.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# The controller call event tracks controller invocations. For example, this line in Rails:
|
4
|
+
#
|
5
|
+
# Processing by PagesController#home as HTML
|
6
|
+
#
|
7
|
+
# @note This event should be installed automatically through probes,
|
8
|
+
# such as the {Probes::ActionControllerLogSubscriber} probe.
|
9
|
+
class ControllerCall < Timber::Event
|
10
|
+
attr_reader :controller, :action, :params, :format
|
11
|
+
|
12
|
+
def initialize(attributes)
|
13
|
+
@controller = attributes[:controller] || raise(ArgumentError.new(":controller is required"))
|
14
|
+
@action = attributes[:action] || raise(ArgumentError.new(":action is required"))
|
15
|
+
@params = attributes[:params]
|
16
|
+
@format = attributes[:format]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
{controller: controller, action: action}
|
21
|
+
end
|
22
|
+
alias to_h to_hash
|
23
|
+
|
24
|
+
def as_json(_options = {})
|
25
|
+
{:controller_call => to_hash}
|
26
|
+
end
|
27
|
+
|
28
|
+
def message
|
29
|
+
message = "Processing by #{controller}##{action}"
|
30
|
+
if !message.nil?
|
31
|
+
message << " as #{format}"
|
32
|
+
end
|
33
|
+
if !params.nil? && params.length > 0
|
34
|
+
message << "\n Parameters: #{params.inspect}"
|
35
|
+
end
|
36
|
+
message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# Allows for custom events that aren't covered elsewhere.
|
4
|
+
#
|
5
|
+
# Custom events can be used to encode information about events that are central
|
6
|
+
# to your line of business like receiving credit card payments, saving a draft of a post,
|
7
|
+
# or changing a user's password.
|
8
|
+
#
|
9
|
+
# For examples of logging custom events see {Logger}.
|
10
|
+
class Custom < Timber::Event
|
11
|
+
attr_reader :type, :message, :data
|
12
|
+
|
13
|
+
# Instantiates a new custom event that can be logged. See {Logger} for examples
|
14
|
+
# on logging custom events.
|
15
|
+
#
|
16
|
+
# @param [Hash] attributes the options to create a custom event with.
|
17
|
+
# @option attributes [Symbol] :type *required* The custom event type. This should be in
|
18
|
+
# snake case. Example: `:my_custom_event`.
|
19
|
+
# @option attributes [String] :message *required* The message to be logged.
|
20
|
+
# @option attributes [Hash] :data A hash of JSON encodable data to be stored with the
|
21
|
+
# log line.
|
22
|
+
def initialize(attributes)
|
23
|
+
@type = attributes[:type] || raise(ArgumentError.new(":type is required"))
|
24
|
+
@message = attributes[:message] || raise(ArgumentError.new(":message is required"))
|
25
|
+
@data = attributes[:data]
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_hash
|
29
|
+
{type => data}
|
30
|
+
end
|
31
|
+
alias to_h to_hash
|
32
|
+
|
33
|
+
def as_json(_options = {})
|
34
|
+
{:custom => to_hash}
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_json(options = {})
|
38
|
+
as_json().to_json(options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# The exception event is used to track exceptions.
|
4
|
+
#
|
5
|
+
# @note This event should be installed automatically through probes,
|
6
|
+
# such as the {Probes::ActionDispatchDebugExceptions} probe.
|
7
|
+
class Exception < Timber::Event
|
8
|
+
attr_reader :name, :exception_message, :backtrace
|
9
|
+
|
10
|
+
def initialize(attributes)
|
11
|
+
@name = attributes[:name] || raise(ArgumentError.new(":name is required"))
|
12
|
+
@exception_message = attributes[:exception_message] || raise(ArgumentError.new(":exception_message is required"))
|
13
|
+
@backtrace = attributes[:backtrace]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
{name: name, message: exception_message, backtrace: backtrace}
|
18
|
+
end
|
19
|
+
alias to_h to_hash
|
20
|
+
|
21
|
+
def as_json(_options = {})
|
22
|
+
{:exception => to_hash}
|
23
|
+
end
|
24
|
+
|
25
|
+
def message
|
26
|
+
message = "#{name} (#{exception_message}):"
|
27
|
+
if backtrace.is_a?(Array) && backtrace.length > 0
|
28
|
+
message << "\n\n"
|
29
|
+
message << backtrace.join("\n")
|
30
|
+
end
|
31
|
+
message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# The HTTP request event tracks incoming HTTP requests.
|
4
|
+
#
|
5
|
+
# @note This event should be installed automatically through probes,
|
6
|
+
# such as the {Probes::ActionControllerLogSubscriber} probe.
|
7
|
+
class HTTPRequest < Timber::Event
|
8
|
+
attr_reader :host, :method, :path, :port, :query_params, :content_type,
|
9
|
+
:remote_addr, :referrer, :request_id, :user_agent
|
10
|
+
|
11
|
+
def initialize(attributes)
|
12
|
+
@host = attributes[:host] || raise(ArgumentError.new(":host is required"))
|
13
|
+
@method = attributes[:method] || raise(ArgumentError.new(":method is required"))
|
14
|
+
@path = attributes[:path] || raise(ArgumentError.new(":path is required"))
|
15
|
+
@port = attributes[:port]
|
16
|
+
@query_params = attributes[:query_params]
|
17
|
+
@content_type = attributes[:content_type]
|
18
|
+
@remote_addr = attributes[:remote_addr]
|
19
|
+
@referrer = attributes[:referrer]
|
20
|
+
@request_id = attributes[:request_id]
|
21
|
+
@user_agent = attributes[:user_agent]
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_hash
|
25
|
+
{host: host, method: method, path: path, port: port, query_params: query_params,
|
26
|
+
headers: {content_type: content_type, remote_addr: remote_addr, referrer: referrer,
|
27
|
+
request_id: request_id, user_agent: user_agent}}
|
28
|
+
end
|
29
|
+
alias to_h to_hash
|
30
|
+
|
31
|
+
def as_json(_options = {})
|
32
|
+
hash = to_hash
|
33
|
+
hash[:headers] = Util::Hash.compact(hash[:headers])
|
34
|
+
hash = Util::Hash.compact(hash)
|
35
|
+
{:http_request => hash}
|
36
|
+
end
|
37
|
+
|
38
|
+
def message
|
39
|
+
'Started %s "%s" for %s' % [
|
40
|
+
method,
|
41
|
+
path,
|
42
|
+
remote_addr]
|
43
|
+
end
|
44
|
+
|
45
|
+
def status_description
|
46
|
+
Rack::Utils::HTTP_STATUS_CODES[status]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# The HTTP response event tracks outgoing HTTP request responses.
|
4
|
+
#
|
5
|
+
# @note This event should be installed automatically through probes,
|
6
|
+
# such as the {Probes::ActionControllerLogSubscriber} probe.
|
7
|
+
class HTTPResponse < Timber::Event
|
8
|
+
attr_reader :status, :time_ms, :additions
|
9
|
+
|
10
|
+
def initialize(attributes)
|
11
|
+
@status = attributes[:status] || raise(ArgumentError.new(":status is required"))
|
12
|
+
@time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required"))
|
13
|
+
@additions = attributes[:additions]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
{status: status, time_ms: time_ms}
|
18
|
+
end
|
19
|
+
alias to_h to_hash
|
20
|
+
|
21
|
+
def as_json(_options = {})
|
22
|
+
{:http_response => to_hash}
|
23
|
+
end
|
24
|
+
|
25
|
+
def message
|
26
|
+
message = "Completed #{status} #{status_description} in #{time_ms}ms"
|
27
|
+
message << " (#{additions.join(" | ".freeze)})" unless additions.empty?
|
28
|
+
message
|
29
|
+
end
|
30
|
+
|
31
|
+
def status_description
|
32
|
+
Rack::Utils::HTTP_STATUS_CODES[status]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# The SQL query event tracks sql queries to your database.
|
4
|
+
#
|
5
|
+
# @note This event should be installed automatically through probes,
|
6
|
+
# such as the {Probes::ActiveRecordLogSubscriber} probe.
|
7
|
+
class SQLQuery < Timber::Event
|
8
|
+
attr_reader :sql, :time_ms, :message
|
9
|
+
|
10
|
+
def initialize(attributes)
|
11
|
+
@sql = attributes[:sql] || raise(ArgumentError.new(":sql is required"))
|
12
|
+
@time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required"))
|
13
|
+
@message = attributes[:message] || raise(ArgumentError.new(":message is required"))
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
{sql: sql, time_ms: time_ms}
|
18
|
+
end
|
19
|
+
alias to_h to_hash
|
20
|
+
|
21
|
+
def as_json(_options = {})
|
22
|
+
{:sql_query => to_hash}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# The template render event track template renderings and their performance.
|
4
|
+
#
|
5
|
+
# @note This event should be installed automatically through probes,
|
6
|
+
# such as the {Probes::ActionViewLogSubscriber} probe.
|
7
|
+
class TemplateRender < Timber::Event
|
8
|
+
attr_reader :message, :name, :time_ms
|
9
|
+
|
10
|
+
def initialize(attributes)
|
11
|
+
@message = attributes[:message] || raise(ArgumentError.new(":message is required"))
|
12
|
+
@name = attributes[:name] || raise(ArgumentError.new(":name is required"))
|
13
|
+
@time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required"))
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
{name: name, time_ms: time_ms}
|
18
|
+
end
|
19
|
+
alias to_h to_hash
|
20
|
+
|
21
|
+
def as_json(_options = {})
|
22
|
+
{:template_render => to_hash}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "timber/events/controller_call"
|
2
|
+
require "timber/events/custom"
|
3
|
+
require "timber/events/exception"
|
4
|
+
require "timber/events/http_request"
|
5
|
+
require "timber/events/http_response"
|
6
|
+
require "timber/events/sql_query"
|
7
|
+
require "timber/events/template_render"
|
8
|
+
|
9
|
+
module Timber
|
10
|
+
module Events
|
11
|
+
# Protocol for casting objects into a `Timber::Event`.
|
12
|
+
#
|
13
|
+
# @example Casting a hash
|
14
|
+
# Timber::Events.build({type: :custom_event, message: "My log message", data: {my: "data"}})
|
15
|
+
def self.build(obj)
|
16
|
+
if obj.is_a?(::Timber::Event)
|
17
|
+
obj
|
18
|
+
elsif obj.respond_to?(:to_timber_event)
|
19
|
+
obj.to_timber_event
|
20
|
+
elsif obj.is_a?(Hash) && obj.key?(:message) && obj.key?(:type) && obj.key?(:data)
|
21
|
+
Events::Custom.new(
|
22
|
+
type: obj[:type],
|
23
|
+
message: obj[:message],
|
24
|
+
data: obj[:data]
|
25
|
+
)
|
26
|
+
elsif obj.is_a?(Struct) && obj.respond_to?(:message) && obj.respond_to?(:type)
|
27
|
+
Events::Custom.new(
|
28
|
+
type: obj.type,
|
29
|
+
message: obj.message,
|
30
|
+
data: obj.respond_to?(:hash) ? obj.hash : obj.to_h # ruby 1.9.3 does not have to_h
|
31
|
+
)
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,24 +1,13 @@
|
|
1
1
|
module Timber
|
2
2
|
module Frameworks
|
3
3
|
module Rails
|
4
|
+
# Installs Timber into your Rails app automatically.
|
4
5
|
class Railtie < ::Rails::Railtie
|
5
6
|
config.timber = Config.instance
|
6
7
|
config.before_initialize do
|
7
|
-
|
8
|
+
Probes.insert!(config.app_middleware, ::Rails::Rack::Logger)
|
8
9
|
end
|
9
10
|
end
|
10
|
-
|
11
|
-
def self.base_logger(logdev)
|
12
|
-
defined?(::ActiveSupport::Logger) ?
|
13
|
-
::ActiveSupport::Logger.new(logdev) :
|
14
|
-
::Logger.new(logdev)
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.logger(logdev)
|
18
|
-
defined?(::ActiveSupport::TaggedLogging) ?
|
19
|
-
::ActiveSupport::TaggedLogging.new(base_logger(logdev)) :
|
20
|
-
base_logger(logdev)
|
21
|
-
end
|
22
11
|
end
|
23
12
|
end
|
24
|
-
end
|
13
|
+
end
|
data/lib/timber/frameworks.rb
CHANGED
@@ -2,20 +2,18 @@ require "logger"
|
|
2
2
|
|
3
3
|
# Attempt to require Rails. We can not list it as a gem
|
4
4
|
# dependency because we want to support multiple frameworks.
|
5
|
-
|
5
|
+
begin
|
6
|
+
require("rails")
|
7
|
+
rescue LoadError
|
8
|
+
end
|
6
9
|
|
7
|
-
if defined?(Rails)
|
10
|
+
if defined?(::Rails) && defined?(::Rails::Railtie)
|
8
11
|
require 'timber/frameworks/rails'
|
9
12
|
end
|
10
13
|
|
11
14
|
module Timber
|
15
|
+
# Namespace for installing Timber into frameworks
|
16
|
+
# @private
|
12
17
|
module Frameworks
|
13
|
-
def self.logger(logdev)
|
14
|
-
if defined?(Timber::Frameworks::Rails)
|
15
|
-
Rails.logger(logdev)
|
16
|
-
else
|
17
|
-
::Logger.new(logdev)
|
18
|
-
end
|
19
|
-
end
|
20
18
|
end
|
21
|
-
end
|
19
|
+
end
|
@@ -1,27 +1,86 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "monitor"
|
2
|
+
require "msgpack"
|
3
3
|
|
4
4
|
module Timber
|
5
5
|
module LogDevices
|
6
|
-
|
7
|
-
|
6
|
+
# A log device that buffers and sends logs to the Timber API over HTTP in intervals. The buffer
|
7
|
+
# uses MessagePack::Buffer, which is fast, efficient with memory, and reduces
|
8
|
+
# the payload size sent to Timber.
|
9
|
+
class HTTP
|
10
|
+
class DeliveryError < StandardError; end
|
8
11
|
|
9
|
-
|
12
|
+
API_URI = URI.parse("https://api.timber.io/http_frames")
|
13
|
+
CONTENT_TYPE = "application/json".freeze
|
14
|
+
CONNECTION_HEADER = "keep-alive".freeze
|
15
|
+
USER_AGENT = "Timber Ruby Gem/#{Timber::VERSION}".freeze
|
10
16
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
HTTPS = Net::HTTP.new(API_URI.host, API_URI.port).tap do |https|
|
18
|
+
https.use_ssl = true
|
19
|
+
https.read_timeout = 30
|
20
|
+
https.ssl_timeout = 10
|
21
|
+
if https.respond_to?(:keep_alive_timeout=)
|
22
|
+
https.keep_alive_timeout = 60
|
15
23
|
end
|
16
|
-
|
24
|
+
https.open_timeout = 10
|
17
25
|
end
|
18
26
|
|
19
|
-
|
27
|
+
DEFAULT_DELIVERY_FREQUENCY = 2.freeze
|
28
|
+
|
29
|
+
# Instantiates a new HTTP log device.
|
30
|
+
#
|
31
|
+
# @param api_key [String] The API key provided to you after you add your application to
|
32
|
+
# [Timber](https://timber.io).
|
33
|
+
# @param [Hash] options the options to create a HTTP log device with.
|
34
|
+
# @option attributes [Symbol] :frequency_seconds (2) How often the client should
|
35
|
+
# attempt to deliver logs to the Timber API. The HTTP client buffers logs between calls.
|
36
|
+
def initialize(api_key, options = {})
|
37
|
+
@api_key = api_key
|
38
|
+
@buffer = []
|
39
|
+
@monitor = Monitor.new
|
40
|
+
@delivery_thread = Thread.new do
|
41
|
+
at_exit { deliver }
|
42
|
+
loop do
|
43
|
+
sleep options[:frequency_seconds] || DEFAULT_DELIVERY_FREQUENCY
|
44
|
+
deliver
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def write(msg)
|
50
|
+
@monitor.synchronize {
|
51
|
+
@buffer << msg
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def close
|
56
|
+
@delivery_thread.kill
|
20
57
|
end
|
21
58
|
|
22
59
|
private
|
23
|
-
def
|
24
|
-
|
60
|
+
def deliver
|
61
|
+
body = @buffer.read
|
62
|
+
|
63
|
+
request = Net::HTTP::Post.new(API_URI.request_uri).tap do |req|
|
64
|
+
req['Authorization'] = authorization_payload
|
65
|
+
req['Connection'] = CONNECTION_HEADER
|
66
|
+
req['Content-Type'] = CONTENT_TYPE
|
67
|
+
req['User-Agent'] = USER_AGENT
|
68
|
+
req.body = body
|
69
|
+
end
|
70
|
+
|
71
|
+
HTTPS.request(request).tap do |res|
|
72
|
+
code = res.code.to_i
|
73
|
+
if code < 200 || code >= 300
|
74
|
+
raise DeliveryError.new("Bad response from Timber API - #{res.code}: #{res.body}")
|
75
|
+
end
|
76
|
+
Config.instance.logger.debug("Success! #{code}: #{res.body}")
|
77
|
+
end
|
78
|
+
|
79
|
+
@buffer.clear
|
80
|
+
end
|
81
|
+
|
82
|
+
def authorization_payload
|
83
|
+
@authorization_payload ||= "Basic #{Base64.strict_encode64(@api_key).chomp}"
|
25
84
|
end
|
26
85
|
end
|
27
86
|
end
|
data/lib/timber/log_devices.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require "timber/log_devices/http"
|
2
|
+
|
3
|
+
module Timber
|
4
|
+
# Namespace for all log devices.
|
5
|
+
# @private
|
6
|
+
module LogDevices
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Timber
|
2
|
+
# Represents a new log entry into the log. This is an intermediary class between
|
3
|
+
# `Logger` and the log device that you set it up with.
|
4
|
+
class LogEntry #:nodoc:
|
5
|
+
DT_PRECISION = 6.freeze
|
6
|
+
|
7
|
+
attr_reader :level, :time, :progname, :message, :context, :event
|
8
|
+
|
9
|
+
# Creates a log entry suitable to be sent to the Timber API.
|
10
|
+
# @param severity [Integer] the log level / severity
|
11
|
+
# @param time [Time] the exact time the log message was written
|
12
|
+
# @param progname [String] the progname scope for the log message
|
13
|
+
# @param message [#to_json] structured data representing the log line event, this can
|
14
|
+
# be anything that responds to #to_json
|
15
|
+
# @return [LogEntry] the resulting LogEntry object
|
16
|
+
def initialize(level, time, progname, message, context, event)
|
17
|
+
@level = level
|
18
|
+
@time = time.utc
|
19
|
+
@progname = progname
|
20
|
+
@message = message
|
21
|
+
@context = context
|
22
|
+
@event = event
|
23
|
+
end
|
24
|
+
|
25
|
+
def as_json(options = {})
|
26
|
+
options ||= {}
|
27
|
+
hash = {level: level, dt: formatted_dt, message: message}
|
28
|
+
|
29
|
+
if !event.nil?
|
30
|
+
hash[:event] = event
|
31
|
+
end
|
32
|
+
|
33
|
+
if !context.nil? && context.length > 0
|
34
|
+
hash[:context] = context
|
35
|
+
end
|
36
|
+
|
37
|
+
if options[:only]
|
38
|
+
hash.select do |key, _value|
|
39
|
+
options[:only].include?(key)
|
40
|
+
end
|
41
|
+
elsif options[:except]
|
42
|
+
hash.select do |key, _value|
|
43
|
+
!options[:except].include?(key)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
hash
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_json(options = {})
|
51
|
+
as_json(options).to_json
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def formatted_dt
|
56
|
+
@formatted_dt ||= time.iso8601(DT_PRECISION)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|