Class FloggerMetadataKey<T>


  • public class FloggerMetadataKey<T>
    extends Object
    Key for logging semi-structured metadata values.

    Metadata keys can be used to provide log statements with strongly typed values which can be read and interpreted by logging backends or other logs related tools. This mechanism is intended for values with specific semantics and should not be seen as a replacement for logging arguments as part of a formatted log message.

    Examples of where using MetadataKey is suitable are:

    • Logging a value with special semantics (e.g. values that are handled specially by the logger backend).
    • Passing configuration to a specific logger backend to modify behaviour for individual log statements or all log statements in a ScopedLoggingContext.
    • Logging a structured value in many places with consistent formatting (e.g. so it can later be re-parsed by logs related tools).

    If you just want to log an general "key value pair" in a small number of log statements, it is still better to just do something like log("key=%s", value).

    Metadata keys are expected to be singleton constants, and should never be allocated at the log site itself. Even though they are expected to be singletons, comparing keys should be done via equals() (rather than '==') since this will be safe in cases where non-singleton keys exist, and is just as fast if the keys are singletons.

    It is strongly recommended that any public FloggerMetadataKey instances are defined as public static final fields in a top-level or nested class, which does no logging. Ideally a separate class would be defined to hold only the keys, since this allows keys to be loaded very early in the logging Platform lifecycle without risking any static initialization issues.

    Custom subclasses of MetadataKey which override either of the protected emit(T, io.spine.logging.flogger.FloggerMetadataKey.KeyValueHandler) methods should take care to avoid calling any code which might trigger logging since this could lead to unexpected recusrion, especially if the key is being logged as part of a ScopedLoggingContext. While there is protection against unbounded reentrant logging in Flogger, it is still best practice to avoid it where possible.

    Metadata keys are passed to a log statement via the with() method, so it can aid readability to choose a name for the constant field which reads "fluently" as part of the log statement. For example:

    
     // Prefer this...
     logger.atInfo().with(FILE_LOGGING_FOR, user).log("User specific log message...");
     // to...
     logger.atInfo().with(SET_LOGGING_TO_USER_FILE, user).log("User specific log message...");
     

    Logger backends can act upon metadata present in log statements to modify behavior. Any metadata entries that are not handled by a backend explicitly are, by default, rendered as part of the log statement in a default format.

    Note that some metadata entries are handled before being processed by the backend (e.g. rate limiting), but a metadata entry remains present to record the that rate limiting was enabled.

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

      • FloggerMetadataKey

        protected FloggerMetadataKey​(String label,
                                     Class<? extends T> clazz,
                                     boolean canRepeat)
        Constructor for custom key subclasses. Most use-cases will not require the use of custom keys, but occasionally it can be useful to create a specific subtype to control the formatting of values or to have a family of related keys with a common parent type.
    • Method Detail

      • single

        public static <T> FloggerMetadataKey<T> single​(String label,
                                                       Class<? extends T> clazz)
        Creates a key for a single piece of metadata. If metadata is set more than once using this key for the same log statement, the last set value will be the one used, and other values will be ignored (although callers should never rely on this behavior).

        Key instances behave like singletons, and two key instances with the same label will still be considered distinct. The recommended approach is to always assign MetadataKey instances to static final constants.

        When calling from Kotlin, please give preference to MetadataKeysKt.singleKey(). In Kotlin, there's no explicit difference between primitive and object classes. When compiling to JVM, it is resolved during the compilation. An accident passing of a potentially primitive class may lead to a runtime exception because metadata keys are used with generics.

      • repeated

        public static <T> FloggerMetadataKey<T> repeated​(String label,
                                                         Class<T> clazz)
        Creates a key for a repeated piece of metadata. If metadata is added more than once using this key for a log statement, all values will be retained as key/value pairs in the order they were added.

        Key instances behave like singletons, and two key instances with the same label will still be considered distinct. The recommended approach is to always assign MetadataKey instances to static final constants.

        When calling from Kotlin, please give preference to MetadataKeysKt.repeatedKey(). In Kotlin, there's no explicit difference between primitive and object classes. When compiling to JVM, it is resolved during the compilation. An accident passing of a potentially primitive class may lead to a runtime exception because metadata keys are used with generics.

      • getLabel

        public final String getLabel()
        Returns a short, human readable text label which will prefix the metadata in cases where it is formatted as part of the log message.
      • cast

        public final T cast​(Object value)
        Cast an arbitrary value to the type of this key.
      • canRepeat

        public final boolean canRepeat()
        Whether this key can be used to set more than one value in the metadata.
      • emit

        protected void emit​(T value,
                            FloggerMetadataKey.KeyValueHandler kvh)
        Override this method to provide custom logic for emitting one or more key/value pairs for a given metadata value (call safeEmit(Object,KeyValueHandler) from logging code to actually emit values).

        By default this method simply emits the given value with this key's label, but it can be customized key/value pairs if necessary.

        Note that if multiple key/value pairs are emitted, the following best-practice should be followed:

        • Key names should be of the form "<label>.<suffix>".
        • Suffixes should only contain lower case ASCII letters and underscore (i.e. [a-z_]).

        This method is called as part of logs processing and could be invoked a very large number of times in performance critical code. Implementations must be very careful to avoid calling any code which might risk deadlocks, stack overflow, concurrency issues or performance problems. In particular, implementations of this method should be careful to avoid:

        • Calling any code which could log using the same MetadataKey instance (unless you implement protection against reentrant calling in this method).
        • Calling code which might block (e.g. performing file I/O or acquiring locks).
        • Allocating non-trivial amounds of memory (e.g. recording values in an unbounded data structure).

        If you do implement a MetadataKey with non-trivial value processing, you should always make it very clear in the documentation that the key may not be suitable for widespread use.

        By default this method just calls out.handle(getLabel(), value).

      • emitRepeated

        protected void emitRepeated​(Iterator<T> values,
                                    FloggerMetadataKey.KeyValueHandler kvh)
        Override this method to provide custom logic for emitting one or more key/value pairs for a sequence of metadata values (call safeEmitRepeated(Iterator,KeyValueHandler) from logging code to actually emit values).

        Emits one or more key/value pairs for a sequence of repeated metadata values. By default this method simply calls emit(Object,KeyValueHandler) once for each value, in order. However it could be overridden to treat the sequence of values for a repeated key as a single entity (e.g. by joining elements with a separator).

        See the emit(Object,KeyValueHandler) method for additional caveats for custom implementations.

      • getBloomFilterMask

        public final long getBloomFilterMask()
        Returns a 64-bit bloom filter mask for this metadata key, usable by backend implementations to efficiently determine uniqueness of keys (e.g. for deduplication and grouping). This value is calculated on the assumption that there are normally not more than 10 distinct metadata keys being processed at any time. If more distinct keys need to be processed using this Bloom Filter mask, it will result in a higher than optimal false-positive rate.
      • hashCode

        public final int hashCode()
        Overrides:
        hashCode in class Object
      • equals

        public final boolean equals​(Object obj)
        Overrides:
        equals in class Object