java.lang.Object
net.i2p.router.web.GraphListener
All Implemented Interfaces:
RateSummaryListener

public class GraphListenerextends Object implements RateSummaryListener
Creates and updates the in-memory or on-disk RRD database, and provides methods to generate graphs of the data.
  • Field Summary

    Modifier and Type
    Field
    Description
    private long
    Wall-clock ms when this listener took its Rate registration, or 0 if never.
    private long
    Steps written by backfill(List) since this listener attached.
    private long
    Coalesce count of the Rate when this listener attached.
    private int
    Current consecutive error count.
    private final I2PAppContext
    The context whose stats are graphed.
    private RrdDb
    The RRD database instance.
    private String
    RRD datasource name for the event count.
    private final boolean
    True if the rate is saved between runs.
    private long
    Wall-clock ms of the newest step stored, live or backfilled; 0 if none.
    private long
    Wall-clock ms of the last successful RRD write, or 0 if none has succeeded.
    private final Log
    Logger for this class.
    private String
    RRD datasource name for the primary stat value.
    private final Rate
    Rate the samples are recorded into.
    Renderer for generating graph images.
    private int
    Number of rows in the RRD archive.
    private Sample
    Current sample being populated.
    private long
    Coalesce delta at which _unrecoverableSteps was last charged.
    private long
    Steps proven lost because the Rate no longer retains them.
    private long
    Write count of this listener when it attached; 0 for a fresh instance.
    private long
    Total successful RRD writes.
    (package private) static final ConsolFun
    Consolidation function used for RRD archives.
    (package private) static final DsType
    Data source type used for RRD datasources.
    (package private) static final int
    Offset (seconds) subtracted from "now" when computing the fetch window end, so a clock skewed slightly ahead of system time does not yield NaNs.
    private static final int
    Consecutive write failures at which the report stops being routine.
    static final int
    Most rows a persistent RRD archive keeps, three months at one-minute resolution.
    private static final int
    Minimum number of rows to keep in the archive.
    (package private) static final int
    Number of periods in one day (1440 = 60 minutes * 24 hours at 1-minute resolution).
    static final String
    Whether RRDs are kept on disk between restarts rather than held in memory.
    (package private) static final String
    Note that .jrb files are NOT compatible with .rrd files.
    (package private) static final String
    Prefix for RRD file names.
    (package private) static final String
    Suffix for RRD file names.
    private static final ConcurrentMap<String,String>
    Datasource id to the stat it records, so a log line can name the stat rather than the 20-character munge JRobin requires.
    private static final int
    Number of primary steps per consolidation.
    private static final long
    Three months in milliseconds (used to compute max rows for persistent RRDs).
    private static final double
    X-factor (allowed fraction of unknown data) for RRD consolidation.
  • Constructor Summary

    Constructors
    Constructor
    Description
    Create a listener for the given rate stat.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    add(double totalValue, long eventCount, double totalEventTime, long period)
    Add a new data point to the RRD database.
    int
    Replay retained samples whose steps this listener never stored, oldest first.
    (package private) long
    chargePermanentLoss(long coalesceDelta)
    Record that the drift on this listener cannot be backfilled.
    private void
    Drop our registration on the Rate, but only if we still hold it.
    private void
    Release the RRD handle, leaving the Rate registration alone.
    private void
    closeQuietly(RrdDb db, String baseName)
    Drop a half-built handle so a failed open cannot leak a file descriptor.
    (package private) static String
    JRobin can only deal with 20 character data source names, so we need to create a unique, munged version from the user/developer-visible name.
    boolean
    Compare with another listener by the underlying rate.
    (package private) long
    When this listener attached, the origin for a never-written listener's age.
    (package private) RrdBackendFactory
    The backend factory matching this listener's persistence setting.
    private static RrdBackendFactory
    getBackendFactory(boolean isPersistent)
    The RRD backend for persistent or in-memory data.
    (package private) long
    Steps written by backfill(List) since this listener attached.
    (package private) long
    Coalesces on this Rate since this listener attached.
    (package private) long
    The ledger invariant: coalesces the Rate produced, minus steps this listener stored.
    (package private) int
    Consecutive failed writes, reset by any successful live write.
    (package private) RrdDb
    Get the RRD database instance.
    (package private) String
    Get the RRD datasource name for the event count.
    (package private) long
    Newest archive step stored, live or backfilled.
    (package private) long
    When the most recent RRD write succeeded, or 0 if none has.
    double[]
    getLastValues(int count)
    Fetch the last count data points from the RRD database for use by the dual-baseline minigraph renderer (data-rx/data-tx attributes on the canvas).
    (package private) String
    Get the RRD datasource name for the primary stat.
    Retrieve the tracked rate.
    (package private) int
    Get the number of rows in the RRD archive.
    (package private) long
    Steps this listener has stored since it attached.
    (package private) long
    Steps proven lost beyond the Rate's retained-sample ring.
    (package private) long
    Number of successful RRD writes since startup.
    int
    Hash code based on the underlying rate.
    (package private) boolean
    Whether this listener has stopped recording.
    (package private) boolean
    Whether this listener is still the one its Rate notifies on coalesce.
    (package private) boolean
    Whether the open RRD handle can still take a write.
    private void
    noteWrite(long whenSeconds)
    Record a step that is now in the RRD, advancing the write ledger.
    private void
    Count a failed write and report it, without giving up the listener.
    (package private) long
    now()
    Get the current time from the router clock.
    private boolean
    Build the RRD handle, sample and renderer for this listener.
    (package private) static boolean
    Does self still own a Rate's summary-listener registration?
    private String
    Name the rate this listener records, for log lines that must say which graph.
    (package private) static boolean
    recoverable(long lastStoredTimeMs, List<Rate.CoalescedSample> retained)
    Can a listener still recover its drift, or are the missing steps gone?
    (package private) static void
    registerName(String id, String statName)
    Record which stat a datasource id stands for.
    void
    Render a graph of the stat data using default settings.
    void
    renderGraph(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit)
    Single graph.
    void
    renderGraph(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, List<GraphListener> extras, String titleOverride, boolean showRestarts)
    Render this stat with any number of extra series overlaid as lines.
    void
    renderGraph(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, GraphListener lsnr2, String titleOverride, boolean showRestarts)
    Single or two-data-source graph.
    void
    renderGraphLines(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, List<GraphListener> extras, String titleOverride, boolean showRestarts)
    Render this stat with any number of extra series, every series drawn as a line.
    void
    renderGraphMeta(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, List<GraphListener> extras, String titleOverride, boolean showRestarts, boolean meta)
    void
    renderGraphMeta(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, GraphListener lsnr2, String titleOverride, boolean showRestarts, boolean meta)
    As renderGraph(OutputStream, int, int, boolean, boolean, boolean, boolean, int, int, boolean), but emitting the plot geometry and series as JSON rather than drawing the graph.
    void
    renderGraphMetaLines(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, List<GraphListener> extras, String titleOverride, boolean showRestarts, boolean meta)
    (package private) boolean
    Re-obtain the RRD handle, keeping this listener registered and its ledger intact.
    (package private) static double
    sampleValue(double totalValue, long eventCount)
    Value to store for a period, given the totals the rate hands its listeners.
    boolean
    Open (or re-open) the RRD database and take the Rate's summary registration.
    (package private) static String
    Translate a datasource id back to the stat it records.
    void
    Stop listening and close the RRD database.
    (package private) static long
    toArchiveSecondMs(long timestampMs)
    Truncate a wall-clock stamp to the whole second an RRD actually stores.

    Methods inherited from class Object

    clone, getClass, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • PROP_PERSISTENT

      public static final String PROP_PERSISTENT
      Whether RRDs are kept on disk between restarts rather than held in memory.
      See Also:
    • RRD_DIR

      static final String RRD_DIR
      Note that .jrb files are NOT compatible with .rrd files.
      See Also:
    • RRD_PREFIX

      static final String RRD_PREFIX
      Prefix for RRD file names.
      See Also:
    • RRD_SUFFIX

      static final String RRD_SUFFIX
      Suffix for RRD file names.
      See Also:
    • CF

      static final ConsolFun CF
      Consolidation function used for RRD archives.
    • DS

      static final DsType DS
      Data source type used for RRD datasources.
    • XFF

      private static final double XFF
      X-factor (allowed fraction of unknown data) for RRD consolidation.
      See Also:
    • STEPS

      private static final int STEPS
      Number of primary steps per consolidation.
      See Also:
    • _context

      private final I2PAppContext _context
      The context whose stats are graphed.
    • _log

      private final Log _log
      Logger for this class.
    • _rate

      private final Rate _rate
      Rate the samples are recorded into.
    • _isPersistent

      private final boolean _isPersistent
      True if the rate is saved between runs.
    • _name

      private String _name
      RRD datasource name for the primary stat value.
    • _eventName

      private String _eventName
      RRD datasource name for the event count.
    • _db

      private RrdDb _db
      The RRD database instance.
    • _sample

      private Sample _sample
      Current sample being populated.
    • _renderer

      private GraphRenderer _renderer
      Renderer for generating graph images.
    • _rows

      private int _rows
      Number of rows in the RRD archive.
    • MAX_CONSECUTIVE_ERRORS

      private static final int MAX_CONSECUTIVE_ERRORS
      Consecutive write failures at which the report stops being routine.

      No longer a teardown threshold. A momentary I/O error used to detach the listener here, which converted a transient fault into a permanent hole in the graph and then left it unfilled for up to a full sync interval. Retirement is now only ever the heal ladder's explicit REBUILD.

      See Also:
    • _consecutiveErrors

      private volatile int _consecutiveErrors
      Current consecutive error count.
    • _lastUpdateSuccess

      private volatile long _lastUpdateSuccess
      Wall-clock ms of the last successful RRD write, or 0 if none has succeeded.
    • _attachedMs

      private volatile long _attachedMs
      Wall-clock ms when this listener took its Rate registration, or 0 if never.

      The origin for how long a listener that has never written has had the chance to write. Measured from the router's graphing start instead, a listener created a second ago inherits the age of the whole graphing session and is reported as stalled before it has ever had a period to coalesce, so every rebuild and every on-demand graph would trip the error.

    • _updateCount

      private volatile long _updateCount
      Total successful RRD writes.
    • _lastStoredTimeMs

      private volatile long _lastStoredTimeMs
      Wall-clock ms of the newest step stored, live or backfilled; 0 if none.
    • _backfillCount

      private volatile long _backfillCount
      Steps written by backfill(List) since this listener attached.
    • _coalesceBaseline

      private volatile long _coalesceBaseline
      Coalesce count of the Rate when this listener attached.

      Rate.getCoalesceCount() counts from the Rate's construction, and a stat registered at router startup has been coalescing long before the console attaches to it. Comparing that absolute count against this listener's own write count would report the entire pre-attach history as drift, on every listener, forever. The baseline is what makes the ledger an actual invariant rather than a number that only ever grows.

    • _updateBaseline

      private volatile long _updateBaseline
      Write count of this listener when it attached; 0 for a fresh instance.
    • _unrecoverableSteps

      private volatile long _unrecoverableSteps
      Steps proven lost because the Rate no longer retains them.

      The retained-sample ring holds a fixed handful of coalesces, so a listener that was unreachable for longer than that can never recover the intervening steps. Those are reported once rather than retried forever, which needs a counter as well as a flag: "drift is positive" says a step was lost, but only a count says how many are gone for good.

      See Also:
    • _unrecoverableCoalesceDelta

      private volatile long _unrecoverableCoalesceDelta
      Coalesce delta at which _unrecoverableSteps was last charged.

      Keyed on the coalesce delta rather than on the drift because the coalesce delta only ever grows, so each lost step is charged exactly once even when the drift dips back to zero after a heal and climbs again.

    • PERIODS

      static final int PERIODS
      Number of periods in one day (1440 = 60 minutes * 24 hours at 1-minute resolution).
      See Also:
    • GRAPH_END_OFFSET_SECONDS

      static final int GRAPH_END_OFFSET_SECONDS
      Offset (seconds) subtracted from "now" when computing the fetch window end, so a clock skewed slightly ahead of system time does not yield NaNs. Shared with GraphRenderer, which applies the same window for plotting.
      See Also:
    • MIN_ROWS

      private static final int MIN_ROWS
      Minimum number of rows to keep in the archive.
      See Also:
    • MAX_ROWS

      public static final int MAX_ROWS
      Most rows a persistent RRD archive keeps, three months at one-minute resolution.
      See Also:
    • THREE_MONTHS

      private static final long THREE_MONTHS
      Three months in milliseconds (used to compute max rows for persistent RRDs).
      See Also:
    • STAT_NAMES

      private static final ConcurrentMap<String,String> STAT_NAMES
      Datasource id to the stat it records, so a log line can name the stat rather than the 20-character munge JRobin requires.

      Bounded by the distinct stat-and-period pairs ever graphed - one entry each, replaced in place when a listener is rebuilt - so it holds no per-rebuild state.

  • Constructor Details

    • GraphListener

      public GraphListener(Rate r)
      Create a listener for the given rate stat.
      Parameters:
      r - the rate to track
  • Method Details

    • getLastUpdateSuccess

      long getLastUpdateSuccess()
      When the most recent RRD write succeeded, or 0 if none has.
      Returns:
      wall-clock ms of the last successful write, or 0 if none
    • getAttachedMs

      long getAttachedMs()
      When this listener attached, the origin for a never-written listener's age.
      Returns:
      wall-clock ms when the listener took its Rate registration, or 0 if never
    • getUpdateCount

      long getUpdateCount()
      Number of successful RRD writes since startup.
      Returns:
      the live-write count, backfilled steps included
    • getBackfillCount

      long getBackfillCount()
      Steps written by backfill(List) since this listener attached.
      Returns:
      backfilled step count
    • getLastStoredTimeMs

      long getLastStoredTimeMs()
      Newest archive step stored, live or backfilled.
      Returns:
      wall-clock ms of the newest stored step, whole seconds, or 0 if none
    • getConsecutiveErrors

      int getConsecutiveErrors()
      Consecutive failed writes, reset by any successful live write.
      Returns:
      current streak length
    • getUnrecoverableSteps

      long getUnrecoverableSteps()
      Steps proven lost beyond the Rate's retained-sample ring.
      Returns:
      cumulative permanently lost step count
    • getCoalesceDelta

      long getCoalesceDelta()
      Coalesces on this Rate since this listener attached.
      Returns:
      coalesce delta since attach, never negative
      See Also:
    • getStoredDelta

      long getStoredDelta()
      Steps this listener has stored since it attached.
      Returns:
      stored delta since attach, never negative
    • getCoalesceDrift

      long getCoalesceDrift()
      The ledger invariant: coalesces the Rate produced, minus steps this listener stored.

      Positive means a coalesce produced a sample this listener never put into the RRD, which is a provably lost step rather than an inference from a stalled write. Zero is the only healthy value; negative is not reachable, because a step is only stored after its coalesce has already been counted.

      Returns:
      coalesces minus stored steps
    • chargePermanentLoss

      long chargePermanentLoss(long coalesceDelta)
      Record that the drift on this listener cannot be backfilled.

      Charged against the coalesce delta rather than the drift, which only grows, so every lost step is charged exactly once. A later heal that drops the drift back to zero and a further outage both still get reported.

      Parameters:
      coalesceDelta - coalesce delta from getCoalesceDelta()
      Returns:
      steps newly charged, 0 when they were already charged
    • add

      public void add(double totalValue, long eventCount, double totalEventTime, long period)
      Add a new data point to the RRD database.
      Specified by:
      add in interface RateSummaryListener
      Parameters:
      totalValue - the total value for the period
      eventCount - the number of events in the period
      totalEventTime - the total event time for the period
      period - the period duration in milliseconds
    • sampleValue

      static double sampleValue(double totalValue, long eventCount)
      Value to store for a period, given the totals the rate hands its listeners.

      Pure so the backfill path and the live path cannot drift apart: a backfilled step recorded with a different rule than the live steps around it would put a spike in the middle of an otherwise consistent trace.

      Parameters:
      totalValue - the total value for the period
      eventCount - the number of events in the period
      Returns:
      the primary datasource value for the step
    • noteWrite

      private void noteWrite(long whenSeconds)
      Record a step that is now in the RRD, advancing the write ledger.

      Synchronized because the count is the numerator of the drift invariant the watchdog reads: a lost increment here is reported as a lost step, so the ledger has to be exact rather than a volatile increment that the delivery thread and the watchdog's backfill can race. The monitor covers the field updates only, never the RRD write that precedes them.

      Parameters:
      whenSeconds - archive step written, in seconds
    • noteWriteFailure

      private void noteWriteFailure(Throwable t)
      Count a failed write and report it, without giving up the listener.

      Below the threshold this is routine and rate limited; from the threshold on it is an ERROR naming the rate, because a streak this long is not going to clear itself and the operator has to know which graph is affected. Reported every MAX_CONSECUTIVE_ERRORS failures rather than on every one, so a rate with a persistently failing RRD cannot turn the log into the stall it used to avoid.

      Deliberately does not call stopListening(). Retiring on a transient I/O error left a hole in the graph that nothing refilled for up to a full sync interval, and the detach was indistinguishable from a real fault in the logs. Recording now stops on its own - the ledger drift grows - and the heal ladder reopens or rebuilds the handle when it can.

      Parameters:
      t - the write failure, counted toward the consecutive-error limit
    • rateName

      private String rateName()
      Name the rate this listener records, for log lines that must say which graph.
      Returns:
      the rate's stat name and period
    • backfill

      public int backfill(List<Rate.CoalescedSample> samples)
      Replay retained samples whose steps this listener never stored, oldest first.

      Recovery for a listener that missed a few steps while nothing was obviously wrong: the values are still in the Rate's retained-sample ring, and an archive step is empty forever once nothing writes it, so writing them late beats losing them.

      RrdDb.store rejects any timestamp not strictly newer than the last one stored, so a sample that lands on or before getLastStoredTimeMs() is skipped rather than attempted: the write path has already covered the recent steps and the ring can overlap them.

      Parameters:
      samples - retained samples, oldest first; may be null or empty
      Returns:
      number of steps written
    • toArchiveSecondMs

      static long toArchiveSecondMs(long timestampMs)
      Truncate a wall-clock stamp to the whole second an RRD actually stores.

      Pure because the whole skip rule is this truncation applied against getLastStoredTimeMs(): comparing raw milliseconds would let a sample 999ms into a step the listener already stored look newer than it is, and the write would then be rejected by the archive as non-monotonic.

      Parameters:
      timestampMs - wall-clock ms, not monotonic time
      Returns:
      the same instant truncated down to a whole second, in ms
    • recoverable

      static boolean recoverable(long lastStoredTimeMs, List<Rate.CoalescedSample> retained)
      Can a listener still recover its drift, or are the missing steps gone?

      The Rate retains a fixed, small ring of coalesces. Once every retained sample is already stored, the steps the listener missed are older than anything the Rate remembers, and no amount of retrying will produce them. Telling those two cases apart is what keeps the watchdog from re-attempting a hopeless backfill every ten seconds and from calling a permanent gap a transient one.

      Pure, so the boundary is unit tested without a Rate, an RRD or a clock.

      Parameters:
      lastStoredTimeMs - newest stored step from getLastStoredTimeMs()
      retained - the Rate's retained samples, oldest first; may be null
      Returns:
      true if at least one retained sample is newer than the last stored step
    • isWritable

      boolean isWritable()
      Whether the open RRD handle can still take a write.

      Distinct from isDetached(): a detached listener has no handle at all, whereas this can be false for a listener that still holds a handle which something else has already closed. That is the cheapest fault to heal, since the listener, its registration and its RRD file all survive and only the handle has to be re-obtained.

      Returns:
      true if the handle is open and can accept a write
    • isDetached

      boolean isDetached()
      Whether this listener has stopped recording.

      A detached listener keeps its entry in GraphGenerator's rate-to-listener map, so the generator uses this to notice that it needs re-creating.

      Returns:
      true if the RRD database is closed and no longer receiving samples
    • isRegistered

      boolean isRegistered()
      Whether this listener is still the one its Rate notifies on coalesce.

      Distinct from isDetached(): a Rate holds exactly one summary listener, so registering a replacement silently orphans this instance while its RRD stays open. That instance is attached - so it renders fine and the console shows a plausible graph - but it can never be called again, and nothing about that state is visible without asking the Rate.

      Returns:
      true if Rate.getSummaryListener() is this instance
    • ownsRegistration

      static boolean ownsRegistration(RateSummaryListener registered, RateSummaryListener self)
      Does self still own a Rate's summary-listener registration?

      Reference identity, deliberately not equals(Object): two listeners for the same Rate are equals but only one of them is ever called, so only reference equality answers "is this instance the live one".

      Parameters:
      registered - current registration, as returned by Rate.getSummaryListener()
      self - the listener asking
      Returns:
      true only if self is the registered instance
    • createName

      static String createName(I2PAppContext ctx, String wanted)
      JRobin can only deal with 20 character data source names, so we need to create a unique, munged version from the user/developer-visible name.
      Parameters:
      ctx - the context whose SHA munges the name
      wanted - the stat name, or the stat name and period, to munge
      Returns:
      a 20-character datasource id, distinct for each name
    • registerName

      static void registerName(String id, String statName)
      Record which stat a datasource id stands for.

      Called when a listener opens its RRD, which is the only place the id is minted.

      Parameters:
      id - the 20-character datasource id from createName(I2PAppContext, String)
      statName - the stat name and period the id was minted from
    • statName

      static String statName(String id)
      Translate a datasource id back to the stat it records.

      The ids are a hash of the stat name, so they cannot be inverted - a log line quoting one names nothing an operator can look up. Pass every id a human reads through here.

      Parameters:
      id - the datasource id
      Returns:
      the registered stat name, or id itself when nothing registered it (an unknown id is still worth logging)
    • getRate

      public Rate getRate()
      Retrieve the tracked rate.
      Returns:
      the rate instance
    • startListening

      public boolean startListening()
      Open (or re-open) the RRD database and take the Rate's summary registration.

      The ledger baseline is taken before the file is touched, not after: a coalesce that happens while this method is opening the database is a step the listener was never registered for, so it is lost and the drift has to say so. Baselineing afterwards would quietly forgive it.

      Returns:
      true if the database was opened and the summary listener registered
    • reopen

      boolean reopen()
      Re-obtain the RRD handle, keeping this listener registered and its ledger intact.

      The cheapest heal there is, for the case where the handle is closed but the listener itself, its Rate registration and its recorded history all survive. Rebuilding instead would work too, at the cost of a new listener, a new renderer and a window in which the rate has no listener at all - so the ladder tries this first.

      Neither the ledger baseline nor the write count is reset: the drift is the fault being healed, and a heal that quietly rebased it would hide the very loss it is repairing.

      Returns:
      true if a fresh, writable handle is in place
    • openRdd

      private boolean openRdd()
      Build the RRD handle, sample and renderer for this listener.

      Split out of startListening() so reopen() can obtain a new handle without touching the Rate. Does no I/O on any rate's monitor: it is called from the sync task and from the health watchdog, never from a Rate-synchronized callback.

      The three fields are published together, at the end and only on success, so a failure part-way through leaves the listener detached rather than attached with a null sample that the delivery thread would dereference.

      Returns:
      true if _db is set and writable
    • closeQuietly

      private void closeQuietly(RrdDb db, String baseName)
      Drop a half-built handle so a failed open cannot leak a file descriptor.
      Parameters:
      db - the handle to release, or null if none was built
      baseName - the rate's name, for the log line
    • stopListening

      public void stopListening()
      Stop listening and close the RRD database.

      The registration on the Rate is dropped only if this instance still holds it, so tearing down a superseded listener cannot silence its live replacement.

    • closeHandle

      private void closeHandle()
      Release the RRD handle, leaving the Rate registration alone.

      Shared by stopListening() and reopen(): the only difference between tearing a listener down and healing it is whether the Rate keeps pointing at this instance, and that has to be the caller's decision.

      The handle is cleared before it is closed, so a delivery thread that is midway through a write sees a null database and skips rather than writing to a handle that is on its way out.

    • clearRateRegistration

      private void clearRateRegistration()
      Drop our registration on the Rate, but only if we still hold it.

      Rate keeps a single summary listener, so an instance tearing down after a newer one has registered would otherwise stop the newer one being called. The newer one keeps its open RRD and still reports itself attached, so recording dies with no write error, no detach, and no exception anywhere - every health check keeps saying healthy.

      Rate.clearSummaryListener(RateSummaryListener) does the ownership test and the clear as one atomic operation, so a registration that appears between the two cannot be dropped. A clear that finds the registration already gone is the ordinary post-detach case and is not reported; only a registration held by a different live listener is, because that means two teardowns raced.

    • renderGraph

      public void renderGraph(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit) throws IOException
      Single graph.
      Parameters:
      out - the output stream to write the graph to
      width - image width in pixels
      height - image height in pixels
      hideLegend - if true, omit the legend
      hideGrid - if true, omit the grid lines
      hideTitle - if true, omit the title
      showEvents - if true, plot the event count rather than the stat
      periodCount - number of time periods to display, or -1 for default
      end - number of periods before now
      showCredit - if true, keep the signature line
      Throws:
      IOException - if this listener has no open RRD, or the graph cannot be produced
    • renderGraph

      public void renderGraph(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, GraphListener lsnr2, String titleOverride, boolean showRestarts) throws IOException
      Single or two-data-source graph.
      Parameters:
      out - the output stream to write the graph to
      width - image width in pixels
      height - image height in pixels
      hideLegend - if true, omit the legend
      hideGrid - if true, omit the grid lines
      hideTitle - if true, omit the title
      showEvents - if true, plot the event count rather than the stat
      periodCount - number of time periods to display, or -1 for default
      end - number of periods before now
      showCredit - if true, keep the signature line
      lsnr2 - 2nd data source to plot on same graph, or null. Not recommended for events.
      titleOverride - If non-null, overrides the title
      showRestarts - if true, draw the vertical restart lines and "Router restarted" label
      Throws:
      IOException - if this listener has no open RRD, or the graph cannot be produced
    • renderGraphMeta

      public void renderGraphMeta(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, GraphListener lsnr2, String titleOverride, boolean showRestarts, boolean meta) throws IOException
      As renderGraph(OutputStream, int, int, boolean, boolean, boolean, boolean, int, int, boolean), but emitting the plot geometry and series as JSON rather than drawing the graph.

      A graph is served as an <img>, so its SVG is an isolated document and a client cannot read the numbers out of it. This is the separate request that carries them, for a cursor readout.

      Parameters:
      out - the output stream to write the graph or the metadata to
      width - image width in pixels
      height - image height in pixels
      hideLegend - if true, omit the legend
      hideGrid - if true, omit the grid lines
      hideTitle - if true, omit the title
      showEvents - if true, plot the event count rather than the stat
      periodCount - number of time periods to display, or -1 for default
      end - number of periods before now
      showCredit - if true, keep the signature line
      lsnr2 - 2nd data source to plot on same graph, or null. Not recommended for events.
      titleOverride - If non-null, overrides the title
      showRestarts - if true, draw the vertical restart lines and "Router restarted" label
      meta - true to write the metadata instead of the image
      Throws:
      IOException - if this listener has no open RRD, or the graph cannot be produced
    • renderGraph

      public void renderGraph(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, List<GraphListener> extras, String titleOverride, boolean showRestarts) throws IOException
      Render this stat with any number of extra series overlaid as lines.

      Every series shares one axis, so the caller must supply stats that measure the same thing; see GraphGroups for the sanctioned groupings.

      Parameters:
      out - the output stream to write the graph to
      width - image width in pixels
      height - image height in pixels
      hideLegend - if true, omit the legend
      hideGrid - if true, omit the grid lines
      hideTitle - if true, omit the title
      showEvents - if true, plot the event count rather than the stat
      periodCount - number of time periods to display, or -1 for default
      end - number of periods before now
      showCredit - if true, keep the signature line
      extras - extra series in legend order, or null for none
      titleOverride - If non-null, overrides the title
      showRestarts - if true, draw the vertical restart lines and "Router restarted" label
      Throws:
      IOException - if this listener has no open RRD, or the graph cannot be produced
    • renderGraphMeta

      public void renderGraphMeta(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, List<GraphListener> extras, String titleOverride, boolean showRestarts, boolean meta) throws IOException
      Parameters:
      out - the output stream to write the graph or the metadata to
      width - image width in pixels
      height - image height in pixels
      hideLegend - if true, omit the legend
      hideGrid - if true, omit the grid lines
      hideTitle - if true, omit the title
      showEvents - if true, plot the event count rather than the stat
      periodCount - number of time periods to display, or -1 for default
      end - number of periods before now
      showCredit - if true, keep the signature line
      extras - extra series in legend order, or null for none
      titleOverride - If non-null, overrides the title
      showRestarts - if true, draw the vertical restart lines and "Router restarted" label
      meta - true to write the metadata instead of the image
      Throws:
      IOException - if this listener has no open RRD, or the graph cannot be produced
    • renderGraphLines

      public void renderGraphLines(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, List<GraphListener> extras, String titleOverride, boolean showRestarts) throws IOException
      Render this stat with any number of extra series, every series drawn as a line.

      Identical to renderGraph(OutputStream, int, int, boolean, boolean, boolean, boolean, int, int, boolean, List, String, boolean) except that the primary is a line rather than a filled area, so no member of the group is hidden behind another.

      Parameters:
      out - the output stream to write the graph to
      width - image width in pixels
      height - image height in pixels
      hideLegend - if true, omit the legend
      hideGrid - if true, omit the grid lines
      hideTitle - if true, omit the title
      showEvents - if true, plot the event count rather than the stat
      periodCount - number of time periods to display, or -1 for default
      end - number of periods before now
      showCredit - if true, keep the signature line
      extras - extra series in legend order, or null for none
      titleOverride - If non-null, overrides the title
      showRestarts - if true, draw the vertical restart lines and "Router restarted" label
      Throws:
      IOException - if this listener has no open RRD, or the graph cannot be produced
    • renderGraphMetaLines

      public void renderGraphMetaLines(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, int end, boolean showCredit, List<GraphListener> extras, String titleOverride, boolean showRestarts, boolean meta) throws IOException
      Parameters:
      out - the output stream to write the graph or the metadata to
      width - image width in pixels
      height - image height in pixels
      hideLegend - if true, omit the legend
      hideGrid - if true, omit the grid lines
      hideTitle - if true, omit the title
      showEvents - if true, plot the event count rather than the stat
      periodCount - number of time periods to display, or -1 for default
      end - number of periods before now
      showCredit - if true, keep the signature line
      extras - extra series in legend order, or null for none
      titleOverride - If non-null, overrides the title
      showRestarts - if true, draw the vertical restart lines and "Router restarted" label
      meta - true to write the metadata instead of the image
      Throws:
      IOException - if this listener has no open RRD, or the graph cannot be produced
    • renderGraph

      public void renderGraph(OutputStream out) throws IOException
      Render a graph of the stat data using default settings.
      Parameters:
      out - the output stream to write the graph to
      Throws:
      IOException - if rendering fails
    • getName

      String getName()
      Get the RRD datasource name for the primary stat.
      Returns:
      the datasource name
    • getEventName

      String getEventName()
      Get the RRD datasource name for the event count.
      Returns:
      the event datasource name
    • getData

      RrdDb getData()
      Get the RRD database instance.
      Returns:
      the RRD database
    • now

      long now()
      Get the current time from the router clock.
      Returns:
      the current time in milliseconds
    • getBackendFactory

      RrdBackendFactory getBackendFactory()
      The backend factory matching this listener's persistence setting.
      Returns:
      the NIO file factory when persistent, otherwise the in-memory one
    • getBackendFactory

      private static RrdBackendFactory getBackendFactory(boolean isPersistent)
      The RRD backend for persistent or in-memory data.
      Parameters:
      isPersistent - whether the graph data is kept in files on disk
      Returns:
      the default NIO file factory when persistent, the in-memory factory otherwise
    • getRows

      int getRows()
      Get the number of rows in the RRD archive.
      Returns:
      the row count
    • getLastValues

      public double[] getLastValues(int count)
      Fetch the last count data points from the RRD database for use by the dual-baseline minigraph renderer (data-rx/data-tx attributes on the canvas). Points are right-aligned. The returned array is NaN-padded on the left when fewer points are available.
      Specified by:
      getLastValues in interface RateSummaryListener
      Parameters:
      count - number of most recent data points to retrieve
      Returns:
      array of average values (units depend on the stat), length = count
    • equals

      public boolean equals(Object obj)
      Compare with another listener by the underlying rate.
      Overrides:
      equals in class Object
      Parameters:
      obj - the object to compare with
      Returns:
      true if the listeners track the same rate
    • hashCode

      public int hashCode()
      Hash code based on the underlying rate.
      Overrides:
      hashCode in class Object
      Returns:
      the hash code