contextualized_logs 0.0.1.pre.alpha → 0.0.2.pre.alpha
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/README.md +217 -155
- data/app/controllers/application_controller.rb +1 -0
- data/app/controllers/concerns/datadog_set_trace_details.rb +36 -0
- data/app/controllers/custom_context_controller.rb +17 -0
- data/app/controllers/model_controller.rb +1 -1
- data/app/models/model.rb +1 -1
- data/app/workers/model_worker.rb +2 -2
- data/config/initializers/contextualized_logs.rb +23 -0
- data/config/initializers/datadog.rb +17 -0
- data/config/initializers/sidekiq.rb +13 -13
- data/config/routes.rb +1 -0
- data/db/development.sqlite3 +0 -0
- data/lib/contextualized_logs.rb +25 -0
- data/lib/contextualized_logs/config.rb +27 -0
- data/lib/contextualized_logs/contextualized_controller.rb +54 -40
- data/lib/contextualized_logs/contextualized_logger.rb +74 -67
- data/lib/contextualized_logs/contextualized_model.rb +28 -24
- data/lib/contextualized_logs/contextualized_worker.rb +52 -18
- data/lib/contextualized_logs/current_context.rb +69 -67
- data/lib/contextualized_logs/sidekiq/middleware/client/inject_current_context.rb +3 -3
- data/lib/contextualized_logs/sidekiq/middleware/server/restore_current_context.rb +3 -3
- data/lib/contextualized_logs/version.rb +1 -1
- metadata +7 -2
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
require 'active_support'
|
|
2
|
-
|
|
2
|
+
require 'active_record'
|
|
3
3
|
|
|
4
4
|
module ContextualizedLogs
|
|
5
5
|
module ContextualizedModel
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class_methods do
|
|
11
|
-
attr_reader :contextualizable_keys
|
|
8
|
+
class << self
|
|
9
|
+
attr_writer :current_context
|
|
12
10
|
|
|
13
11
|
def current_context
|
|
14
|
-
@current_context ||
|
|
12
|
+
@current_context || ContextualizedLogs.config.current_context
|
|
15
13
|
end
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
def included(base)
|
|
16
|
+
unless base.ancestors.include? ActiveRecord::Base
|
|
17
|
+
raise ArgumentError, "ContextualizedLogs::ContextualizedModel can only be included in a ActiveRecord::Base"
|
|
18
|
+
end
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
base.extend(ClassMethods)
|
|
21
|
+
|
|
22
|
+
base.class_eval do
|
|
23
|
+
after_find do |object|
|
|
24
|
+
# Rails.logger.debug "after_find #{object}"
|
|
25
|
+
ContextualizedModel.contextualize(object, self.class.contextualizable_keys, ContextualizedModel.current_context)
|
|
26
|
+
end
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
after_create do
|
|
29
|
+
# Rails.logger.debug "after_create #{self}"
|
|
30
|
+
ContextualizedModel.contextualize(self, self.class.contextualizable_keys, ContextualizedModel.current_context)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
25
33
|
end
|
|
26
|
-
end
|
|
27
34
|
|
|
28
|
-
class << self
|
|
29
35
|
def contextualize(model, keys, context)
|
|
30
36
|
# Rails.logger.debug "model: #{model}"
|
|
31
37
|
# Rails.logger.debug "keys: #{keys}"
|
|
32
38
|
# Rails.logger.debug "context.context: #{context}"
|
|
33
|
-
# Rails.logger.debug "context.
|
|
34
|
-
return unless context.
|
|
39
|
+
# Rails.logger.debug "context.contextualize_model_enabled: #{context.contextualize_model_enabled}"
|
|
40
|
+
return unless context.contextualize_model_enabled
|
|
35
41
|
keys&.each do |k, v|
|
|
36
42
|
v = model.try(v.to_sym)
|
|
37
43
|
context.add_context(k, v) if v
|
|
@@ -39,15 +45,13 @@ module ContextualizedLogs
|
|
|
39
45
|
end
|
|
40
46
|
end
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# Rails.logger.debug "after_find #{object}"
|
|
45
|
-
ContextualizedModel.contextualize(object, self.class.contextualizable_keys, self.class.current_context)
|
|
46
|
-
end
|
|
48
|
+
module ClassMethods
|
|
49
|
+
attr_reader :contextualizable_keys
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def contextualizable(keys: {})
|
|
54
|
+
@contextualizable_keys = keys
|
|
51
55
|
end
|
|
52
56
|
end
|
|
53
57
|
end
|
|
@@ -1,40 +1,74 @@
|
|
|
1
1
|
require 'active_support'
|
|
2
|
+
require 'sidekiq'
|
|
2
3
|
|
|
3
4
|
module ContextualizedLogs
|
|
4
5
|
module ContextualizedWorker
|
|
5
6
|
extend ActiveSupport::Concern
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class_methods do
|
|
12
|
-
# contextualize_args
|
|
8
|
+
class << self
|
|
9
|
+
attr_writer :current_context
|
|
10
|
+
attr_writer :default_contextualize_model
|
|
11
|
+
attr_writer :default_contextualize_worker
|
|
13
12
|
|
|
14
13
|
def current_context
|
|
15
|
-
@current_context ||
|
|
14
|
+
@current_context || ContextualizedLogs.config.current_context
|
|
16
15
|
end
|
|
17
16
|
|
|
18
|
-
def
|
|
19
|
-
@
|
|
17
|
+
def default_contextualize_model
|
|
18
|
+
@default_contextualize_model || ContextualizedLogs.config.worker_default_contextualize_model
|
|
20
19
|
end
|
|
21
20
|
|
|
22
|
-
def
|
|
23
|
-
@
|
|
21
|
+
def default_contextualize_worker
|
|
22
|
+
@default_contextualize_worker || ContextualizedLogs.config.worker_default_contextualize_worker
|
|
24
23
|
end
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
def included(base)
|
|
26
|
+
unless base.ancestors.include? ::Sidekiq::Worker
|
|
27
|
+
raise ArgumentError, "ContextualizedLogs::ContextualizedWorker can only be included in a Sidekiq::Worker"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
base.extend(ClassMethods)
|
|
31
|
+
|
|
32
|
+
# Automatically add sidekiq middleware when we're first included
|
|
33
|
+
#
|
|
34
|
+
# This might only occur when the worker class is first loaded in a
|
|
35
|
+
# development rails environment, but that happens before the middleware
|
|
36
|
+
# chain is invoked so we're all good.
|
|
37
|
+
#
|
|
38
|
+
::Sidekiq.configure_server do |config|
|
|
39
|
+
unless config.server_middleware.exists? Sidekiq::Middleware::Server::RestoreCurrentContext
|
|
40
|
+
config.server_middleware.add Sidekiq::Middleware::Server::RestoreCurrentContext
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
::Sidekiq.configure_client do |config|
|
|
44
|
+
unless config.client_middleware.exists? Sidekiq::Middleware::Client::InjectCurrentContext
|
|
45
|
+
config.client_middleware.add Sidekiq::Middleware::Client::InjectCurrentContext
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
27
50
|
|
|
28
|
-
|
|
29
|
-
|
|
51
|
+
module ClassMethods
|
|
52
|
+
def contextualize_worker_enabled
|
|
53
|
+
return @contextualize_worker_enabled if defined?(@contextualize_worker_enabled)
|
|
54
|
+
|
|
55
|
+
ContextualizedWorker.default_contextualize_worker
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def contextualize_model_enabled
|
|
59
|
+
return @contextualize_model_enabled if defined?(@contextualize_model_enabled)
|
|
60
|
+
|
|
61
|
+
ContextualizedWorker.default_contextualize_model
|
|
30
62
|
end
|
|
31
63
|
|
|
32
|
-
|
|
33
|
-
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def contextualize_worker(enabled)
|
|
67
|
+
@contextualize_worker_enabled = enabled
|
|
34
68
|
end
|
|
35
69
|
|
|
36
|
-
def
|
|
37
|
-
@
|
|
70
|
+
def contextualize_model(enabled)
|
|
71
|
+
@contextualize_model_enabled = enabled
|
|
38
72
|
end
|
|
39
73
|
end
|
|
40
74
|
end
|
|
@@ -6,94 +6,96 @@ module ContextualizedLogs
|
|
|
6
6
|
class CurrentContext < ActiveSupport::CurrentAttributes
|
|
7
7
|
# ⚠️ do not use this class to store any controller specific info..
|
|
8
8
|
|
|
9
|
-
attribute
|
|
9
|
+
attribute :custom_attributes,
|
|
10
10
|
:request_uuid, :request_user_agent, :request_origin, :request_referer, :request_xhr, # request
|
|
11
11
|
:current_job_id, :enqueued_jobs_ids, :worker, :worker_args, # sidekiq
|
|
12
12
|
:request_remote_ip, :request_ip, :request_remote_addr, :request_x_forwarded_for, # ips
|
|
13
13
|
:errors,
|
|
14
|
-
:
|
|
14
|
+
:contextualize_model_enabled, # enable model context values
|
|
15
15
|
:context_values, :context_values_count, # context values
|
|
16
16
|
:resource_name # controller_action to correlate APM metrics
|
|
17
17
|
|
|
18
18
|
MAX_CONTEXT_VALUES = 100
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
20
|
+
class << self
|
|
21
|
+
def context
|
|
22
|
+
# https://docs.hq.com/logs/processing/attributes_naming_convention/#source-code
|
|
23
|
+
|
|
24
|
+
data = {}
|
|
25
|
+
|
|
26
|
+
data[:resource_name] = resource_name unless resource_name.nil?
|
|
27
|
+
|
|
28
|
+
# normalized
|
|
29
|
+
data[:http] = data[:http] || {}
|
|
30
|
+
data[:http][:referer] = request_referer unless request_referer.nil?
|
|
31
|
+
data[:http][:request_id] = request_uuid unless request_uuid.nil?
|
|
32
|
+
data[:http][:useragent] = request_user_agent unless request_user_agent.nil?
|
|
33
|
+
data[:http][:origin] = request_origin unless request_origin.nil?
|
|
34
|
+
data.delete(:http) if data[:http].empty?
|
|
35
|
+
|
|
36
|
+
# normalized
|
|
37
|
+
data[:network] = data[:network] || { client: {} }
|
|
38
|
+
data[:network][:client][:ip] = request_ip unless request_ip.nil?
|
|
39
|
+
data[:network][:client][:remote_addr] = request_remote_addr unless request_remote_addr.nil?
|
|
40
|
+
data[:network][:client][:remote_ip] = request_remote_ip unless request_remote_ip.nil?
|
|
41
|
+
data[:network][:client][:x_forwarded_for] = request_x_forwarded_for unless request_x_forwarded_for.nil?
|
|
42
|
+
data.delete(:network) if data[:network][:client].empty?
|
|
43
|
+
|
|
44
|
+
# eventual error response
|
|
45
|
+
# normalized
|
|
46
|
+
data[:errors] = errors unless errors.nil?
|
|
47
|
+
|
|
48
|
+
# context_values
|
|
49
|
+
unless context_values.nil?
|
|
50
|
+
if context_values.is_a?(Hash) && !context_values.empty?
|
|
51
|
+
data[:context_values] = {}
|
|
52
|
+
context_values.each { |k, v| data[:context_values][k.to_sym] = v }
|
|
53
|
+
end
|
|
52
54
|
end
|
|
53
|
-
end
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
unless current_job_id.nil? && worker.nil?
|
|
57
|
+
data[:job] = { id: current_job_id, worker: worker }
|
|
58
|
+
data[:job][:args] = worker_args if worker_args
|
|
59
|
+
end
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
data.deep_merge(custom_attributes || {})
|
|
62
|
+
end
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
def to_json
|
|
65
|
+
attributes.to_json
|
|
66
|
+
end
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
def add_context(key, value)
|
|
69
|
+
self.context_values_count ||= 0
|
|
70
|
+
self.context_values_count += 1
|
|
71
|
+
if self.context_values_count >= MAX_CONTEXT_VALUES
|
|
72
|
+
Rails.logger.warn('high number of context values') if self.context_values_count == MAX_CONTEXT_VALUES
|
|
73
|
+
return
|
|
73
74
|
|
|
75
|
+
end
|
|
76
|
+
self.context_values ||= {}
|
|
77
|
+
self.context_values[key] ||= []
|
|
78
|
+
unless self.context_values[key].include?(value)
|
|
79
|
+
self.context_values[key] << value
|
|
80
|
+
end
|
|
74
81
|
end
|
|
75
|
-
self.context_values ||= {}
|
|
76
|
-
self.context_values[key] ||= []
|
|
77
|
-
unless self.context_values[key].include?(value)
|
|
78
|
-
self.context_values[key] << value
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
def from_json(json)
|
|
84
|
+
return unless json
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
begin
|
|
87
|
+
values = JSON.parse(json).deep_symbolize_keys
|
|
88
|
+
values.each { |k, v| send("#{k}=", v) }
|
|
89
|
+
rescue
|
|
90
|
+
end
|
|
89
91
|
end
|
|
90
|
-
end
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
def add_error(error)
|
|
94
|
+
return if error.nil?
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
self.errors ||= []
|
|
97
|
+
self.errors << error
|
|
98
|
+
end
|
|
97
99
|
end
|
|
98
100
|
end
|
|
99
101
|
end
|
|
@@ -19,11 +19,11 @@ module ContextualizedLogs
|
|
|
19
19
|
# no need to `Current.reset`
|
|
20
20
|
worker_klass = worker_class.is_a?(String) ? worker_class.constantize : worker_class
|
|
21
21
|
if worker_klass.include?(ContextualizedWorker)
|
|
22
|
-
current_context =
|
|
22
|
+
current_context = ContextualizedWorker.current_context
|
|
23
23
|
current_context.enqueued_jobs_ids ||= []
|
|
24
24
|
current_context.enqueued_jobs_ids << job['jid']
|
|
25
|
-
current_context.
|
|
26
|
-
if worker_klass.
|
|
25
|
+
current_context.contextualize_model_enabled = worker_klass.contextualize_model_enabled
|
|
26
|
+
if worker_klass.contextualize_worker_enabled
|
|
27
27
|
job['context'] = current_context.to_json
|
|
28
28
|
Rails.logger.info "sidekiq: enqueing job #{worker_class}: #{job['jid']}, on queue: #{queue}"
|
|
29
29
|
Rails.logger.dump('Injecting context', JSON.parse(current_context.to_json), :debug)
|
|
@@ -15,14 +15,14 @@ module ContextualizedLogs
|
|
|
15
15
|
worker_klass = worker.class
|
|
16
16
|
if worker_klass.include?(ContextualizedWorker)
|
|
17
17
|
job_context_json = job['context']
|
|
18
|
-
current_context =
|
|
18
|
+
current_context = ContextualizedWorker.current_context
|
|
19
19
|
current_context.from_json(job_context_json) if job_context_json
|
|
20
20
|
current_context.current_job_id = job['jid']
|
|
21
21
|
current_context.worker = worker.class.to_s
|
|
22
22
|
# https://github.com/mperham/sidekiq/wiki/Job-Format
|
|
23
23
|
current_context.worker_args = worker_klass.contextualize_args(job['args']) if worker_klass.respond_to?(:contextualize_args) && job['args']
|
|
24
|
-
current_context.
|
|
25
|
-
if worker_klass.
|
|
24
|
+
current_context.contextualize_model_enabled = worker_klass.contextualize_model_enabled
|
|
25
|
+
if worker_klass.contextualize_worker_enabled
|
|
26
26
|
Rails.logger.info "sidekiq: performing job #{worker_klass}: #{job['jid']}, on queue #{queue}"
|
|
27
27
|
yield
|
|
28
28
|
Rails.logger.info "sidekiq: completing job #{worker_klass}: #{job['jid']}, on queue #{queue}"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: contextualized_logs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2.pre.alpha
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hugues Bernet-Rollande
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-04-
|
|
11
|
+
date: 2020-04-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
Online logging solution (like [Datadog](https://www.datadoghq.com)) have drastically transform the way we log.
|
|
@@ -44,6 +44,8 @@ files:
|
|
|
44
44
|
- app/channels/application_cable/channel.rb
|
|
45
45
|
- app/channels/application_cable/connection.rb
|
|
46
46
|
- app/controllers/application_controller.rb
|
|
47
|
+
- app/controllers/concerns/datadog_set_trace_details.rb
|
|
48
|
+
- app/controllers/custom_context_controller.rb
|
|
47
49
|
- app/controllers/model_controller.rb
|
|
48
50
|
- app/helpers/application_helper.rb
|
|
49
51
|
- app/jobs/application_job.rb
|
|
@@ -67,7 +69,9 @@ files:
|
|
|
67
69
|
- config/initializers/assets.rb
|
|
68
70
|
- config/initializers/backtrace_silencers.rb
|
|
69
71
|
- config/initializers/content_security_policy.rb
|
|
72
|
+
- config/initializers/contextualized_logs.rb
|
|
70
73
|
- config/initializers/cookies_serializer.rb
|
|
74
|
+
- config/initializers/datadog.rb
|
|
71
75
|
- config/initializers/filter_parameter_logging.rb
|
|
72
76
|
- config/initializers/inflections.rb
|
|
73
77
|
- config/initializers/mime_types.rb
|
|
@@ -85,6 +89,7 @@ files:
|
|
|
85
89
|
- db/seeds.rb
|
|
86
90
|
- db/test.sqlite3
|
|
87
91
|
- lib/contextualized_logs.rb
|
|
92
|
+
- lib/contextualized_logs/config.rb
|
|
88
93
|
- lib/contextualized_logs/contextualized_controller.rb
|
|
89
94
|
- lib/contextualized_logs/contextualized_logger.rb
|
|
90
95
|
- lib/contextualized_logs/contextualized_model.rb
|