contextual_logger 1.2.0.pre.1 → 1.2.0
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/lib/contextual_logger/context.rb +21 -0
- data/lib/contextual_logger/context_handler.rb +14 -0
- data/lib/contextual_logger/global_context_lock_message.rb +10 -0
- data/lib/contextual_logger/logger_with_context.rb +17 -4
- data/lib/contextual_logger/version.rb +1 -1
- data/lib/contextual_logger.rb +43 -16
- metadata +7 -5
- data/lib/contextual_logger/context/handler.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1345e9a0914c195f20d4cb22012452faff2bde5c9a869ee41db6a5c80ac70b03
|
4
|
+
data.tar.gz: 9614e6145e032712d0e8ebf64b1266ed7872200be70a782f5b1a9ed54edeeb1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec61e408108a4485223e9c4d07029957e345f39d689d797db26263c37bfe5264661f11909ac808e2e0102176b172a7f2867b1d3bec624e58f3a927c6f53996e3
|
7
|
+
data.tar.gz: 28976f222764d2323103690a8e76896b85914f59f594c0264554fd502832dfedc4fdeb2fac988baa98f6998dedadbb283e2da3ff4a647dd0341446d7b0cc99c4
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ContextualLogger
|
4
|
+
module Context
|
5
|
+
EMPTY_CONTEXT = {}.freeze
|
6
|
+
|
7
|
+
def thread_context_key_for_logger_instance
|
8
|
+
# We include the object_id here to make these thread/fiber locals unique per logger instance.
|
9
|
+
@thread_context_key_for_logger_instance ||= "ContextualLogger::Context.context_for_#{object_id}".to_sym
|
10
|
+
end
|
11
|
+
|
12
|
+
def current_context_override
|
13
|
+
Thread.current[thread_context_key_for_logger_instance]
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_context_override=(context_override)
|
17
|
+
ContextualLogger.global_context_lock_message ||= "ContextualLogger::Context.current_context_override set for #{self.class.name} #{object_id}: #{context_override.inspect}"
|
18
|
+
Thread.current[thread_context_key_for_logger_instance] = context_override.freeze
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ContextualLogger
|
4
|
+
class ContextHandler
|
5
|
+
def initialize(instance, previous_context_override)
|
6
|
+
@instance = instance
|
7
|
+
@previous_context_override = previous_context_override
|
8
|
+
end
|
9
|
+
|
10
|
+
def reset!
|
11
|
+
@instance.current_context_override = @previous_context_override
|
12
|
+
end
|
13
|
+
end
|
14
|
+
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,7 +23,13 @@ module ContextualLogger
|
|
16
23
|
@logger = logger
|
17
24
|
self.level = level
|
18
25
|
@context = normalize_context(context)
|
19
|
-
|
26
|
+
end
|
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.
|
31
|
+
def global_context
|
32
|
+
@global_context ||= @logger.current_context.deep_merge(@context) # this will include any with_context overrides on the `logger`
|
20
33
|
end
|
21
34
|
|
22
35
|
def level
|
@@ -29,10 +42,10 @@ module ContextualLogger
|
|
29
42
|
|
30
43
|
def write_entry_to_log(severity, timestamp, progname, message, context:)
|
31
44
|
merged_context =
|
32
|
-
if
|
33
|
-
|
45
|
+
if context.any?
|
46
|
+
current_context.deep_merge(context)
|
34
47
|
else
|
35
|
-
|
48
|
+
current_context
|
36
49
|
end
|
37
50
|
|
38
51
|
@logger.write_entry_to_log(severity, timestamp, progname, message, context: merged_context)
|
data/lib/contextual_logger.rb
CHANGED
@@ -4,7 +4,9 @@ require 'active_support'
|
|
4
4
|
require 'active_support/core_ext/module/delegation'
|
5
5
|
require 'json'
|
6
6
|
require_relative './contextual_logger/redactor'
|
7
|
-
require_relative './contextual_logger/context
|
7
|
+
require_relative './contextual_logger/context'
|
8
|
+
require_relative './contextual_logger/context_handler'
|
9
|
+
require_relative './contextual_logger/global_context_lock_message'
|
8
10
|
|
9
11
|
module ContextualLogger
|
10
12
|
LOG_LEVEL_NAMES_TO_SEVERITY =
|
@@ -42,16 +44,37 @@ module ContextualLogger
|
|
42
44
|
end
|
43
45
|
end
|
44
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
|
45
51
|
module LoggerMixin
|
52
|
+
include Context
|
53
|
+
|
46
54
|
delegate :register_secret, :register_secret_regex, to: :redactor
|
47
55
|
|
56
|
+
def global_context
|
57
|
+
@global_context ||= Context::EMPTY_CONTEXT
|
58
|
+
end
|
59
|
+
|
48
60
|
def global_context=(context)
|
49
|
-
|
61
|
+
if (global_context_lock_message = ::ContextualLogger.global_context_lock_message)
|
62
|
+
raise ::ContextualLogger::GlobalContextIsLocked, global_context_lock_message
|
63
|
+
end
|
64
|
+
@global_context = context.freeze
|
50
65
|
end
|
51
66
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
67
|
+
def current_context
|
68
|
+
current_context_override || global_context
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO: Deprecate current_context_for_thread in v2.0.
|
72
|
+
alias current_context_for_thread current_context
|
73
|
+
|
74
|
+
def with_context(stacked_context)
|
75
|
+
context_handler = ContextHandler.new(self, current_context_override)
|
76
|
+
self.current_context_override = deep_merge_with_current_context(stacked_context)
|
77
|
+
|
55
78
|
if block_given?
|
56
79
|
begin
|
57
80
|
yield
|
@@ -59,15 +82,11 @@ module ContextualLogger
|
|
59
82
|
context_handler.reset!
|
60
83
|
end
|
61
84
|
else
|
62
|
-
# If no block given,
|
85
|
+
# If no block given, return context handler to the caller so they can call reset! themselves.
|
63
86
|
context_handler
|
64
87
|
end
|
65
88
|
end
|
66
89
|
|
67
|
-
def current_context_for_thread
|
68
|
-
Context::Handler.current_context
|
69
|
-
end
|
70
|
-
|
71
90
|
# In the methods generated below, we assume that presence of context means new code that is
|
72
91
|
# aware of ContextualLogger...and that that code never uses progname.
|
73
92
|
# This is important because we only get 3 args total (not including &block) passed to `add`,
|
@@ -102,7 +121,7 @@ module ContextualLogger
|
|
102
121
|
|
103
122
|
# Note that this interface needs to stay compatible with the underlying ::Logger#add interface,
|
104
123
|
# which is: def add(severity, message = nil, progname = nil)
|
105
|
-
def add(arg_severity, arg1 = nil, arg2 = nil, **context) # Ruby will prefer to match hashes
|
124
|
+
def add(arg_severity, arg1 = nil, arg2 = nil, **context) # Ruby will prefer to match hashes to last argument because of **
|
106
125
|
severity = arg_severity || UNKNOWN
|
107
126
|
if log_level_enabled?(severity)
|
108
127
|
if arg1.nil?
|
@@ -117,7 +136,7 @@ module ContextualLogger
|
|
117
136
|
message = arg1
|
118
137
|
progname = arg2 || @progname
|
119
138
|
end
|
120
|
-
write_entry_to_log(severity, Time.now, progname, message, context:
|
139
|
+
write_entry_to_log(severity, Time.now, progname, message, context: deep_merge_with_current_context(context))
|
121
140
|
end
|
122
141
|
|
123
142
|
true
|
@@ -141,7 +160,7 @@ module ContextualLogger
|
|
141
160
|
normalized_message = ContextualLogger.normalize_message(message)
|
142
161
|
normalized_progname = ContextualLogger.normalize_message(progname) unless progname.nil?
|
143
162
|
if @formatter
|
144
|
-
@formatter.call(severity, timestamp, normalized_progname, { message: normalized_message }
|
163
|
+
@formatter.call(severity, timestamp, normalized_progname, { message: normalized_message, **context })
|
145
164
|
else
|
146
165
|
"#{basic_json_log_entry(severity, timestamp, normalized_progname, normalized_message, context: context)}\n"
|
147
166
|
end
|
@@ -151,12 +170,20 @@ module ContextualLogger
|
|
151
170
|
message_hash = {
|
152
171
|
message: normalized_progname ? "#{normalized_progname}: #{normalized_message}" : normalized_message,
|
153
172
|
severity: severity,
|
154
|
-
timestamp: timestamp
|
173
|
+
timestamp: timestamp,
|
174
|
+
**context
|
155
175
|
}
|
156
176
|
message_hash[:progname] = normalized_progname if normalized_progname
|
157
177
|
|
158
|
-
|
159
|
-
|
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
|
160
187
|
end
|
161
188
|
end
|
162
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
|
4
|
+
version: 1.2.0
|
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-
|
11
|
+
date: 2023-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -45,7 +45,9 @@ extensions: []
|
|
45
45
|
extra_rdoc_files: []
|
46
46
|
files:
|
47
47
|
- lib/contextual_logger.rb
|
48
|
-
- lib/contextual_logger/context
|
48
|
+
- lib/contextual_logger/context.rb
|
49
|
+
- lib/contextual_logger/context_handler.rb
|
50
|
+
- lib/contextual_logger/global_context_lock_message.rb
|
49
51
|
- lib/contextual_logger/logger_with_context.rb
|
50
52
|
- lib/contextual_logger/overrides/active_support/tagged_logging/formatter.rb
|
51
53
|
- lib/contextual_logger/redactor.rb
|
@@ -67,9 +69,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
69
|
version: '0'
|
68
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
71
|
requirements:
|
70
|
-
- - "
|
72
|
+
- - ">="
|
71
73
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
74
|
+
version: '0'
|
73
75
|
requirements: []
|
74
76
|
rubygems_version: 3.1.6
|
75
77
|
signing_key:
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ContextualLogger
|
4
|
-
module Context
|
5
|
-
class Handler
|
6
|
-
THREAD_CONTEXT_NAMESPACE = 'ContextualLoggerCurrentLoggingContext'
|
7
|
-
|
8
|
-
attr_reader :previous_context, :context
|
9
|
-
|
10
|
-
def self.current_context
|
11
|
-
Thread.current[THREAD_CONTEXT_NAMESPACE] || {}
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize(context, previous_context: nil)
|
15
|
-
@previous_context = previous_context || self.class.current_context
|
16
|
-
@context = context
|
17
|
-
end
|
18
|
-
|
19
|
-
def set!
|
20
|
-
Thread.current[THREAD_CONTEXT_NAMESPACE] = context
|
21
|
-
end
|
22
|
-
|
23
|
-
def reset!
|
24
|
-
Thread.current[THREAD_CONTEXT_NAMESPACE] = previous_context
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|