module Logging::MappedDiagnosticContext
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.
Constants
- NAME
The name used to retrieve the MDC from thread-local storage.
- STACK_NAME
The name used to retrieve the MDC stack from thread-local storage.
Public Instance Methods
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.
# File lib/logging/diagnostic_context.rb, line 62 def []( key ) context.fetch(key.to_s, nil) end
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.
# File lib/logging/diagnostic_context.rb, line 50 def []=( key, value ) clear_context peek.store(key.to_s, value) end
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.
# File lib/logging/diagnostic_context.rb, line 126 def clear clear_context Thread.current.thread_variable_set(STACK_NAME, nil) self end
Remove the flattened context.
# File lib/logging/diagnostic_context.rb, line 196 def clear_context Thread.current.thread_variable_set(NAME, nil) self end
Returns the Hash acting as the storage for this MappedDiagnosticContext. A new storage Hash is created for each Thread running in the application.
# 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
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.
# File lib/logging/diagnostic_context.rb, line 73 def delete( key ) clear_context peek.delete(key.to_s) end
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.
# 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
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.
# 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
Returns the most current Hash from the stack of contexts.
# File lib/logging/diagnostic_context.rb, line 190 def peek stack.last end
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.
# 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
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.
# File lib/logging/diagnostic_context.rb, line 99 def push( hash ) clear_context stack << sanitize(hash) self end
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.
# 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
Returns the stack of Hash objects that are storing the diagnostic context information. This stack is guarnteed to always contain at least one Hash.
# 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
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.
# File lib/logging/diagnostic_context.rb, line 86 def update( hash ) clear_context sanitize(hash, peek) self end