java.lang.Object
org.metabit.platform.interfacing.jjournal.Journal
All Implemented Interfaces:
AutoCloseable

public class Journal extends Object implements AutoCloseable
jjournal main class for systemd journal read access.
Instantiate with the appropriate set of access flags, then use its member functions to select, read, iterate, filter etc. systemd journal entries.
This library reflects the concepts used in systemd journals, especially way they designed cursor and filters.
Version:
$Id: $Id
  • Field Details

    • ACCEPTABLE_FLAGS_FOR_DIRECTORY_OPEN

      public static final EnumSet<OpenFlags> ACCEPTABLE_FLAGS_FOR_DIRECTORY_OPEN
      Constant ACCEPTABLE_FLAGS_FOR_DIRECTORY_OPEN
    • LIB_NAME

      public static final String LIB_NAME
      Constant LIB_NAME="systemd"
      See Also:
    • MILLION

      static final int MILLION
      See Also:
    • JOURNAL_FIELD_NAME_REGEXP

      static final Pattern JOURNAL_FIELD_NAME_REGEXP
    • libJournal

      private static NativeJournal libJournal
    • runtime

      protected static jnr.ffi.Runtime runtime
      Constant runtime
    • journalHandle

      private jnr.ffi.byref.AddressByReference journalHandle
    • actualJournalHandle

      private jnr.ffi.Address actualJournalHandle
    • filtersActiveFlag

      private boolean filtersActiveFlag
    • reusableByteBuffer

      private ByteBuffer reusableByteBuffer
    • reusableLongLongByReference

      private final jnr.ffi.byref.LongLongByReference reusableLongLongByReference
    • reusablePointerByReference

      private final jnr.ffi.byref.PointerByReference reusablePointerByReference
    • journalCharset

      private Charset journalCharset
    • journalTimeZoneOffset

      private ZoneOffset journalTimeZoneOffset
  • Constructor Details

    • Journal

      public Journal(OpenFlags... openFlags) throws JJournalException
      constructor with varargs.
      Parameters:
      openFlags - flags to use when opening the Journal access. See OpenFlags enum for details.
      Throws:
      JJournalException - on severe errors
    • Journal

      public Journal(Set<OpenFlags> openFlags) throws JJournalException
      open journal accesss with flags.
      Parameters:
      openFlags - an EnumSet of flags.
      Throws:
      JJournalException - example for specifying a single flag: EnumSet.of(OpenFlags.LOCAL_ONLY)
    • Journal

      public Journal(String namespace, EnumSet<OpenFlags> openFlags) throws JJournalException
      constructor, opening journal access within a namespace.

      sd_journal_open_namespace() is similar to sd_journal_open() but takes an additional namespace parameter that specifies which journal namespace to operate on. If specified as NULL the call is identical to sd_journal_open(). If non-NULL only data from the namespace identified by the specified parameter is accessed. This call understands two additional flags: if SD_JOURNAL_ALL_NAMESPACES is specified the namespace parameter is ignored and all defined namespaces are accessed simultaneously; if SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE the specified namespace and the default namespace are accessed but no others (this flag has no effect when namespace is passed as NULL). For details about journal namespaces see systemd-journald.service(8).

      Parameters:
      namespace - namespace to operate on
      openFlags - flags for opening journal access
      Throws:
      JJournalException - on failure
    • Journal

      public Journal(String namespace, OpenFlags... openFlags) throws JJournalException
      constructor, opening journal access within a namespace.

      sd_journal_open_namespace() is similar to sd_journal_open() but takes an additional namespace parameter that specifies which journal namespace to operate on. If specified as NULL the call is identical to sd_journal_open(). If non-NULL only data from the namespace identified by the specified parameter is accessed. This call understands two additional flags: if SD_JOURNAL_ALL_NAMESPACES is specified the namespace parameter is ignored and all defined namespaces are accessed simultaneously; if SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE the specified namespace and the default namespace are accessed but no others (this flag has no effect when namespace is passed as NULL). For details about journal namespaces see systemd-journald.service(8).

      Parameters:
      namespace - namespace to operate on
      openFlags - flags for opening journal access
      Throws:
      JJournalException - on failure NB: if you provide a String parameter, you'll get this - the namespace constructor, which is the one used most often. If you are looking to use the file-based constructor, make sure to provide an List of String objects.
    • Journal

      public Journal(List<String> filenames) throws JJournalException
      open one or multiple journal files. "All files will be opened and interleaved automatically. Please note that in the case of a live journal, this function is only useful for debugging, because individual journal files can be rotated at any moment, and the opening of specific files is inherently racy." As per documentation, there are no flags available or used here.
      Parameters:
      filenames - the files to use, with paths. (no special order, it might as well be a set).
      Throws:
      JJournalException - exception on severe errors
    • Journal

      @Deprecated public Journal(Path directory, Set<OpenFlags> openFlags) throws JJournalException
      Deprecated.
      open a journal *directory*. to differentiate from the open-files signature, this is intentionally using a single Path, instead of a String.
      Parameters:
      directory - path
      openFlags - This accepts OS_ROOT, SYSTEM, and CURRENT_USER flags. for OS_ROOT, the specified path is taken as root from where the relative subdirectories /var/log/journal and /run/log/journal are searched. SYSTEM and CURRENT_USER filter/limit the available files to system, and current user, respectively.
      Throws:
      JJournalException - exception on severe errors
    • Journal

      public Journal(Path directory, OpenFlags... openFlags) throws JJournalException
      open a journal *directory*. to differentiate from the open-files signature, this is intentionally using a single Path, instead of a String.
      Parameters:
      directory - path
      openFlags - This accepts OS_ROOT, SYSTEM, and CURRENT_USER flags. for OS_ROOT, the specified path is taken as root from where the relative subdirectories /var/log/journal and /run/log/journal are searched. SYSTEM and CURRENT_USER filter/limit the available files to system, and current user, respectively.
      Throws:
      JJournalException - exception on severe errors
  • Method Details

    • init

      private void init()
    • close

      public void close()
      close after use. Also used for AutoCloseable.
      Specified by:
      close in interface AutoCloseable
    • moveToEarliest

      public void moveToEarliest() throws JJournalException
      move to first valid entry. (log is ordered chronologically)
      Throws:
      JJournalException - on failure
    • moveToNext

      public boolean moveToNext() throws JJournalException
      move to next entry.
      Returns:
      true if successful, false if not.
      Throws:
      JJournalException - if the movement fails altogether in a bad way. useful for loops like this: `while(journal.moveToNext()) {...} `
    • moveToPrevious

      public boolean moveToPrevious() throws JJournalException
      move back one entry.
      Returns:
      true if successful, false if not.
      Throws:
      JJournalException - if this goes really wrong
    • skip

      public long skip(long numEntriesToSkip) throws JJournalException
      skip a number of entries
      Parameters:
      numEntriesToSkip - positive to skip forward, negative to skip backwards.
      Returns:
      difference between requested skip steps, and actual ones. if the move was performed as asked, this is 0. if the move was only partially performed, because the cursor hit the end, this is >0 and the number of "missing skips"
      Throws:
      JJournalException - if the movement failed entirely usage e.g. while (journal.skip(SKIPAHEADVALUE) == 0) ...
    • moveToLatest

      public void moveToLatest() throws JJournalException
      move to last valid entry. (log is ordered chronologically)
      Throws:
      JJournalException - on failure
    • getCursorRaw

      public String getCursorRaw() throws JJournalException
      get current cursor value as String.
      Returns:
      current journal cursor as String.
      Throws:
      JJournalException - on severe failures. "The cursor identifies a journal entry globally and in a stable way" Debugging hint: if you get no cursor on a journal, you may have opened something empty or nonexistent. (e.g. accessing a file as namespace)
    • testCursorRaw

      public boolean testCursorRaw(String cursorValue) throws JJournalException
      test whether current position matches supplied cursor.
      Parameters:
      cursorValue - the cursor to use
      Returns:
      true if valid, false if invalid
      Throws:
      JJournalException - on severe failures.
    • seekCursorRaw

      public void seekCursorRaw(String cursorValue) throws JJournalException
      journal cursor seek function, wrapped for Java.
      Parameters:
      cursorValue - the string value containing the opaque cursor
      Throws:
      JJournalException - if things go seriously wrong
    • seekCursor

      public boolean seekCursor(String cursorValue) throws JJournalException
      move journal access to a provided cursor; going to the first valid entry on that cursor.
      Parameters:
      cursorValue - the cursor to seek=move to
      Returns:
      true if successful, false if not.
      Throws:
      JJournalException - if things go seriously wrong
    • getAvailableFields

      public EnumSet<JournalField> getAvailableFields() throws JJournalException
      list all available fields in current journal, insofar defined in the fields enum. additional fields are ignored - see getAvailableFieldsAsString() for the alternative encompassing these.
      Returns:
      the set of fields defined in this journal
      Throws:
      JJournalException - if things go seriously wrong
    • getAvailableFieldsAsStrings

      public Set<String> getAvailableFieldsAsStrings() throws JJournalException
      list all available fields in current journal, as Strings.
      Returns:
      the set of field names in this journal
      Throws:
      JJournalException - if things go seriously wrong
    • getTimestampAsInstant

      public Instant getTimestampAsInstant() throws JJournalException
      get current journal entry timestamp as Java8 instant
      Returns:
      Instant of the current journal entry
      Throws:
      JJournalException - if things go wrong (e.g. no valid current entry)
    • getTimestampAsMicrosecondsLong

      public long getTimestampAsMicrosecondsLong() throws JJournalException
      get raw microseconds value of the current journal entry.
      Returns:
      a java Long containing the microseconds of the current journal entry.
      Throws:
      JJournalException - if things go wrong (e.g. no valid current entry)
    • getFirstAndLastInstant

      public Period getFirstAndLastInstant() throws JJournalException
      get the timeframe the current journal is covering - its earliest, and its latest entry, specified in microseconds. it's a pity java doesn't have a tuple, nor a proper "Period" class representing an start-to-end value. java.time.Period instead is just a distance vector, just like Duration. I'd call that a naming failure.
      Returns:
      a library class containing the "since" and "until" time
      Throws:
      JJournalException - if things go wrong
    • moveForwardUntilTime

      public boolean moveForwardUntilTime(Instant time) throws JJournalException
      move forward in the journal until a given time is matched.
      Parameters:
      time - the earliest instance the move may stop at.
      Returns:
      true if we arrived at a match, false if we did not (e.g. too late).
      Throws:
      JJournalException - on severe problems. UNTESTED -- TODO: use seek instead, sd_journal_seek_realtime_usec
    • moveBackwardUntilLatestTime

      public boolean moveBackwardUntilLatestTime(Instant time) throws JJournalException
      move backward in the journal until a given time is matched.
      Parameters:
      time - the latest instance the move may stop at. consider moveBackwardUntilEarliestTime if you want the earliest entry
      Returns:
      true if we arrived at a match, false if we did not (e.g. too late).
      Throws:
      JJournalException - on severe problems. UNTESTED -- TODO: use seek instead, sd_journal_seek_realtime_usec
    • moveBackwardUntilEarliestTime

      public boolean moveBackwardUntilEarliestTime(Instant time) throws JJournalException
      moveBackwardUntilEarliestTime move to the first
      Parameters:
      time - the earliest time acceptable
      Returns:
      true if this function could achieve its goal, false if not.
      Throws:
      JJournalException - if there was an error, e.g. journal access failing. UNTESTED
    • foreachInTimerange

      public int foreachInTimerange(Instant startTime, Instant endTime, int limitCount, JournalConsumer<Journal,JJournalException> toBeCalledOnEachEntry) throws JJournalException
      loop over journal entries within a given timeframe. note this does not do a moveToEarliest() by itself (anymore). It starts from current position.
      Parameters:
      startTime - the time of the first entry to operate on, from current position onward. "null" for: do not move. use moveToEarliest(); before calling this function if you want to start from the earliest entry.
      endTime - the time of the first entry the range ended (that is, not processed anymore - one past last) "null" for tail of journal.
      limitCount - abort after this many entries having been processed.
      toBeCalledOnEachEntry - Consumer to be called on each entry.

      If you need to abort the loop, suggested way is to throw a RuntimeException, or an JJournalException. There is also JJournalException(CONSUMER_ENDING_LOOP) for the specific purpose of identifying this.

      Returns:
      number of entries processed in this range. 0 if empty/none found matching.
      Throws:
      JJournalException - on errors, or on Consumer function aborting the loop.
    • hasActiveFilters

      public boolean hasActiveFilters()

      hasActiveFilters.

      Returns:
      a boolean
    • filteringAddFilterExpressionExactMatch

      public void filteringAddFilterExpressionExactMatch(String journalField, String valueToMatch) throws JJournalException
      add a filter for a field to match a String
      Parameters:
      journalField - field to match
      valueToMatch - value to match exactly with
      Throws:
      IllegalArgumentException - if the field name string provided doesn't match an actual field.
      JJournalException - if any.
    • filteringAddFilterExpressionExactMatch

      public void filteringAddFilterExpressionExactMatch(JournalField journalField, String valueToMatch) throws JJournalException
      add a filter for a field to match a String
      Parameters:
      journalField - field to match
      valueToMatch - value to match exactly with
      Throws:
      JJournalException - if any.
    • filteringAddFilterExpressionExactMatch

      public void filteringAddFilterExpressionExactMatch(JournalField journalField, byte[] valueToMatch) throws JJournalException
      add a filter for a field to match a byte sequence
      Parameters:
      journalField - field to match
      valueToMatch - value to match exactly with
      Throws:
      JJournalException - on severe errors
    • filteringAddFilterExpressionExactMatch

      public void filteringAddFilterExpressionExactMatch(String fieldName, byte[] valueToMatch) throws JJournalException
      add a filter for a field name outside the predefined ones.
      Parameters:
      fieldName - field to match. may contain 0-9, A-Z and _ only.
      valueToMatch - value to match exactly with
      Throws:
      JJournalException - if the field name is invalid, or something else went wrong.
    • filteringAddFilterExpressionExactMatchUnchecked

      private void filteringAddFilterExpressionExactMatchUnchecked(String fieldName, byte[] valueToMatch) throws JJournalException
      Throws:
      JJournalException
    • filteringAddDisjunctionOperator

      public void filteringAddDisjunctionOperator() throws JJournalException
      add an "OR" to the filter expression list. It connects all filteringAddFilter* entries added since (a) most recent OR, (b) most recent AND; (c) the start of the filter expression list, whichever comes first, in an logical OR with all matches added afterwards, until (a) next OR (b) next AND (c) the end of filter expression list whichever comes next.
      Throws:
      JJournalException - if this fails, or if it is invoked without and filters defined.
    • filteringAddConjunctionOperator

      public void filteringAddConjunctionOperator() throws JJournalException
      add an "AND" to the filter expression list. It connects all filteringAddFilter* entries added since (a) most recent AND; (b) the start of the filter expression list, whichever comes first, in an logical OR with all matches added afterwards, until (a) next AND (b) the end of filter expression list whichever comes next. This AND operator takes precedence over the OR operator.
      Throws:
      JJournalException - if this fails, or if it is invoked without and filters defined.
    • filteringReset

      public void filteringReset()
      reset the filter list.
    • readFieldAsString

      public String readFieldAsString(String fieldname) throws JJournalException, IllegalArgumentException
      read a field as String
      Parameters:
      fieldname - ID of the field to be read
      Returns:
      field contents (without field name or separating :)
      Throws:
      JJournalException - on failure - e.g. if the field does not exist
      IllegalArgumentException - if the fieldname contains invalid characters note: this reuses the internal buffer, so it is even less thread-safe than other calls.
    • readFieldAsByteArray

      public byte[] readFieldAsByteArray(String fieldname) throws JJournalException, IllegalArgumentException
      read a field as byte[]. The byte array is, as is wasteful Java tradition, allocated for this purpose only.
      Parameters:
      fieldname - ID of the field to be read
      Returns:
      a newly allocated byte array containing the field contents.
      Throws:
      JJournalException - on failure - e.g. if the field does not exist
      IllegalArgumentException - if the fieldname contains invalid characters
    • readFieldToByteArray

      public void readFieldToByteArray(String fieldname, ByteBuffer destinationBuffer) throws JJournalException, IllegalArgumentException
      read a field into existing byte[]. This call retains the fieldname and separator!
      Parameters:
      fieldname - ID of the field to be read
      destinationBuffer - ByteBuffer for storing the result in. caller needs to take care of position, mark, limit etc.
      Throws:
      JJournalException - on failure - e.g. if the field does not exist
      IllegalArgumentException - if the fieldname contains invalid characters
    • readFieldAsString

      public String readFieldAsString(JournalField field) throws JJournalException
      read a field as String
      Parameters:
      field - ID of the field to be read
      Returns:
      a newly allocated byte array containing the field contents.
      Throws:
      JJournalException - on failure - e.g. if the field does not exist
    • readFieldAsByteArray

      public byte[] readFieldAsByteArray(JournalField field) throws JJournalException
      read a field as byte[]. The byte array is, as is wasteful Java tradition, allocated for this purpose only.
      Parameters:
      field - ID of the field to be read
      Returns:
      a newly allocated byte array containing the field contents.
      Throws:
      JJournalException - on failure - e.g. if the field does not exist
    • readFieldAsStringRaw

      public String readFieldAsStringRaw(JournalField field) throws JJournalException
      read a field as String
      Parameters:
      field - a JournalField object
      Returns:
      full contents of the field, including the FIELDNAME header.
      Throws:
      JJournalException - on failure - e.g. if the field does not exist note: this reuses the internal buffer, so it is even less thread-safe than other calls.
    • readFieldsAsStrings

      public void readFieldsAsStrings(List<String> fieldnames, Map<String,String> targetMap) throws JJournalException
      read a complete set of fields.
      Parameters:
      fieldnames - a collection of fields to read. illegal fieldnames cause an exception; non-existing fields will appear, with the value to be set (overwritten) to null.
      targetMap - collection to place the findings in.
      Throws:
      JJournalException - on severe errors.
    • readFieldsAsStrings

      public void readFieldsAsStrings(Set<JournalField> fields, Map<JournalField,String> targetMap) throws JJournalException
      read a complete set of fields, using the JournalField enum.
      Parameters:
      fields - a collection of fields to read. empty fields will get their corresponding values set to null.
      targetMap - collection to place the findings in.
      Throws:
      JJournalException - on severe errors.
    • readFieldAsStringReturnNullOnEmpty

      private String readFieldAsStringReturnNullOnEmpty(String fieldname) throws JJournalException
      Throws:
      JJournalException
    • readDataToBuffer

      public void readDataToBuffer(String fieldName, ByteBuffer buffer) throws JJournalException
      read field data to buffer. Lower level access; you have to take care of the buffer, conversion etc. yourself. This is suitable for accessing binary data. The easier way in most cases is to use readFieldAsString().
      Parameters:
      fieldName - the name of the field to be read, as String
      buffer - the ByteBuffer to read the data into. You have to allocate and prepare (e.g. clear, flip etc) the buffer yourself.
      Throws:
      JJournalException - when things go wrong. org.metabit.platform.interfacing.jjournal.JJournalException BUFFER_TOO_SMALL exception when the message doesn't fit in.
    • getDataSize

      public int getDataSize(String fieldName) throws JJournalException
      how long is that field anyhow?
      Parameters:
      fieldName - field to check for its length
      Returns:
      the size of the field, in bytes.
      Throws:
      JJournalException - on errors, especially invalid field names (and being on an invalid position).
    • readMessageField

      public String readMessageField() throws JJournalException
      convenience function to read MESSAGE field of current journal entry. not threadsafe.
      Returns:
      String containing the journal entry message
      Throws:
      JJournalException - exception on severe errors
    • getBytesUsedByJournalFiles

      public long getBytesUsedByJournalFiles() throws JJournalException
      as the function name says: getBytesUsedByJournalFiles
      Returns:
      long containing the bytes used by journal files
      Throws:
      JJournalException - when things go wrong.
    • hasRuntimeFiles

      public boolean hasRuntimeFiles() throws JJournalException
      check if runtime journal files have been found.
      Returns:
      true if runtime journal files have been found.
      Throws:
      JJournalException - when things go wrong. see directory /run/systemd/journal/
    • hasPersistentFiles

      public boolean hasPersistentFiles() throws JJournalException
      check if persistent journal files have been found.
      Returns:
      true if persistent journal files have been found.
      Throws:
      JJournalException - when things go wrong. see directory /var/log/journal/
    • getJournalTimeZoneOffset

      public ZoneOffset getJournalTimeZoneOffset()

      Getter for the field journalTimeZoneOffset.

      Returns:
      a ZoneOffset object
    • setJournalTimeZoneOffset

      public void setJournalTimeZoneOffset(ZoneOffset journalTimeZoneOffset)

      Setter for the field journalTimeZoneOffset.

      Parameters:
      journalTimeZoneOffset - a ZoneOffset object
    • validateFieldName

      public static void validateFieldName(String journalField) throws JJournalException
      validate field names. returns quietly if everything is ok.
      Parameters:
      journalField - string to be used as a journal field name. Use Uppercase ASCII characters, digits, and underscores only.
      Throws:
      IllegalArgumentException - if the string is refused man sd_journal_send: ...The variable name must be in uppercase and consist only of characters, numbers and underscores, and may not begin with an underscore.[...] A number of well-known fields are defined, see systemd.journal-fields(7) for details, but additional application defined fields may be used. This omits specifying which charset this refers to; so "ÖRTLICHKEIT" would be valid too. But judging from all examples, and the source code, interpretation of this library is that this is *meaning* ASCII uppercase.
      JJournalException
    • turnTwoLongsIntoUUID

      private UUID turnTwoLongsIntoUUID(long high, long low)
    • loadLib

      static void loadLib()