Class MdcProjector

java.lang.Object
io.github.pimak.ntfy.core.MdcProjector

public final class MdcProjector extends Object
Projects a logging framework's diagnostic context (MDC) onto the small, bounded, sanitized map of key -> value pairs an alert body may carry — the include-mdc-keys feature.

The allow-list is the whole point. Only keys the operator named are ever read, there is deliberately no wildcard and no "include everything" mode, and an unset allow-list yields an empty map without the MDC ever being consulted. The MDC of a production application routinely holds session tokens, user identifiers, and other data that must never leave the process in a notification, so the selection can only ever widen by an explicit configuration change.

Adapters (logback, JUL, ...) call project(java.util.List<java.lang.String>, java.util.function.Function<java.lang.String, java.lang.String>) with the configured key list and a lookup function bound to their framework's context, and hand the result to AlertEvent. This class is public precisely because those adapters live in sibling modules; it is otherwise an internal utility with no state.

Three properties make the projection safe to append to an alert body:

  • Structure-safe. Every control character, Unicode line/paragraph separator and format (Cf) character is replaced with a single space, so an MDC value can neither forge body structure (a fake Caused by: line) nor break PayloadTruncator's line-by-line splitting, nor smuggle a bidi override past a reader's eyes.
  • Bounded. At most 16 entries, each value at most 256 characters, and at most 1024 characters overall — so the context block can never crowd the message, cause chain, and stack frames out of ntfy's 4096-byte body budget.
  • Cheap. The caps are enforced while iterating, so a pathological 10 000-entry allow-list costs at most 16 context lookups per event, not 10 000.

Every guard here is idempotent: sanitize(java.util.Map<java.lang.String, java.lang.String>) applied to an already-sanitized map returns an equal map, which is what lets AlertEvent re-run it on construction as belt-and-braces without mangling a projection this class already produced.

  • Method Details

    • project

      public static Map<String,String> project(List<String> allowedKeys, Function<String,String> lookup)
      Reads the allow-listed keys out of a logging context and returns the sanitized, bounded, configured-order map to render into an alert body.

      Semantics, in order:

      1. A null/empty allowedKeys or a null lookup returns an empty map without calling lookup even once — the feature being unset must cost nothing and must not touch the context at all.
      2. allowedKeys is walked in order; that order IS the rendering order in the body.
      3. null/blank keys and keys already consulted are skipped.
      4. A key whose value is absent, blank, or nothing but control characters produces no entry — and therefore no line in the body — rather than an empty "key: " line.
      5. A lookup that throws is treated exactly like an absent value: a hostile or simply broken context implementation must never break the logging path it is decorating.
      Parameters:
      allowedKeys - the operator-configured MDC key names, in the order they should be rendered
      lookup - reads one key from the ambient logging context, returning null when absent
      Returns:
      an unmodifiable, insertion-ordered map; never null