Skip to content

Commit

Permalink
journal_print.py allow user passing fields
Browse files Browse the repository at this point in the history
Allow users to pass fields among other options.
  • Loading branch information
jtmoon79 committed Apr 23, 2023
1 parent bd28bd9 commit 3c5a18a
Showing 1 changed file with 105 additions and 54 deletions.
159 changes: 105 additions & 54 deletions tools/journal_print.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#!/usr/bin/env python3
#
# file: print_journal_events.py
# -*- coding: utf-8 -*-
# vim: set fileencoding=utf-8 :
# file: print_journal_events.py
#
# helper tool to double-check .journal file entries found using
# python-systemd:journal.Reader

__doc__: str = \
"""\
__doc__: str = """\
Print all events from .journal file in the order returned by
`python-systemd:journal.Reader.get_next`, which itself calls
`libsytemd` API `sd_journal_next`.
Expand All @@ -16,22 +15,11 @@
`libsystemd.so` is used.
"""

import argparse
import os
from typing import Optional
from typing import List, Optional
import sys
try:
from systemd import journal
except ImportError as err:
print(err, file=sys.stderr)
print("Install on Ubuntu with:\n sudo apt install python3-systemd", file=sys.stderr)
print("Or with pip:\n pip install systemd-python", file=sys.stderr)
sys.exit(1)

# TODO allow passing field names to print, overrides default field name tuple
# use argparse to do this.

# default Journal File
JOURNAL_FILE = "./logs/programs/journal/user-1000.journal"
KEY_MONOTONIC_TIMESTAMP = "__MONOTONIC_TIMESTAMP"
KEY_SOURCE_REALTIME_TIMESTAMP = "_SOURCE_REALTIME_TIMESTAMP"
KEY__REALTIME_TIMESTAMP = "__REALTIME_TIMESTAMP"
Expand All @@ -40,29 +28,89 @@
SEP = "|"

if __name__ == "__main__":
file_: str = JOURNAL_FILE
time_: Optional[str] = None
# primitive argument parsing
do_help = False
if len(sys.argv) > 1:
file_ = sys.argv[1]
if file_ in ("-h", "--help", "-?"):
do_help = True
if len(sys.argv) > 2:
time_ = sys.argv[2]
if len(sys.argv) > 3 or len(sys.argv) <= 1 or do_help:
print(f"""Usage:
{os.path.basename(__file__ )} journal_file [time]
parser = argparse.ArgumentParser(
description=__doc__,
prog=os.path.basename(__file__),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
# TODO: how to both show defaults and preserve newlines in help message?
epilog=(
"""Works well when piped to column.
where journal_file is the path to a .journal file.
where time is from Unix epoch in microseconds.
"""
+ os.path.basename(__file__)
+ """ -j /var/log/journal/$(hostname)/system.journal | column -t -s '|'"""
),
)
parser.add_argument(
"--journal-file",
"-j",
type=str,
required=True,
)
parser.add_argument(
"fields",
metavar="field",
type=str,
nargs="*",
default=",".join(
(
KEY_MONOTONIC_TIMESTAMP,
KEY_SOURCE_REALTIME_TIMESTAMP,
KEY__REALTIME_TIMESTAMP,
KEY_MESSAGE_ID,
KEY_MESSAGE,
)
),
help="print these journal entry fields separated by commas",
)
parser.add_argument(
"--time",
"-t",
type=int,
default=None,
help="Unix epoch in microseconds to start printing from",
)
parser.add_argument(
"--oneline",
"-o",
type=bool,
default=False,
help="Strip newlines from MESSAGE field",
)
parser.add_argument(
"--separator",
"-s",
type=str,
default=SEP,
help="Separator between fields",
)

args = parser.parse_args()

About:
""", file=sys.stderr)
print(__doc__, file=sys.stderr)
# late import to allow user to see `--help` message without this error
try:
from systemd import journal
except ImportError as err:
print(err, file=sys.stderr)
print(
"Install on Ubuntu with:\n sudo apt install python3-systemd",
file=sys.stderr,
)
print("Or with pip:\n pip install systemd-python", file=sys.stderr)
sys.exit(1)

reader = journal.Reader(files=[file_])
journal_file: str = args.journal_file
fields: List = []
if isinstance(args.fields, str):
fields += args.fields.split(",")
elif isinstance(args.fields, list):
for field in args.fields:
fields += field.split(",")
time_: Optional[str] = args.time
oneline: bool = args.oneline
sep = args.separator

reader = journal.Reader(files=[journal_file])

if time_ is not None:
t: int = int(time_)
Expand All @@ -72,29 +120,32 @@

try:
print(f"index", end="")
print(f"{SEP}{KEY_MONOTONIC_TIMESTAMP}", end="")
print(f"{SEP}{KEY_SOURCE_REALTIME_TIMESTAMP}", end="")
print(f"{SEP}{KEY__REALTIME_TIMESTAMP}", end="")
print(f"{SEP}{KEY_MESSAGE_ID}", end="")
print(f"{SEP}{KEY_MESSAGE}")
for field in fields:
print(f"{sep}{field}", end="")
print()
sys.stdout.flush()
i = 0
while entry := reader.get_next():
i += 1
mt_ts = entry.get(KEY_MONOTONIC_TIMESTAMP, "")
mt_ts = str(mt_ts.timestamp)
srt_ts = entry.get(KEY_SOURCE_REALTIME_TIMESTAMP, " ")
rt_ts = entry.get(KEY__REALTIME_TIMESTAMP, " ")
mesg_id = entry.get(KEY_MESSAGE_ID, " ")
mesg_id = str(mesg_id).replace("-", "")
message = entry.get(KEY_MESSAGE, " ")
message = message.replace("\n", " ")
print(f"{i}", end="")
print(f"{SEP}{mt_ts}", end="")
print(f"{SEP}{srt_ts}", end="")
print(f"{SEP}{rt_ts}", end="")
print(f"""{SEP}{mesg_id}""", end="")
print(f"{SEP}{message}")
for field in fields:
# special handling for monotonic timestamp
if field == KEY_MONOTONIC_TIMESTAMP:
mt_ts = entry.get(KEY_MONOTONIC_TIMESTAMP, " ")
mt_ts = str(mt_ts.timestamp)
print(f"{sep}{mt_ts}", end="")
else:
# defalt print at least a space to piping stdout to
# `column -t -s '|'` will correctly parse
value = entry.get(field, " ")
# replace some control characters with glyph representations
if oneline:
value = value.replace("\0", "␀")
value = value.replace("\n", "␤")
value = value.replace("\1", "␁")
value = value.replace("\r", "␊")
print(f"{sep}{value}", end="")
print()
sys.stdout.flush()
except BrokenPipeError:
# this occurs when piping to `head`
Expand Down

0 comments on commit 3c5a18a

Please sign in to comment.