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.
@@ -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