contextualized_logs 0.0.1.pre.alpha → 0.0.2.pre.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|