Class LogbackAlertAppender

java.lang.Object
ch.qos.logback.core.spi.ContextAwareBase
ch.qos.logback.core.UnsynchronizedAppenderBase<ch.qos.logback.classic.spi.ILoggingEvent>
io.github.pimak.ntfy.logback.LogbackAlertAppender
All Implemented Interfaces:
ch.qos.logback.core.Appender<ch.qos.logback.classic.spi.ILoggingEvent>, ch.qos.logback.core.spi.ContextAware, ch.qos.logback.core.spi.FilterAttachable<ch.qos.logback.classic.spi.ILoggingEvent>, ch.qos.logback.core.spi.LifeCycle

public class LogbackAlertAppender extends ch.qos.logback.core.UnsynchronizedAppenderBase<ch.qos.logback.classic.spi.ILoggingEvent>
A thin Logback UnsynchronizedAppenderBase<ILoggingEvent> shell over the framework-neutral AlertEngine. Configuration arrives either through JavaBean setters (Joran/XML config) or through a pre-built NtfyConfig injected via setConfig(NtfyConfig) by the auto-installing NtfyLogbackConfigurator. Every decision that matters — logger exclusion (including this library's own package), the NO_ALERT marker gate, storm rate-limiting, digesting, payload assembly, and HTTP publishing — lives in the engine; this class only maps a Logback event onto an AlertEvent and calls AlertEngine.submit(io.github.pimak.ntfy.core.AlertEvent).

Self-diagnoses exclusively through the inherited StatusManager methods (via LogbackDiagnostics), so no SLF4J feedback loop is possible and no credential ever appears in diagnostic output.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    Marker name a caller attaches to a single log call to opt it out of alerting entirely.

    Fields inherited from class ch.qos.logback.core.UnsynchronizedAppenderBase

    name, started

    Fields inherited from class ch.qos.logback.core.spi.ContextAwareBase

    context
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    protected void
    append(ch.qos.logback.classic.spi.ILoggingEvent event)
    Gates on level, then maps the event and hands it to the engine, which owns all further gating and publishing.
    Read-only pipeline observability counters (published / suppressed / failed) for this appender.
    void
    setActions(String actions)
    Action buttons as a raw ntfy Actions header value (short format, e.g.
    void
    setAppName(String appName)
     
    void
    setAsync(boolean async)
    Asynchronous (offloaded) delivery: individual error alerts are handed to the engine's bounded work queue and published by a daemon worker, so a slow/unreachable ntfy server never blocks the logging thread.
    void
    setAsyncQueueCapacity(int asyncQueueCapacity)
    Bounded async delivery queue capacity; only consulted when async is true.
    void
    false asks the server not to store the message (Cache: no).
    void
    setClickUrl(String clickUrl)
    URL opened when a notification is tapped (ntfy Click header).
    void
    setConnectTimeout(String connectTimeout)
    Accepts a duration string (bare int = ms, Nms/Ns/Nm/Nh/Nd, or ISO-8601).
    void
    setDigestPriority(String digestPriority)
     
    void
    setDigestTags(String digestTags)
     
    void
    setEnabled(boolean enabled)
     
    void
    setErrorPriority(String errorPriority)
     
    void
    setErrorTags(String errorTags)
     
    void
    setExcludedExceptionTypes(String excludedExceptionTypes)
    Comma-separated fully qualified exception class names.
    void
    setExcludedLoggers(String excludedLoggers)
    A single comma-separated value of logger-name prefixes to exclude from alerting.
    void
    setFirebase(String firebase)
    false asks the server not to forward the message to Firebase (Firebase: no).
    void
    URL of the icon shown beside the notification (ntfy Icon header).
    void
    setIconsByLogger(String iconsByLogger)
    Per-logger icon overrides as prefix=url pairs separated by commas.
    void
    setIncludeMdcKeys(String includeMdcKeys)
    A comma-separated allow-list of MDC keys rendered into alert bodies, one key: value line each, in the order given here.
    void
    setLocale(String locale)
    Language of notification bodies and diagnostics as a BCP 47 tag (e.g.
    void
    setMaxAlertsPerWindow(int maxAlertsPerWindow)
     
    void
    setMaxStackFrames(int maxStackFrames)
     
    void
    setPassword(String password)
     
    void
    setRequestTimeout(String requestTimeout)
    Accepts a duration string (bare int = ms, Nms/Ns/Nm/Nh/Nd, or ISO-8601).
    void
    setRequireHttpsForCredentials(boolean requireHttpsForCredentials)
    Strict mode: refuse engine activation (rather than only warn) when credentials would be sent over a cleartext http:// endpoint.
    void
    setStartupPing(String startupPing)
    Opt-in startup self-test: off (default), probe (read-only reachability check, publishes nothing) or publish (a real low-priority test notification).
    void
    setStartupPingFailFast(boolean startupPingFailFast)
    When true, a failed startup self-test aborts application startup instead of only warning.
    void
    setStartupPingNotifyFailures(boolean startupPingNotifyFailures)
    Whether a failed self-test is announced as a notification on a route that PASSED.
    void
    setStartupPingWarn(String startupPingWarn)
    Startup self-test for the optional WARN route: off (default), probe or publish.
    void
    setSuppressionWindow(String suppressionWindow)
    Accepts a duration string (bare int = ms, Nms/Ns/Nm/Nh/Nd, or ISO-8601).
    void
     
    void
     
    void
     
    void
     
    void
    setUsername(String username)
     
    void
    setWarnPriority(String warnPriority)
    ntfy Priority header for WARN alerts and the WARN digest; default default.
    void
    setWarnTags(String warnTags)
    ntfy Tags header for WARN alerts and the WARN digest; default warning.
    void
    setWarnTopic(String warnTopic)
    The topic WARN-level events are published to.
    void
    Builds an AlertEngine from the injected config (if present) or from the JavaBean setters, starts it, and marks this appender started ONLY if the engine actually activated.
    void
    Tears the engine down (flushing any pending digest) and marks this appender stopped.

    Methods inherited from class ch.qos.logback.core.UnsynchronizedAppenderBase

    addFilter, buildReentryGuard, clearAllFilters, doAppend, getCopyOfAttachedFiltersList, getFilterChainDecision, getName, isStarted, setName, toString

    Methods inherited from class ch.qos.logback.core.spi.ContextAwareBase

    addError, addError, addInfo, addInfo, addStatus, addWarn, addWarn, getContext, getDeclaredOrigin, getStatusManager, setContext

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

    Methods inherited from interface ch.qos.logback.core.spi.ContextAware

    addError, addError, addInfo, addInfo, addStatus, addWarn, addWarn, getContext, setContext
  • Field Details

    • NO_ALERT_MARKER_NAME

      public static final String NO_ALERT_MARKER_NAME
      Marker name a caller attaches to a single log call to opt it out of alerting entirely. Re-exposed from AlertEngine.NO_ALERT_MARKER_NAME so existing callers keep a stable handle instead of a magic string.
      See Also:
  • Constructor Details

    • LogbackAlertAppender

      public LogbackAlertAppender()
  • Method Details

    • setUrl

      public void setUrl(String url)
    • setTopic

      public void setTopic(String topic)
    • setToken

      public void setToken(String token)
    • setUsername

      public void setUsername(String username)
    • setPassword

      public void setPassword(String password)
    • setTitle

      public void setTitle(String title)
    • setAppName

      public void setAppName(String appName)
    • setMaxStackFrames

      public void setMaxStackFrames(int maxStackFrames)
    • setConnectTimeout

      public void setConnectTimeout(String connectTimeout)
      Accepts a duration string (bare int = ms, Nms/Ns/Nm/Nh/Nd, or ISO-8601).
    • setRequestTimeout

      public void setRequestTimeout(String requestTimeout)
      Accepts a duration string (bare int = ms, Nms/Ns/Nm/Nh/Nd, or ISO-8601).
    • setMaxAlertsPerWindow

      public void setMaxAlertsPerWindow(int maxAlertsPerWindow)
    • setSuppressionWindow

      public void setSuppressionWindow(String suppressionWindow)
      Accepts a duration string (bare int = ms, Nms/Ns/Nm/Nh/Nd, or ISO-8601).
    • setErrorPriority

      public void setErrorPriority(String errorPriority)
    • setDigestPriority

      public void setDigestPriority(String digestPriority)
    • setErrorTags

      public void setErrorTags(String errorTags)
    • setDigestTags

      public void setDigestTags(String digestTags)
    • setWarnTopic

      public void setWarnTopic(String warnTopic)
      The topic WARN-level events are published to. Setting it to a non-blank value is the entire opt-in for WARN alerting: unset (the default), this appender alerts on ERROR and above only. Set it to the same value as <topic> to alert on warnings through the main topic at a different priority. XML: <warnTopic>my-app-warnings</warnTopic>.
    • setWarnPriority

      public void setWarnPriority(String warnPriority)
      ntfy Priority header for WARN alerts and the WARN digest; default default.
    • setWarnTags

      public void setWarnTags(String warnTags)
      ntfy Tags header for WARN alerts and the WARN digest; default warning.
    • setClickUrl

      public void setClickUrl(String clickUrl)
      URL opened when a notification is tapped (ntfy Click header).
    • setActions

      public void setActions(String actions)
      Action buttons as a raw ntfy Actions header value (short format, e.g. "view, View logs, https://grafana.example.com/d/abc").
    • setExcludedLoggers

      public void setExcludedLoggers(String excludedLoggers)
      A single comma-separated value of logger-name prefixes to exclude from alerting.
    • setExcludedExceptionTypes

      public void setExcludedExceptionTypes(String excludedExceptionTypes)
      Comma-separated fully qualified exception class names. An event whose cause chain contains any of them never alerts — matched anywhere in the chain, not just on the surface throwable.
    • setCache

      public void setCache(String cache)
      false asks the server not to store the message (Cache: no). Subscribers who are offline at that moment never receive it. Default true.

      Declared as a String rather than a boolean on purpose. Joran converts an XML value with Boolean.valueOf, which maps everything that is not "true" to false — so <cache>yes</cache> would turn caching OFF and cost every offline subscriber their alerts, silently, on the one surface where the value is typed by hand. NtfyConfig.Builder reads the common spellings and reports anything else at start().

    • setFirebase

      public void setFirebase(String firebase)
      false asks the server not to forward the message to Firebase (Firebase: no). Default true. A String for the same reason as setCache(String).
    • setIcon

      public void setIcon(String icon)
      URL of the icon shown beside the notification (ntfy Icon header). Must be an http/https URL; a value that is not one is dropped with a diagnostic. ntfy renders JPEG and PNG only, but that is a property of the bytes served, not of the path, so a URL whose extension names another format is warned about and still sent.
    • setIconsByLogger

      public void setIconsByLogger(String iconsByLogger)
      Per-logger icon overrides as prefix=url pairs separated by commas. The longest prefix matching an alert's logger wins; anything unmatched keeps the default icon. Alerts only — a notification has no logger to match on.
    • setIncludeMdcKeys

      public void setIncludeMdcKeys(String includeMdcKeys)
      A comma-separated allow-list of MDC keys rendered into alert bodies, one key: value line each, in the order given here. Unset by default, and there is deliberately no wildcard form: an MDC value reaches a notification only when an operator named its key, because a production MDC routinely holds tokens and user identifiers that must never leave the process.
    • setLocale

      public void setLocale(String locale)
      Language of notification bodies and diagnostics as a BCP 47 tag (e.g. fr, de-DE). Defaults to English; an unknown/unshipped locale silently uses English. XML: <locale>fr</locale>.
    • setEnabled

      public void setEnabled(boolean enabled)
    • setAsync

      public void setAsync(boolean async)
      Asynchronous (offloaded) delivery: individual error alerts are handed to the engine's bounded work queue and published by a daemon worker, so a slow/unreachable ntfy server never blocks the logging thread. On by default; set false for pre-2.0 synchronous delivery.
    • setAsyncQueueCapacity

      public void setAsyncQueueCapacity(int asyncQueueCapacity)
      Bounded async delivery queue capacity; only consulted when async is true.
    • setRequireHttpsForCredentials

      public void setRequireHttpsForCredentials(boolean requireHttpsForCredentials)
      Strict mode: refuse engine activation (rather than only warn) when credentials would be sent over a cleartext http:// endpoint. On by default; set false for the pre-2.0 warn-and-activate behavior.
    • setStartupPing

      public void setStartupPing(String startupPing)
      Opt-in startup self-test: off (default), probe (read-only reachability check, publishes nothing) or publish (a real low-priority test notification). Verifies at boot that the configured endpoint, credentials and topic actually work, instead of finding out when the first real error alert fails to deliver. An unrecognized value keeps the default and is warned about on the Logback status manager.
    • setStartupPingFailFast

      public void setStartupPingFailFast(boolean startupPingFailFast)
      When true, a failed startup self-test aborts application startup instead of only warning. Off by default, and it also makes the self-test run inline (adding at most connect+request timeout to boot) since a start that already returned cannot be aborted. Aimed at CI/staging rather than production.
    • setStartupPingWarn

      public void setStartupPingWarn(String startupPingWarn)
      Startup self-test for the optional WARN route: off (default), probe or publish. Independent of setStartupPing(String) — ntfy permissions are granted per topic, so a healthy ERROR route proves nothing about the WARN one. Only consulted when a warnTopic is configured and its route survived.
    • setStartupPingNotifyFailures

      public void setStartupPingNotifyFailures(boolean startupPingNotifyFailures)
      Whether a failed self-test is announced as a notification on a route that PASSED. On by default; it can never fire unless some route passed, so it cannot publish to a topic the self-test has not just verified. Set false to keep probe absolutely publish-free.
    • start

      public void start()
      Builds an AlertEngine from the injected config (if present) or from the JavaBean setters, starts it, and marks this appender started ONLY if the engine actually activated. A silently/partially unconfigured engine leaves the appender isStarted()==false (the engine already reported why via the StatusManager), exactly as the original appender did. A second start() without an intervening stop() is a guarded no-op, so the engine and its threads can never be orphaned.
      Specified by:
      start in interface ch.qos.logback.core.spi.LifeCycle
      Overrides:
      start in class ch.qos.logback.core.UnsynchronizedAppenderBase<ch.qos.logback.classic.spi.ILoggingEvent>
    • append

      protected void append(ch.qos.logback.classic.spi.ILoggingEvent event)
      Gates on level, then maps the event and hands it to the engine, which owns all further gating and publishing. The gate lives here (mirroring the Quarkus adapter's SEVERE gate) so the documented level contract holds on every install path — without it, a root-logger auto-install would push every INFO/DEBUG line verbatim to the ntfy topic.

      ERROR and above always alert. WARN alerts only when the engine activated a warn route; below WARN nothing ever does, on any configuration. Sub-ERROR events therefore cost a level comparison and nothing else in the default setup.

      Specified by:
      append in class ch.qos.logback.core.UnsynchronizedAppenderBase<ch.qos.logback.classic.spi.ILoggingEvent>
    • stop

      public void stop()
      Tears the engine down (flushing any pending digest) and marks this appender stopped. Null-safe when never started and safe to call twice — AlertEngine.stop() is itself idempotent.
      Specified by:
      stop in interface ch.qos.logback.core.spi.LifeCycle
      Overrides:
      stop in class ch.qos.logback.core.UnsynchronizedAppenderBase<ch.qos.logback.classic.spi.ILoggingEvent>
    • getCounters

      public PipelineCounters getCounters()
      Read-only pipeline observability counters (published / suppressed / failed) for this appender. The returned holder is stable for the appender's whole lifetime — the same instance before and after start()/stop() cycles — so the tallies are monotonic and never reset by a stop or a Logback context reset/reattach. Counters are pulled here, never logged, preserving the loop-safe design.