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.
@@ -1,37 +1,43 @@
1
1
  require 'active_support'
2
- require_relative 'current_context'
2
+ require 'active_record'
3
3
 
4
4
  module ContextualizedLogs
5
5
  module ContextualizedModel
6
6
  extend ActiveSupport::Concern
7
7
 
8
- DEFAULT_CURRENT_CONTEXT = ContextualizedLogs::CurrentContext
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 || DEFAULT_CURRENT_CONTEXT
12
+ @current_context || ContextualizedLogs.config.current_context
15
13
  end
16
14
 
17
- private
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
- def contextualizable(keys: {})
20
- @contextualizable_keys = keys
21
- end
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
- def set_current_context(current_context)
24
- @current_context = current_context
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.contextualized_model_enabled: #{context.contextualized_model_enabled}"
34
- return unless context.contextualized_model_enabled
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
- included do
43
- after_find do |object|
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
- after_create do
49
- # Rails.logger.debug "after_create #{self}"
50
- ContextualizedModel.contextualize(self, self.class.contextualizable_keys, self.class.current_context)
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
- DEFAULT_CURRENT_CONTEXT = ContextualizedLogs::CurrentContext
8
- DEFAULT_CONTEXTUALIZED_WORKER_ENABLED = false
9
- DEFAULT_CONTEXTUALIZED_MODEL_ENABLED = false
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 || DEFAULT_CURRENT_CONTEXT
14
+ @current_context || ContextualizedLogs.config.current_context
16
15
  end
17
16
 
18
- def contextualized_worker_enabled
19
- @contextualized_worker_enabled || DEFAULT_CONTEXTUALIZED_WORKER_ENABLED
17
+ def default_contextualize_model
18
+ @default_contextualize_model || ContextualizedLogs.config.worker_default_contextualize_model
20
19
  end
21
20
 
22
- def contextualized_model_enabled
23
- @contextualized_model_enabled || DEFAULT_CONTEXTUALIZED_MODEL_ENABLED
21
+ def default_contextualize_worker
22
+ @default_contextualize_worker || ContextualizedLogs.config.worker_default_contextualize_worker
24
23
  end
25
24
 
26
- private
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
- def set_current_context(current_context)
29
- @current_context = current_context
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
- def contextualized_worker(enabled)
33
- @contextualized_worker_enabled = enabled
64
+ private
65
+
66
+ def contextualize_worker(enabled)
67
+ @contextualize_worker_enabled = enabled
34
68
  end
35
69
 
36
- def contextualized_model(enabled)
37
- @contextualized_model_enabled = enabled
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
- :contextualized_model_enabled, # enable model context values
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
- def self.context
21
- # https://docs.hq.com/logs/processing/attributes_naming_convention/#source-code
22
-
23
- data = {}
24
-
25
- data[:resource_name] = resource_name unless resource_name.nil?
26
-
27
- # normalized
28
- data[:http] = {}
29
- data[:http][:referer] = request_referer unless request_referer.nil?
30
- data[:http][:request_id] = request_uuid unless request_uuid.nil?
31
- data[:http][:useragent] = request_user_agent unless request_user_agent.nil?
32
- data[:http][:origin] = request_origin unless request_origin.nil?
33
- data.delete(:http) if data[:http].empty?
34
-
35
- # normalized
36
- data[:network] = { client: {} }
37
- data[:network][:client][:ip] = request_ip unless request_ip.nil?
38
- data[:network][:client][:remote_addr] = request_remote_addr unless request_remote_addr.nil?
39
- data[:network][:client][:remote_ip] = request_remote_ip unless request_remote_ip.nil?
40
- data[:network][:client][:x_forwarded_for] = request_x_forwarded_for unless request_x_forwarded_for.nil?
41
- data.delete(:network) if data[:network][:client].empty?
42
-
43
- # eventual error response
44
- # normalized
45
- data[:errors] = errors unless errors.nil?
46
-
47
- # context_values
48
- unless context_values.nil?
49
- if context_values.is_a?(Hash) && !context_values.empty?
50
- data[:context_values] = {}
51
- context_values.each { |k, v| data[:context_values][k.to_sym] = v }
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
- unless current_job_id.nil? && worker.nil?
56
- data[:job] = { id: current_job_id, worker: worker }
57
- data[:job][:args] = worker_args if worker_args
58
- end
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
- data
61
- end
61
+ data.deep_merge(custom_attributes || {})
62
+ end
62
63
 
63
- def self.to_json
64
- attributes.to_json
65
- end
64
+ def to_json
65
+ attributes.to_json
66
+ end
66
67
 
67
- def self.add_context(key, value)
68
- self.context_values_count ||= 0
69
- self.context_values_count += 1
70
- if self.context_values_count >= MAX_CONTEXT_VALUES
71
- Rails.logger.warn('high number of context values') if self.context_values_count == MAX_CONTEXT_VALUES
72
- return
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
- def self.from_json(json)
83
- return unless json
83
+ def from_json(json)
84
+ return unless json
84
85
 
85
- begin
86
- values = JSON.parse(json).deep_symbolize_keys
87
- values.each { |k, v| send("#{k}=", v) }
88
- rescue
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
- def self.add_error(error)
93
- return if error.nil?
93
+ def add_error(error)
94
+ return if error.nil?
94
95
 
95
- self.errors ||= []
96
- self.errors << error
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 = worker_klass.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.contextualized_model_enabled = worker_klass.contextualized_model_enabled
26
- if worker_klass.contextualized_worker_enabled
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 = worker_klass.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.contextualized_model_enabled = worker_klass.contextualized_model_enabled
25
- if worker_klass.contextualized_worker_enabled
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}"
@@ -1,3 +1,3 @@
1
1
  module ContextualizedLogs
2
- VERSION = '0.0.1-alpha'
2
+ VERSION = '0.0.2-alpha'
3
3
  end
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.1.pre.alpha
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-24 00:00:00.000000000 Z
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