Class ScopedLoggingContext


  • public abstract class ScopedLoggingContext
    extends Object
    A user facing API for creating and modifying scoped logging contexts in applications.

    Scoped contexts provide a way for application code to attach metadata and control the behaviour of logging within well defined contexts. This is most often associated with making "per request" modifications to logging behaviour such as:

    • Adding a request ID to every log statement.
    • Forcing logging at a finer level for a specific request (e.g. based on a URL debug parameter).

    Contexts are nestable and new contexts can be added to provide additional metadata which will be available to logging as long as the context is installed.

    Note that in the current API contexts are also modifiable after creation, but this usage is discouraged and may be removed in future. The problem with modifying contexts after creation is that, since contexts can be shared between threads, it is potentially confusing if tags are added to a context when it is being used concurrently by multiple threads.

    Note that since logging contexts are designed to be modified by code in libraries and helper functions which do not know about each other, the data structures and behaviour of logging contexts are carefully designed to avoid any accidental "undoing" of existing behaviour. In particular:

    • Tags can only be added to contexts, never modified or removed.
    • Logging that's enabled by one context cannot be disabled from within a nested context.

    One possibly surprising result of this behaviour is that it's not possible to disable logging from within a context. However this is quite intentional, since overly verbose logging should be fixed by other mechanisms (code changes, global logging configuration), and not on a "per request" basis.

    Depending on the framework used, it's possible that the current logging context will be automatically propagated to some or all threads or sub-tasks started from within the context. This is not guaranteed however and the semantic behaviour of context propagation is not defined by this class.

    In particular, if you haven't explicitly opened a context in which to run your code, there is no guarantee that a default "global" context exists. In this case any attempts to add metadata (e.g. via addTags(io.spine.logging.flogger.context.Tags)) will fail, returning false.

    Context support and automatic propagation is heavily reliant on Java platform capabilities, and precise behaviour is likely to differ between runtime environments or frameworks. Context propagation may not behave the same everywhere, and in some situations logging contexts may not be supported at all. Methods which attempt to affect context state may do nothing in some environments, or when called at some points in an application. If application code relies on modifications to an existing, implicit logging context, it should always check the return values of any modification methods called (e.g. addTags(Tags)).

    See Also:
    Original Java code of Google Flogger
    • Constructor Detail

      • ScopedLoggingContext

        protected ScopedLoggingContext()
    • Method Detail

      • getInstance

        public static ScopedLoggingContext getInstance()
        Returns the platform/framework specific implementation of the logging context API. This is a singleton value and need not be cached by callers. If logging contexts are not supported, this method will return an empty context implementation which has no effect.
      • newContext

        public abstract ScopedLoggingContext.Builder newContext()
        Creates a new context builder to which additional logging metadata can be attached before being installed or used to wrap some existing code.
        
         ScopedLoggingContext ctx = ScopedLoggingContext.getInstance();
         Foo result = ctx.newContext().withTags(Tags.of("my_tag", someValue)).call(MyClass::doFoo);
         

        Implementations of this API must return a subclass of ScopedLoggingContext.Builder which can install all necessary metadata into a new context from the builder's current state.

        Note for users: if you don't need an instance of ScopedLoggingContext for some reason such as testability (injecting it, for example), consider using the static methods in ScopedLoggingContexts instead to avoid the need to call getInstance():

        
         Foo result = ScopedLoggingContexts.newContext()
             .withTags(Tags.of("my_tag", someValue))
             .call(MyClass::doFoo);
         
      • newContext

        public abstract ScopedLoggingContext.Builder newContext​(ScopeType scopeType)
        Creates a new context builder to which additional logging metadata can be attached before being installed or used to wrap some existing code.

        This method is the same as newContext() except it additionally binds a new ScopeType instance to the newly created context. This allows log statements to control stateful logging operations (e.g. rate limiting) using per(ScopeType) method.

        Note for users: if you don't need an instance of ScopedLoggingContext for some reason such as testability (injecting it, for example), consider using the static methods in ScopedLoggingContexts instead to avoid the need to call getInstance().

      • addTags

        @CanIgnoreReturnValue
        public boolean addTags​(Tags tags)
        Adds tags to the current set of log tags for the current context. Tags are merged together and existing tags cannot be modified. This is deliberate since two pieces of code may not know about each other and could accidentally use the same tag name; in that situation it's important that both tag values are preserved.

        Furthermore, the types of data allowed for tag values are strictly controlled. This is also very deliberate since these tags must be efficiently added to every log statement and so it's important that they resulting string representation is reliably cacheable and can be calculated without invoking arbitrary code (e.g. the toString() method of some unknown user type).

        Returns:
        false if there is no current context, or scoped contexts are not supported.
      • addMetadata

        @CanIgnoreReturnValue
        public <T> boolean addMetadata​(FloggerMetadataKey<T> key,
                                       T value)
        Adds a single metadata key/value pair to the current context.

        Unlike Tags, which have a well defined value ordering, independent of the order in which values were added, context metadata preserves the order of addition. As such, it is not advised to add values for the same metadata key from multiple threads, since that may create non-deterministic ordering. It is recommended (where possible) to add metadata when building a new context, rather than adding it to context visible to multiple threads.

      • applyLogLevelMap

        @CanIgnoreReturnValue
        public boolean applyLogLevelMap​(LogLevelMap logLevelMap)
        Applies the given log level map to the current context. Log level settings are merged with any existing setting from the current (or parent) contexts such that logging will be enabled for a log statement if:
        • It was enabled by the given map.
        • It was already enabled by the current context.

        The effects of this call will be undone only when the current context terminates.

        Returns:
        false if there is no current context, or scoped contexts are not supported.