Module: Logging::MappedDiagnosticContext
- Extended by:
- MappedDiagnosticContext
- Included in:
- MappedDiagnosticContext
- Defined in:
- lib/logging/diagnostic_context.rb
Overview
A Mapped Diagnostic Context, or MDC in short, is an instrument used to distinguish interleaved log output from different sources. Log output is typically interleaved when a server handles multiple clients near-simultaneously.
Interleaved log output can still be meaningful if each log entry from different contexts had a distinctive stamp. This is where MDCs come into play.
The MDC provides a hash of contextual messages that are identified by unique keys. These unique keys are set by the application and appended to log messages to identify groups of log events. One use of the Mapped Diagnostic Context is to store HTTP request headers associated with a Rack request. These headers can be included with all log messages emitted while generating the HTTP response.
When configured to do so, PatternLayout instances will automatically retrieve the mapped diagnostic context for the current thread with out any user intervention. This context information can be used to track user sessions in a Rails application, for example.
Note that MDCs are managed on a per thread basis. MDC operations such as ‘[]`, `[]=`, and `clear` affect the MDC of the current thread only. MDCs of other threads remain unaffected.
By default, when a new thread is created it will inherit the context of its parent thread. However, the ‘inherit` method may be used to inherit context for any other thread in the application.
Constant Summary collapse
- NAME =
The name used to retrieve the MDC from thread-local storage.
:logging_mapped_diagnostic_context
- STACK_NAME =
The name used to retrieve the MDC stack from thread-local storage.
:logging_mapped_diagnostic_context_stack
Instance Method Summary collapse
-
#[](key) ⇒ Object
Public: Get the context value identified with the key parameter.
-
#[]=(key, value) ⇒ Object
Public: Put a context value as identified with the key parameter into the current thread’s context map.
-
#clear ⇒ Object
Public: Clear all mapped diagnostic information if any.
-
#clear_context ⇒ Object
Remove the flattened context.
-
#context ⇒ Object
Returns the Hash acting as the storage for this MappedDiagnosticContext.
-
#delete(key) ⇒ Object
Public: Remove the context value identified with the key parameter.
-
#flatten(ary) ⇒ Object
Given an Array of Hash objects, flatten all the key/value pairs from the Hash objects in the ary into a single Hash.
-
#inherit(obj) ⇒ Object
Public: Inherit the diagnostic context of another thread.
-
#peek ⇒ Object
Returns the most current Hash from the stack of contexts.
-
#pop ⇒ Object
Public: Remove the most recently pushed Hash from the stack of contexts.
-
#push(hash) ⇒ Object
Public: Push a new Hash of key/value pairs onto the stack of contexts.
-
#sanitize(hash, target = {}) ⇒ Object
Given a Hash convert all keys into Strings.
-
#stack ⇒ Object
Returns the stack of Hash objects that are storing the diagnostic context information.
-
#update(hash) ⇒ Object
Public: Add all the key/value pairs from the given hash to the current mapped diagnostic context.
Instance Method Details
#[](key) ⇒ Object
Public: Get the context value identified with the key parameter.
key - The String identifier for the context.
Returns the value associated with the key or nil if there is no value present.
62 63 64 |
# File 'lib/logging/diagnostic_context.rb', line 62 def []( key ) context.fetch(key.to_s, nil) end |
#[]=(key, value) ⇒ Object
Public: Put a context value as identified with the key parameter into the current thread’s context map.
key - The String identifier for the context. value - The String value to store.
Returns the value.
50 51 52 53 |
# File 'lib/logging/diagnostic_context.rb', line 50 def []=( key, value ) clear_context peek.store(key.to_s, value) end |
#clear ⇒ Object
Public: Clear all mapped diagnostic information if any. This method is useful in cases where the same thread can be potentially used over and over in different unrelated contexts.
Returns the MappedDiagnosticContext.
126 127 128 129 130 |
# File 'lib/logging/diagnostic_context.rb', line 126 def clear clear_context Thread.current.thread_variable_set(STACK_NAME, nil) self end |
#clear_context ⇒ Object
Remove the flattened context.
196 197 198 199 |
# File 'lib/logging/diagnostic_context.rb', line 196 def clear_context Thread.current.thread_variable_set(NAME, nil) self end |
#context ⇒ Object
Returns the Hash acting as the storage for this MappedDiagnosticContext. A new storage Hash is created for each Thread running in the application.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/logging/diagnostic_context.rb', line 160 def context c = Thread.current.thread_variable_get(NAME) if c.nil? c = if Thread.current.thread_variable_get(STACK_NAME) flatten(stack) else Hash.new end Thread.current.thread_variable_set(NAME, c) end return c end |
#delete(key) ⇒ Object
Public: Remove the context value identified with the key parameter.
key - The String identifier for the context.
Returns the value associated with the key or nil if there is no value present.
73 74 75 76 |
# File 'lib/logging/diagnostic_context.rb', line 73 def delete( key ) clear_context peek.delete(key.to_s) end |
#flatten(ary) ⇒ Object
Given an Array of Hash objects, flatten all the key/value pairs from the Hash objects in the ary into a single Hash. The flattening occurs left to right. So that the key/value in the very last Hash overrides any other key from the previous Hash objcts.
ary - An Array of Hash objects.
Returns a Hash.
229 230 231 232 233 234 235 |
# File 'lib/logging/diagnostic_context.rb', line 229 def flatten( ary ) return ary.first.dup if ary.length == 1 hash = {} ary.each { |h| hash.update h } return hash end |
#inherit(obj) ⇒ Object
Public: Inherit the diagnostic context of another thread. In the vast majority of cases the other thread will the parent that spawned the current thread. The diagnostic context from the parent thread is cloned before being inherited; the two diagnostic contexts can be changed independently.
Returns the MappedDiagnosticContext.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/logging/diagnostic_context.rb', line 140 def inherit( obj ) case obj when Hash Thread.current.thread_variable_set(STACK_NAME, [obj.dup]) when Thread return if Thread.current == obj DIAGNOSTIC_MUTEX.synchronize do if hash = obj.thread_variable_get(STACK_NAME) Thread.current.thread_variable_set(STACK_NAME, [flatten(hash)]) end end end self end |
#peek ⇒ Object
Returns the most current Hash from the stack of contexts.
190 191 192 |
# File 'lib/logging/diagnostic_context.rb', line 190 def peek stack.last end |
#pop ⇒ Object
Public: Remove the most recently pushed Hash from the stack of contexts. If no contexts have been pushed then no action will be taken. The default context cannot be popped off the stack; please use the ‘clear` method if you want to remove all key/value pairs from the context.
Returns nil or the Hash removed from the stack.
112 113 114 115 116 117 |
# File 'lib/logging/diagnostic_context.rb', line 112 def pop return unless Thread.current.thread_variable_get(STACK_NAME) return unless stack.length > 1 clear_context stack.pop end |
#push(hash) ⇒ Object
Public: Push a new Hash of key/value pairs onto the stack of contexts.
hash - The Hash of values to push onto the context stack.
Returns this context. Raises an ArgumentError if hash is not a Hash.
99 100 101 102 103 |
# File 'lib/logging/diagnostic_context.rb', line 99 def push( hash ) clear_context stack << sanitize(hash) self end |
#sanitize(hash, target = {}) ⇒ Object
Given a Hash convert all keys into Strings. The values are not altered in any way. The converted keys and their values are stored in the target Hash if provided. Otherwise a new Hash is created and returned.
hash - The Hash of values to push onto the context stack. target - The target Hash to store the key value pairs.
Returns a new Hash with all keys converted to Strings. Raises an ArgumentError if hash is not a Hash.
211 212 213 214 215 216 217 218 |
# File 'lib/logging/diagnostic_context.rb', line 211 def sanitize( hash, target = {} ) unless hash.is_a?(Hash) raise ArgumentError, "Expecting a Hash but received a #{hash.class.name}" end hash.each { |k,v| target[k.to_s] = v } return target end |
#stack ⇒ Object
Returns the stack of Hash objects that are storing the diagnostic context information. This stack is guarnteed to always contain at least one Hash.
179 180 181 182 183 184 185 186 |
# File 'lib/logging/diagnostic_context.rb', line 179 def stack s = Thread.current.thread_variable_get(STACK_NAME) if s.nil? s = [{}] Thread.current.thread_variable_set(STACK_NAME, s) end return s end |
#update(hash) ⇒ Object
Public: Add all the key/value pairs from the given hash to the current mapped diagnostic context. The keys will be converted to strings. Existing keys of the same name will be overwritten.
hash - The Hash of values to add to the current context.
Returns this context.
86 87 88 89 90 |
# File 'lib/logging/diagnostic_context.rb', line 86 def update( hash ) clear_context sanitize(hash, peek) self end |