contextual_logger 1.2.0.colin.4 → 1.2.0.colin.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b8180f031cfc4c1facc22a0cfbbadf8970b01a1b7c1d18f974dbf54fd1712d5
4
- data.tar.gz: d310df13569303aa5f6964d1cf9f73583fcfcabcd5950f330b00a30f8eb151f5
3
+ metadata.gz: c4b2dc21d1f78b3b6a4a70a782e717eb607ace39fbb085f68fe5c5fee1086e9e
4
+ data.tar.gz: 17422f6d289ab064d5b212b057870da7c6e283cf24fcf3d419874caf2621b923
5
5
  SHA512:
6
- metadata.gz: e0c0350c9f36cf27071a7e1da1f42363979852304c4b029ca24d0a550c77bfb403815dc09420d5c8d9c164b003f8a22213e5d33216e1647ae63143a3b73447e9
7
- data.tar.gz: 4984642ce8b36e78c3f14f0415ac95d67dec97f9f5651f6c63deb715bbfa27134243f7286e09802146b6629df0bc1077c476ea3e3d5ad783779daa718aef032d
6
+ metadata.gz: 15ff319daf3bcd7c4fd6a9c815cdfc394af288749c52c583fb522d571915fb1fe4bc888a9db8ff0040e4d509a37c9027610aa41a3fa59bcec46dfb551045bd81
7
+ data.tar.gz: 4a7ebd9b1b4f37a950d8cbea6b87ad797264701b87e349246c1bce4edfb0743ed3557c827545665a8c84a37c43c4062aeefced0b76880e558084278093bc26fb
@@ -2,17 +2,20 @@
2
2
 
3
3
  module ContextualLogger
4
4
  module Context
5
- def thread_context_for_logger_instance
5
+ EMPTY_CONTEXT = {}.freeze
6
+
7
+ def thread_context_key_for_logger_instance
6
8
  # We include the object_id here to make these thread/fiber locals unique per logger instance.
7
- @thread_context_for_logger_instance ||= "ContextualLogger::Context.context_for_#{object_id}".to_sym
9
+ @thread_context_key_for_logger_instance ||= "ContextualLogger::Context.context_for_#{object_id}".to_sym
8
10
  end
9
11
 
10
12
  def current_context_override
11
- Thread.current[thread_context_for_logger_instance]
13
+ ContextualLogger.global_context_lock_message ||= "ContextualLogger::Context.current_context_override set for #{self.class.name} #{object_id}"
14
+ Thread.current[thread_context_key_for_logger_instance]
12
15
  end
13
16
 
14
17
  def current_context_override=(context_override)
15
- Thread.current[thread_context_for_logger_instance] = context_override.freeze
18
+ Thread.current[thread_context_key_for_logger_instance] = context_override.freeze
16
19
  end
17
20
  end
18
21
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ContextualLogger
4
+ class << self
5
+ attr_accessor :global_context_lock_message # nil or a string indicating what locked the global context
6
+ end
7
+
8
+
9
+ class GlobalContextIsLocked < StandardError
10
+ end
11
+ end
@@ -6,6 +6,13 @@ module ContextualLogger
6
6
  # A logger that deep_merges additional context and then delegates to the given logger.
7
7
  # Keeps it own log level (called override_level) that may be set independently of the logger it delegates to.
8
8
  # If override_level is non-nil, it takes precedence; if it is nil (the default), then it delegates to the logger.
9
+ #
10
+ # Context Precedence:
11
+ # 1. inline **context passed to the logger method
12
+ # 2. `with_context` overrides on this LoggerWithContext object
13
+ # 3. context passed to this LoggerWithContext constructor
14
+ # 4. `with_context` overrides on the logger passed to this constructor
15
+ # 5. `global_context` set on the logger passed to this constructor
9
16
  class LoggerWithContext
10
17
  include LoggerMixin
11
18
 
@@ -16,11 +23,13 @@ module ContextualLogger
16
23
  @logger = logger
17
24
  self.level = level
18
25
  @context = normalize_context(context)
19
- @merged_context_cache = {} # so we don't have to merge every time
20
26
  end
21
27
 
28
+ # TODO: It's a (small) bug that the global_context is memoized at this point. There's a chance that the @logger.current_context
29
+ # changes after this because of an enclosing @logger.with_context block. If that happens, we'll miss that extra context.
30
+ # The tradeoff is that we don't want to keep calling deep_merge.
22
31
  def global_context
23
- @logger.global_context
32
+ @global_context ||= @logger.current_context.deep_merge(@context) # this will include any with_context overrides on the `logger`
24
33
  end
25
34
 
26
35
  def level
@@ -33,10 +42,10 @@ module ContextualLogger
33
42
 
34
43
  def write_entry_to_log(severity, timestamp, progname, message, context:)
35
44
  merged_context =
36
- if @merged_context_cache.size >= 1000 # keep this cache memory use finite
37
- @merged_context_cache[context] || @context.deep_merge(context)
45
+ if context.any?
46
+ current_context.deep_merge(context)
38
47
  else
39
- @merged_context_cache[context] ||= @context.deep_merge(context)
48
+ current_context
40
49
  end
41
50
 
42
51
  @logger.write_entry_to_log(severity, timestamp, progname, message, context: merged_context)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ContextualLogger
4
- VERSION = '1.2.0.colin.4'
4
+ VERSION = '1.2.0.colin.5'
5
5
  end
@@ -6,6 +6,7 @@ require 'json'
6
6
  require_relative './contextual_logger/redactor'
7
7
  require_relative './contextual_logger/context'
8
8
  require_relative './contextual_logger/context_handler'
9
+ require_relative './contextual_logger/global_context_lock_message'
9
10
 
10
11
  module ContextualLogger
11
12
  LOG_LEVEL_NAMES_TO_SEVERITY =
@@ -43,16 +44,23 @@ module ContextualLogger
43
44
  end
44
45
  end
45
46
 
47
+ # Context Precedence when this is mixed into a logger:
48
+ # 1. inline **context passed to the logger method
49
+ # 2. `with_context` overrides on the logger object
50
+ # 3. `global_context` set on the logger passed to this constructor
46
51
  module LoggerMixin
47
52
  include Context
48
53
 
49
54
  delegate :register_secret, :register_secret_regex, to: :redactor
50
55
 
51
56
  def global_context
52
- @global_context ||= {}.freeze
57
+ @global_context ||= Context::EMPTY_CONTEXT
53
58
  end
54
59
 
55
60
  def global_context=(context)
61
+ if (global_context_lock_message = ::ContextualLogger.global_context_lock_message)
62
+ raise ::ContextualLogger::GlobalContextIsLocked, global_context_lock_message
63
+ end
56
64
  @global_context = context.freeze
57
65
  end
58
66
 
@@ -63,18 +71,19 @@ module ContextualLogger
63
71
  # TODO: Deprecate current_context_for_thread in v2.0.
64
72
  alias current_context_for_thread current_context
65
73
 
66
- def with_context(context)
67
- previous_context_override = current_context_override
68
- self.current_context_override = current_context.deep_merge(context)
74
+ def with_context(stacked_context)
75
+ context_handler = ContextHandler.new(self, self.current_context_override)
76
+ self.current_context_override = deep_merge_with_current_context(stacked_context)
77
+
69
78
  if block_given?
70
79
  begin
71
80
  yield
72
81
  ensure
73
- self.current_context_override = previous_context_override
82
+ context_handler.reset!
74
83
  end
75
84
  else
76
85
  # If no block given, return context handler to the caller so they can call reset! themselves.
77
- ContextHandler.new(self, previous_context_override)
86
+ context_handler
78
87
  end
79
88
  end
80
89
 
@@ -112,7 +121,7 @@ module ContextualLogger
112
121
 
113
122
  # Note that this interface needs to stay compatible with the underlying ::Logger#add interface,
114
123
  # which is: def add(severity, message = nil, progname = nil)
115
- def add(arg_severity, arg1 = nil, arg2 = nil, **context) # Ruby will prefer to match hashes up to last ** argument
124
+ def add(arg_severity, arg1 = nil, arg2 = nil, **context) # Ruby will prefer to match hashes to last argument because of **
116
125
  severity = arg_severity || UNKNOWN
117
126
  if log_level_enabled?(severity)
118
127
  if arg1.nil?
@@ -127,7 +136,7 @@ module ContextualLogger
127
136
  message = arg1
128
137
  progname = arg2 || @progname
129
138
  end
130
- write_entry_to_log(severity, Time.now, progname, message, context: current_context.deep_merge(context))
139
+ write_entry_to_log(severity, Time.now, progname, message, context: deep_merge_with_current_context(context))
131
140
  end
132
141
 
133
142
  true
@@ -151,7 +160,7 @@ module ContextualLogger
151
160
  normalized_message = ContextualLogger.normalize_message(message)
152
161
  normalized_progname = ContextualLogger.normalize_message(progname) unless progname.nil?
153
162
  if @formatter
154
- @formatter.call(severity, timestamp, normalized_progname, { message: normalized_message }.merge!(context))
163
+ @formatter.call(severity, timestamp, normalized_progname, { message: normalized_message, **context })
155
164
  else
156
165
  "#{basic_json_log_entry(severity, timestamp, normalized_progname, normalized_message, context: context)}\n"
157
166
  end
@@ -161,12 +170,20 @@ module ContextualLogger
161
170
  message_hash = {
162
171
  message: normalized_progname ? "#{normalized_progname}: #{normalized_message}" : normalized_message,
163
172
  severity: severity,
164
- timestamp: timestamp
173
+ timestamp: timestamp,
174
+ **context
165
175
  }
166
176
  message_hash[:progname] = normalized_progname if normalized_progname
167
177
 
168
- # merge! is faster and OK here since message_hash is still local only to this method
169
- message_hash.merge!(context).to_json
178
+ message_hash.to_json
179
+ end
180
+
181
+ def deep_merge_with_current_context(stacked_context)
182
+ if stacked_context.any?
183
+ current_context.deep_merge(stacked_context)
184
+ else
185
+ current_context
186
+ end
170
187
  end
171
188
  end
172
189
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contextual_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0.colin.4
4
+ version: 1.2.0.colin.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Ebentier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-02 00:00:00.000000000 Z
11
+ date: 2023-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -47,6 +47,7 @@ files:
47
47
  - lib/contextual_logger.rb
48
48
  - lib/contextual_logger/context.rb
49
49
  - lib/contextual_logger/context_handler.rb
50
+ - lib/contextual_logger/global_context_lock_message.rb
50
51
  - lib/contextual_logger/logger_with_context.rb
51
52
  - lib/contextual_logger/overrides/active_support/tagged_logging/formatter.rb
52
53
  - lib/contextual_logger/redactor.rb