ominfra 0.0.0.dev90__py3-none-any.whl → 0.0.0.dev92__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,7 @@
7
7
  """
8
8
  TODO:
9
9
  - create log group
10
- - log stats - chunk sizes etc
10
+ - log stats - chunk sizes, byte count, num calls, etc
11
11
 
12
12
  ==
13
13
 
@@ -311,6 +311,9 @@ def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
311
311
  # ../../../../../omlish/lite/strings.py
312
312
 
313
313
 
314
+ ##
315
+
316
+
314
317
  def camel_case(name: str, lower: bool = False) -> str:
315
318
  if not name:
316
319
  return ''
@@ -325,6 +328,9 @@ def snake_case(name: str) -> str:
325
328
  return '_'.join([name[l:r].lower() for l, r in zip([None, *uppers], [*uppers, None])]).strip('_')
326
329
 
327
330
 
331
+ ##
332
+
333
+
328
334
  def is_dunder(name: str) -> bool:
329
335
  return (
330
336
  name[:2] == name[-2:] == '__' and
@@ -343,10 +349,31 @@ def is_sunder(name: str) -> bool:
343
349
  )
344
350
 
345
351
 
352
+ ##
353
+
354
+
346
355
  def attr_repr(obj: ta.Any, *attrs: str) -> str:
347
356
  return f'{type(obj).__name__}({", ".join(f"{attr}={getattr(obj, attr)!r}" for attr in attrs)})'
348
357
 
349
358
 
359
+ ##
360
+
361
+
362
+ FORMAT_NUM_BYTES_SUFFIXES: ta.Sequence[str] = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB']
363
+
364
+
365
+ def format_num_bytes(num_bytes: int) -> str:
366
+ for i, suffix in enumerate(FORMAT_NUM_BYTES_SUFFIXES):
367
+ value = num_bytes / 1024 ** i
368
+ if num_bytes < 1024 ** (i + 1):
369
+ if value.is_integer():
370
+ return f'{int(value)}{suffix}'
371
+ else:
372
+ return f'{value:.2f}{suffix}'
373
+
374
+ return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
375
+
376
+
350
377
  ########################################
351
378
  # ../../auth.py
352
379
  """
@@ -1421,129 +1448,6 @@ def check_runtime_version() -> None:
1421
1448
  f'Requires python {REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
1422
1449
 
1423
1450
 
1424
- ########################################
1425
- # ../journald/messages.py
1426
-
1427
-
1428
- @dc.dataclass(frozen=True)
1429
- class JournalctlMessage:
1430
- raw: bytes
1431
- dct: ta.Optional[ta.Mapping[str, ta.Any]] = None
1432
- cursor: ta.Optional[str] = None
1433
- ts_us: ta.Optional[int] = None # microseconds UTC
1434
-
1435
-
1436
- class JournalctlMessageBuilder:
1437
- def __init__(self) -> None:
1438
- super().__init__()
1439
-
1440
- self._buf = DelimitingBuffer(b'\n')
1441
-
1442
- _cursor_field = '__CURSOR'
1443
- _timestamp_field = '_SOURCE_REALTIME_TIMESTAMP'
1444
-
1445
- def _make_message(self, raw: bytes) -> JournalctlMessage:
1446
- dct = None
1447
- cursor = None
1448
- ts = None
1449
-
1450
- try:
1451
- dct = json.loads(raw.decode('utf-8', 'replace'))
1452
- except Exception: # noqa
1453
- log.exception('Failed to parse raw message: %r', raw)
1454
-
1455
- else:
1456
- cursor = dct.get(self._cursor_field)
1457
-
1458
- if tsv := dct.get(self._timestamp_field):
1459
- if isinstance(tsv, str):
1460
- try:
1461
- ts = int(tsv)
1462
- except ValueError:
1463
- try:
1464
- ts = int(float(tsv))
1465
- except ValueError:
1466
- log.exception('Failed to parse timestamp: %r', tsv)
1467
- elif isinstance(tsv, (int, float)):
1468
- ts = int(tsv)
1469
- else:
1470
- log.exception('Invalid timestamp: %r', tsv)
1471
-
1472
- return JournalctlMessage(
1473
- raw=raw,
1474
- dct=dct,
1475
- cursor=cursor,
1476
- ts_us=ts,
1477
- )
1478
-
1479
- def feed(self, data: bytes) -> ta.Sequence[JournalctlMessage]:
1480
- ret: ta.List[JournalctlMessage] = []
1481
- for line in self._buf.feed(data):
1482
- ret.append(self._make_message(check_isinstance(line, bytes))) # type: ignore
1483
- return ret
1484
-
1485
-
1486
- ########################################
1487
- # ../threadworker.py
1488
-
1489
-
1490
- class ThreadWorker(abc.ABC):
1491
- def __init__(
1492
- self,
1493
- *,
1494
- stop_event: ta.Optional[threading.Event] = None,
1495
- ) -> None:
1496
- super().__init__()
1497
-
1498
- if stop_event is None:
1499
- stop_event = threading.Event()
1500
- self._stop_event = stop_event
1501
-
1502
- self._thread: ta.Optional[threading.Thread] = None
1503
-
1504
- self._last_heartbeat: ta.Optional[float] = None
1505
-
1506
- #
1507
-
1508
- def should_stop(self) -> bool:
1509
- return self._stop_event.is_set()
1510
-
1511
- #
1512
-
1513
- @property
1514
- def last_heartbeat(self) -> ta.Optional[float]:
1515
- return self._last_heartbeat
1516
-
1517
- def _heartbeat(self) -> bool:
1518
- self._last_heartbeat = time.time()
1519
-
1520
- if self.should_stop():
1521
- log.info('Stopping: %s', self)
1522
- return False
1523
-
1524
- return True
1525
-
1526
- #
1527
-
1528
- def is_alive(self) -> bool:
1529
- return (thr := self._thread) is not None and thr.is_alive()
1530
-
1531
- def start(self) -> None:
1532
- thr = threading.Thread(target=self._run)
1533
- self._thread = thr
1534
- thr.start()
1535
-
1536
- @abc.abstractmethod
1537
- def _run(self) -> None:
1538
- raise NotImplementedError
1539
-
1540
- def stop(self) -> None:
1541
- raise NotImplementedError
1542
-
1543
- def cleanup(self) -> None: # noqa
1544
- pass
1545
-
1546
-
1547
1451
  ########################################
1548
1452
  # ../../logs.py
1549
1453
  """
@@ -1604,7 +1508,7 @@ class AwsLogMessagePoster:
1604
1508
  - max_items
1605
1509
  - max_bytes - manually build body
1606
1510
  - flush_interval
1607
- - !! sort by timestamp
1511
+ - split sorted chunks if span over 24h
1608
1512
  """
1609
1513
 
1610
1514
  DEFAULT_URL = 'https://logs.{region_name}.amazonaws.com/' # noqa
@@ -1677,7 +1581,7 @@ class AwsLogMessagePoster:
1677
1581
  message=m.message,
1678
1582
  timestamp=m.ts_ms,
1679
1583
  )
1680
- for m in messages
1584
+ for m in sorted(messages, key=lambda m: m.ts_ms)
1681
1585
  ],
1682
1586
  )
1683
1587
 
@@ -1709,6 +1613,142 @@ class AwsLogMessagePoster:
1709
1613
  return [post]
1710
1614
 
1711
1615
 
1616
+ ########################################
1617
+ # ../../../../journald/messages.py
1618
+
1619
+
1620
+ @dc.dataclass(frozen=True)
1621
+ class JournalctlMessage:
1622
+ raw: bytes
1623
+ dct: ta.Optional[ta.Mapping[str, ta.Any]] = None
1624
+ cursor: ta.Optional[str] = None
1625
+ ts_us: ta.Optional[int] = None # microseconds UTC
1626
+
1627
+
1628
+ class JournalctlMessageBuilder:
1629
+ def __init__(self) -> None:
1630
+ super().__init__()
1631
+
1632
+ self._buf = DelimitingBuffer(b'\n')
1633
+
1634
+ _cursor_field = '__CURSOR'
1635
+
1636
+ _timestamp_fields: ta.Sequence[str] = [
1637
+ '_SOURCE_REALTIME_TIMESTAMP',
1638
+ '__REALTIME_TIMESTAMP',
1639
+ ]
1640
+
1641
+ def _get_message_timestamp(self, dct: ta.Mapping[str, ta.Any]) -> ta.Optional[int]:
1642
+ for fld in self._timestamp_fields:
1643
+ if (tsv := dct.get(fld)) is None:
1644
+ continue
1645
+
1646
+ if isinstance(tsv, str):
1647
+ try:
1648
+ return int(tsv)
1649
+ except ValueError:
1650
+ try:
1651
+ return int(float(tsv))
1652
+ except ValueError:
1653
+ log.exception('Failed to parse timestamp: %r', tsv)
1654
+
1655
+ elif isinstance(tsv, (int, float)):
1656
+ return int(tsv)
1657
+
1658
+ log.error('Invalid timestamp: %r', dct)
1659
+ return None
1660
+
1661
+ def _make_message(self, raw: bytes) -> JournalctlMessage:
1662
+ dct = None
1663
+ cursor = None
1664
+ ts = None
1665
+
1666
+ try:
1667
+ dct = json.loads(raw.decode('utf-8', 'replace'))
1668
+ except Exception: # noqa
1669
+ log.exception('Failed to parse raw message: %r', raw)
1670
+
1671
+ else:
1672
+ cursor = dct.get(self._cursor_field)
1673
+ ts = self._get_message_timestamp(dct)
1674
+
1675
+ return JournalctlMessage(
1676
+ raw=raw,
1677
+ dct=dct,
1678
+ cursor=cursor,
1679
+ ts_us=ts,
1680
+ )
1681
+
1682
+ def feed(self, data: bytes) -> ta.Sequence[JournalctlMessage]:
1683
+ ret: ta.List[JournalctlMessage] = []
1684
+ for line in self._buf.feed(data):
1685
+ ret.append(self._make_message(check_isinstance(line, bytes))) # type: ignore
1686
+ return ret
1687
+
1688
+
1689
+ ########################################
1690
+ # ../../../../threadworker.py
1691
+ """
1692
+ TODO:
1693
+ - implement stop lol
1694
+ - collective heartbeat monitoring - ThreadWorkerGroups
1695
+ """
1696
+
1697
+
1698
+ class ThreadWorker(abc.ABC):
1699
+ def __init__(
1700
+ self,
1701
+ *,
1702
+ stop_event: ta.Optional[threading.Event] = None,
1703
+ ) -> None:
1704
+ super().__init__()
1705
+
1706
+ if stop_event is None:
1707
+ stop_event = threading.Event()
1708
+ self._stop_event = stop_event
1709
+
1710
+ self._thread: ta.Optional[threading.Thread] = None
1711
+
1712
+ self._last_heartbeat: ta.Optional[float] = None
1713
+
1714
+ #
1715
+
1716
+ def should_stop(self) -> bool:
1717
+ return self._stop_event.is_set()
1718
+
1719
+ #
1720
+
1721
+ @property
1722
+ def last_heartbeat(self) -> ta.Optional[float]:
1723
+ return self._last_heartbeat
1724
+
1725
+ def _heartbeat(self) -> bool:
1726
+ self._last_heartbeat = time.time()
1727
+
1728
+ if self.should_stop():
1729
+ log.info('Stopping: %s', self)
1730
+ return False
1731
+
1732
+ return True
1733
+
1734
+ #
1735
+
1736
+ def is_alive(self) -> bool:
1737
+ return (thr := self._thread) is not None and thr.is_alive()
1738
+
1739
+ def start(self) -> None:
1740
+ thr = threading.Thread(target=self._run)
1741
+ self._thread = thr
1742
+ thr.start()
1743
+
1744
+ @abc.abstractmethod
1745
+ def _run(self) -> None:
1746
+ raise NotImplementedError
1747
+
1748
+ def stop(self) -> None:
1749
+ raise NotImplementedError
1750
+
1751
+
1712
1752
  ########################################
1713
1753
  # ../../../../../omlish/lite/subprocesses.py
1714
1754
 
@@ -1816,7 +1856,344 @@ def subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
1816
1856
 
1817
1857
 
1818
1858
  ########################################
1819
- # ../journald/tailer.py
1859
+ # ../../../../journald/tailer.py
1860
+ """
1861
+ TODO:
1862
+ - https://www.rootusers.com/how-to-change-log-rate-limiting-in-linux/
1863
+
1864
+ ==
1865
+
1866
+ https://www.freedesktop.org/software/systemd/man/latest/journalctl.html
1867
+
1868
+ Source Options
1869
+ --system, --user :: Show messages from system services and the kernel (with --system). Show messages from service of
1870
+ current user (with --user). If neither is specified, show all messages that the user can see. The
1871
+ --user option affects how --unit= arguments are treated. See --unit=. Note that --user only works if
1872
+ persistent logging is enabled, via the Storage= setting in journald.conf(5).
1873
+ -M, --machine= :: Show messages from a running, local container. Specify a container name to connect to.
1874
+ -m, --merge :: Show entries interleaved from all available journals, including remote ones.
1875
+ -D DIR, --directory=DIR :: Takes a directory path as argument. If specified, journalctl will operate on the specified
1876
+ journal directory DIR instead of the default runtime and system journal paths.
1877
+ -i GLOB, --file=GLOB :: Takes a file glob as an argument. If specified, journalctl will operate on the specified journal
1878
+ files matching GLOB instead of the default runtime and system journal paths. May be specified
1879
+ multiple times, in which case files will be suitably interleaved.
1880
+ --root=ROOT :: Takes a directory path as an argument. If specified, journalctl will operate on journal directories and
1881
+ catalog file hierarchy underneath the specified directory instead of the root directory (e.g.
1882
+ --update-catalog will create ROOT/var/lib/systemd/catalog/database, and journal files under
1883
+ ROOT/run/journal/ or ROOT/var/log/journal/ will be displayed).
1884
+ --image=IMAGE :: Takes a path to a disk image file or block device node. If specified, journalctl will operate on the
1885
+ file system in the indicated disk image. This option is similar to --root=, but operates on file
1886
+ systems stored in disk images or block devices, thus providing an easy way to extract log data from
1887
+ disk images. The disk image should either contain just a file system or a set of file systems within a
1888
+ GPT partition table, following the Discoverable Partitions Specification. For further information on
1889
+ supported disk images, see systemd-nspawn(1)'s switch of the same name.
1890
+ --image-policy=policy :: Takes an image policy string as argument, as per systemd.image-policy(7). The policy is
1891
+ enforced when operating on the disk image specified via --image=, see above. If not specified
1892
+ defaults to the "*" policy, i.e. all recognized file systems in the image are used.
1893
+ --namespace=NAMESPACE :: Takes a journal namespace identifier string as argument. If not specified the data collected by
1894
+ the default namespace is shown. If specified shows the log data of the specified namespace
1895
+ instead. If the namespace is specified as "*" data from all namespaces is shown, interleaved.
1896
+ If the namespace identifier is prefixed with "+" data from the specified namespace and the
1897
+ default namespace is shown, interleaved, but no other. For details about journal namespaces see
1898
+ systemd-journald.service(8).
1899
+
1900
+ Filtering Options
1901
+ -S, --since=, -U, --until= :: Start showing entries on or newer than the specified date, or on or older than the
1902
+ specified date, respectively. Date specifications should be of the format
1903
+ "2012-10-30 18:17:16". If the time part is omitted, "00:00:00" is assumed. If only the
1904
+ seconds component is omitted, ":00" is assumed. If the date component is omitted, the
1905
+ current day is assumed. Alternatively the strings "yesterday", "today", "tomorrow" are
1906
+ understood, which refer to 00:00:00 of the day before the current day, the current day, or
1907
+ the day after the current day, respectively. "now" refers to the current time. Finally,
1908
+ relative times may be specified, prefixed with "-" or "+", referring to times before or
1909
+ after the current time, respectively. For complete time and date specification, see
1910
+ systemd.time(7). Note that --output=short-full prints timestamps that follow precisely
1911
+ this format.
1912
+ -c, --cursor= :: Start showing entries from the location in the journal specified by the passed cursor.
1913
+ --after-cursor= :: Start showing entries from the location in the journal after the location specified by the passed
1914
+ cursor. The cursor is shown when the --show-cursor option is used.
1915
+ --cursor-file=FILE :: If FILE exists and contains a cursor, start showing entries after this location. Otherwise show
1916
+ entries according to the other given options. At the end, write the cursor of the last entry to
1917
+ FILE. Use this option to continually read the journal by sequentially calling journalctl.
1918
+ -b [[ID][±offset]|all], --boot[=[ID][±offset]|all] :: Show messages from a specific boot. This will add a match for
1919
+ "_BOOT_ID=". The argument may be empty, in which case logs for the
1920
+ current boot will be shown. If the boot ID is omitted, a positive
1921
+ offset will look up the boots starting from the beginning of the
1922
+ journal, and an equal-or-less-than zero offset will look up boots
1923
+ starting from the end of the journal. Thus, 1 means the first boot
1924
+ found in the journal in chronological order, 2 the second and so
1925
+ on; while -0 is the last boot, -1 the boot before last, and so on.
1926
+ An empty offset is equivalent to specifying -0, except when the
1927
+ current boot is not the last boot (e.g. because --directory= was
1928
+ specified to look at logs from a different machine). If the
1929
+ 32-character ID is specified, it may optionally be followed by
1930
+ offset which identifies the boot relative to the one given by boot
1931
+ ID. Negative values mean earlier boots and positive values mean
1932
+ later boots. If offset is not specified, a value of zero is
1933
+ assumed, and the logs for the boot given by ID are shown. The
1934
+ special argument all can be used to negate the effect of an
1935
+ earlier use of -b.
1936
+ -u, --unit=UNIT|PATTERN :: Show messages for the specified systemd unit UNIT (such as a service unit), or for any of the
1937
+ units matched by PATTERN. If a pattern is specified, a list of unit names found in the
1938
+ journal is compared with the specified pattern and all that match are used. For each unit
1939
+ name, a match is added for messages from the unit ("_SYSTEMD_UNIT=UNIT"), along with
1940
+ additional matches for messages from systemd and messages about coredumps for the specified
1941
+ unit. A match is also added for "_SYSTEMD_SLICE=UNIT", such that if the provided UNIT is a
1942
+ systemd.slice(5) unit, all logs of children of the slice will be shown. With --user, all
1943
+ --unit= arguments will be converted to match user messages as if specified with --user-unit=.
1944
+ This parameter can be specified multiple times.
1945
+ --user-unit= :: Show messages for the specified user session unit. This will add a match for messages from the unit
1946
+ ("_SYSTEMD_USER_UNIT=" and "_UID=") and additional matches for messages from session systemd and
1947
+ messages about coredumps for the specified unit. A match is also added for "_SYSTEMD_USER_SLICE=UNIT",
1948
+ such that if the provided UNIT is a systemd.slice(5) unit, all logs of children of the unit will be
1949
+ shown. This parameter can be specified multiple times.
1950
+ -t, --identifier=SYSLOG_IDENTIFIER :: Show messages for the specified syslog identifier SYSLOG_IDENTIFIER. This
1951
+ parameter can be specified multiple times.
1952
+ -T, --exclude-identifier=SYSLOG_IDENTIFIER :: Exclude messages for the specified syslog identifier SYSLOG_IDENTIFIER.
1953
+ This parameter can be specified multiple times.
1954
+ -p, --priority= :: Filter output by message priorities or priority ranges. Takes either a single numeric or textual log
1955
+ level (i.e. between 0/"emerg" and 7/"debug"), or a range of numeric/text log levels in the form
1956
+ FROM..TO. The log levels are the usual syslog log levels as documented in syslog(3), i.e. "emerg"
1957
+ (0), "alert" (1), "crit" (2), "err" (3), "warning" (4), "notice" (5), "info" (6), "debug" (7). If a
1958
+ single log level is specified, all messages with this log level or a lower (hence more important) log
1959
+ level are shown. If a range is specified, all messages within the range are shown, including both the
1960
+ start and the end value of the range. This will add "PRIORITY=" matches for the specified priorities.
1961
+ --facility= :: Filter output by syslog facility. Takes a comma-separated list of numbers or facility names. The names
1962
+ are the usual syslog facilities as documented in syslog(3). --facility=help may be used to display a list
1963
+ of known facility names and exit.
1964
+ -g, --grep= :: Filter output to entries where the MESSAGE= field matches the specified regular expression.
1965
+ PERL-compatible regular expressions are used, see pcre2pattern(3) for a detailed description of the
1966
+ syntax. If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case
1967
+ sensitive. This can be overridden with the --case-sensitive option, see below. When used with --lines=
1968
+ (not prefixed with "+"), --reverse is implied.
1969
+ --case-sensitive[=BOOLEAN] :: Make pattern matching case sensitive or case insensitive.
1970
+ -k, --dmesg :: Show only kernel messages. This implies -b and adds the match "_TRANSPORT=kernel".
1971
+
1972
+ Output Options
1973
+ -o, --output= :: Controls the formatting of the journal entries that are shown. Takes one of the following options:
1974
+ short :: is the default and generates an output that is mostly identical to the formatting of classic syslog files,
1975
+ showing one line per journal entry.
1976
+ short-full :: is very similar, but shows timestamps in the format the --since= and --until= options accept. Unlike the
1977
+ timestamp information shown in short output mode this mode includes weekday, year and timezone
1978
+ information in the output, and is locale-independent.
1979
+ short-iso :: is very similar, but shows timestamps in the RFC 3339 profile of ISO 8601.
1980
+ short-iso-precise :: as for short-iso but includes full microsecond precision.
1981
+ short-precise :: is very similar, but shows classic syslog timestamps with full microsecond precision.
1982
+ short-monotonic :: is very similar, but shows monotonic timestamps instead of wallclock timestamps.
1983
+ short-delta :: as for short-monotonic but includes the time difference to the previous entry. Maybe unreliable time
1984
+ differences are marked by a "*".
1985
+ short-unix :: is very similar, but shows seconds passed since January 1st 1970 UTC instead of wallclock timestamps
1986
+ ("UNIX time"). The time is shown with microsecond accuracy.
1987
+ verbose :: shows the full-structured entry items with all fields.
1988
+ export :: serializes the journal into a binary (but mostly text-based) stream suitable for backups and network
1989
+ transfer (see Journal Export Format for more information). To import the binary stream back into native
1990
+ journald format use systemd-journal-remote(8).
1991
+ json :: formats entries as JSON objects, separated by newline characters (see Journal JSON Format for more
1992
+ information). Field values are generally encoded as JSON strings, with three exceptions: Fields larger than
1993
+ 4096 bytes are encoded as null values. (This may be turned off by passing --all, but be aware that this may
1994
+ allocate overly long JSON objects.) Journal entries permit non-unique fields within the same log entry. JSON
1995
+ does not allow non-unique fields within objects. Due to this, if a non-unique field is encountered a JSON
1996
+ array is used as field value, listing all field values as elements. Fields containing non-printable or
1997
+ non-UTF8 bytes are encoded as arrays containing the raw bytes individually formatted as unsigned numbers. Note
1998
+ that this encoding is reversible (with the exception of the size limit).
1999
+ json-pretty :: formats entries as JSON data structures, but formats them in multiple lines in order to make them more
2000
+ readable by humans.
2001
+ json-sse :: formats entries as JSON data structures, but wraps them in a format suitable for Server-Sent Events.
2002
+ json-seq :: formats entries as JSON data structures, but prefixes them with an ASCII Record Separator character (0x1E)
2003
+ and suffixes them with an ASCII Line Feed character (0x0A), in accordance with JavaScript Object Notation
2004
+ (JSON) Text Sequences ("application/json-seq").
2005
+ cat :: generates a very terse output, only showing the actual message of each journal entry with no metadata, not even
2006
+ a timestamp. If combined with the --output-fields= option will output the listed fields for each log record,
2007
+ instead of the message.
2008
+ with-unit :: similar to short-full, but prefixes the unit and user unit names instead of the traditional syslog
2009
+ identifier. Useful when using templated instances, as it will include the arguments in the unit names.
2010
+ --truncate-newline :: Truncate each log message at the first newline character on output, so that only the first line of
2011
+ each message is displayed.
2012
+ --output-fields= :: A comma separated list of the fields which should be included in the output. This has an effect only
2013
+ for the output modes which would normally show all fields (verbose, export, json, json-pretty,
2014
+ json-sse and json-seq), as well as on cat. For the former, the "__CURSOR", "__REALTIME_TIMESTAMP",
2015
+ "__MONOTONIC_TIMESTAMP", and "_BOOT_ID" fields are always printed.
2016
+ -n, --lines= :: Show the most recent journal events and limit the number of events shown. The argument is a positive
2017
+ integer or "all" to disable the limit. Additionally, if the number is prefixed with "+", the oldest
2018
+ journal events are used instead. The default value is 10 if no argument is given. If --follow is used,
2019
+ this option is implied. When not prefixed with "+" and used with --grep=, --reverse is implied.
2020
+ -r, --reverse :: Reverse output so that the newest entries are displayed first.
2021
+ --show-cursor :: The cursor is shown after the last entry after two dashes:
2022
+ -- cursor: s=0639… :: The format of the cursor is private and subject to change.
2023
+ --utc :: Express time in Coordinated Universal Time (UTC).
2024
+ -x, --catalog :: Augment log lines with explanation texts from the message catalog. This will add explanatory help texts
2025
+ to log messages in the output where this is available. These short help texts will explain the context
2026
+ of an error or log event, possible solutions, as well as pointers to support forums, developer
2027
+ documentation, and any other relevant manuals. Note that help texts are not available for all messages,
2028
+ but only for selected ones. For more information on the message catalog, see Journal Message Catalogs.
2029
+ Note: when attaching journalctl output to bug reports, please do not use -x.
2030
+ --no-hostname :: Don't show the hostname field of log messages originating from the local host. This switch has an
2031
+ effect only on the short family of output modes (see above). Note: this option does not remove
2032
+ occurrences of the hostname from log entries themselves, so it does not prevent the hostname from being
2033
+ visible in the logs.
2034
+ --no-full, --full, -l :: Ellipsize fields when they do not fit in available columns. The default is to show full fields,
2035
+ allowing them to wrap or be truncated by the pager, if one is used. The old options -l/--full
2036
+ are not useful anymore, except to undo --no-full.
2037
+ -a, --all :: Show all fields in full, even if they include unprintable characters or are very long. By default, fields
2038
+ with unprintable characters are abbreviated as "blob data". (Note that the pager may escape unprintable
2039
+ characters again.)
2040
+ -f, --follow :: Show only the most recent journal entries, and continuously print new entries as they are appended to
2041
+ the journal.
2042
+ --no-tail :: Show all stored output lines, even in follow mode. Undoes the effect of --lines=.
2043
+ -q, --quiet :: Suppresses all informational messages (i.e. "-- Journal begins at …", "-- Reboot --"), any warning
2044
+ messages regarding inaccessible system journals when run as a normal user.
2045
+
2046
+ Pager Control Options
2047
+ --no-pager :: Do not pipe output into a pager.
2048
+ -e, --pager-end :: Immediately jump to the end of the journal inside the implied pager tool. This implies -n1000 to
2049
+ guarantee that the pager will not buffer logs of unbounded size. This may be overridden with an
2050
+ explicit -n with some other numeric value, while -nall will disable this cap. Note that this option
2051
+ is only supported for the less(1) pager.
2052
+
2053
+ Forward Secure Sealing (FSS) Options
2054
+ --interval= :: Specifies the change interval for the sealing key when generating an FSS key pair with --setup-keys.
2055
+ Shorter intervals increase CPU consumption but shorten the time range of undetectable journal
2056
+ alterations. Defaults to 15min.
2057
+ --verify-key= :: Specifies the FSS verification key to use for the --verify operation.
2058
+ --force :: When --setup-keys is passed and Forward Secure Sealing (FSS) has already been configured, recreate FSS keys.
2059
+
2060
+ Commands
2061
+ -N, --fields :: Print all field names currently used in all entries of the journal.
2062
+ -F, --field= :: Print all possible data values the specified field can take in all entries of the journal.
2063
+ --list-boots :: Show a tabular list of boot numbers (relative to the current boot), their IDs, and the timestamps of the
2064
+ first and last message pertaining to the boot. When specified with -n/--lines=[+]N option, only the
2065
+ first (when the number prefixed with "+") or the last (without prefix) N entries will be shown. When
2066
+ specified with -r/--reverse, the list will be shown in the reverse order.
2067
+ --disk-usage :: Shows the current disk usage of all journal files. This shows the sum of the disk usage of all archived
2068
+ and active journal files.
2069
+ --vacuum-size=, --vacuum-time=, --vacuum-files=
2070
+ --vacuum-size= :: removes the oldest archived journal files until the disk space they use falls below the specified
2071
+ size. Accepts the usual "K", "M", "G" and "T" suffixes (to the base of 1024).
2072
+ --vacuum-time= :: removes archived journal files older than the specified timespan. Accepts the usual "s" (default),
2073
+ "m", "h", "days", "weeks", "months", and "years" suffixes, see systemd.time(7) for details.
2074
+ --vacuum-files= :: leaves only the specified number of separate journal files.
2075
+ Note that running --vacuum-size= has only an indirect effect on the output shown by --disk-usage, as the latter
2076
+ includes active journal files, while the vacuuming operation only operates on archived journal files. Similarly,
2077
+ --vacuum-files= might not actually reduce the number of journal files to below the specified number, as it will not
2078
+ remove active journal files.
2079
+ --vacuum-size=, --vacuum-time= and --vacuum-files= may be combined in a single invocation to enforce any combination
2080
+ of a size, a time and a number of files limit on the archived journal files. Specifying any of these three parameters
2081
+ as zero is equivalent to not enforcing the specific limit, and is thus redundant.
2082
+ These three switches may also be combined with --rotate into one command. If so, all active files are rotated first,
2083
+ and the requested vacuuming operation is executed right after. The rotation has the effect that all currently active
2084
+ files are archived (and potentially new, empty journal files opened as replacement), and hence the vacuuming operation
2085
+ has the greatest effect as it can take all log data written so far into account.
2086
+ --verify :: Check the journal file for internal consistency. If the file has been generated with FSS enabled and the FSS
2087
+ verification key has been specified with --verify-key=, authenticity of the journal file is verified.
2088
+ --sync :: Asks the journal daemon to write all yet unwritten journal data to the backing file system and synchronize all
2089
+ journals. This call does not return until the synchronization operation is complete. This command guarantees
2090
+ that any log messages written before its invocation are safely stored on disk at the time it returns.
2091
+ --relinquish-var :: Asks the journal daemon for the reverse operation to --flush: if requested the daemon will write
2092
+ further log data to /run/log/journal/ and stops writing to /var/log/journal/. A subsequent call to
2093
+ --flush causes the log output to switch back to /var/log/journal/, see above.
2094
+ --smart-relinquish-var :: Similar to --relinquish-var, but executes no operation if the root file system and
2095
+ /var/log/journal/ reside on the same mount point. This operation is used during system
2096
+ shutdown in order to make the journal daemon stop writing data to /var/log/journal/ in case
2097
+ that directory is located on a mount point that needs to be unmounted.
2098
+ --flush :: Asks the journal daemon to flush any log data stored in /run/log/journal/ into /var/log/journal/, if
2099
+ persistent storage is enabled. This call does not return until the operation is complete. Note that this call
2100
+ is idempotent: the data is only flushed from /run/log/journal/ into /var/log/journal/ once during system
2101
+ runtime (but see --relinquish-var below), and this command exits cleanly without executing any operation if
2102
+ this has already happened. This command effectively guarantees that all data is flushed to /var/log/journal/
2103
+ at the time it returns.
2104
+ --rotate :: Asks the journal daemon to rotate journal files. This call does not return until the rotation operation is
2105
+ complete. Journal file rotation has the effect that all currently active journal files are marked as
2106
+ archived and renamed, so that they are never written to in future. New (empty) journal files are then
2107
+ created in their place. This operation may be combined with --vacuum-size=, --vacuum-time= and
2108
+ --vacuum-file= into a single command, see above.
2109
+ --header :: Instead of showing journal contents, show internal header information of the journal fields accessed. This
2110
+ option is particularly useful when trying to identify out-of-order journal entries, as happens for example
2111
+ when the machine is booted with the wrong system time.
2112
+ --list-catalog [128-bit-ID…] :: List the contents of the message catalog as a table of message IDs, plus their short
2113
+ description strings. If any 128-bit-IDs are specified, only those entries are shown.
2114
+ --dump-catalog [128-bit-ID…] :: Show the contents of the message catalog, with entries separated by a line consisting of
2115
+ two dashes and the ID (the format is the same as .catalog files). If any 128-bit-IDs are
2116
+ specified, only those entries are shown.
2117
+ --update-catalog :: Update the message catalog index. This command needs to be executed each time new catalog files are
2118
+ installed, removed, or updated to rebuild the binary catalog index.
2119
+ --setup-keys :: Instead of showing journal contents, generate a new key pair for Forward Secure Sealing (FSS). This will
2120
+ generate a sealing key and a verification key. The sealing key is stored in the journal data directory
2121
+ and shall remain on the host. The verification key should be stored externally. Refer to the Seal=
2122
+ option in journald.conf(5) for information on Forward Secure Sealing and for a link to a refereed
2123
+ scholarly paper detailing the cryptographic theory it is based on.
2124
+ -h, --help :: Print a short help text and exit.
2125
+ --version :: Print a short version string and exit.
2126
+
2127
+ Environment
2128
+ $SYSTEMD_LOG_LEVEL :: The maximum log level of emitted messages (messages with a higher log level, i.e. less important
2129
+ ones, will be suppressed). Takes a comma-separated list of values. A value may be either one of
2130
+ (in order of decreasing importance) emerg, alert, crit, err, warning, notice, info, debug, or an
2131
+ integer in the range 0…7. See syslog(3) for more information. Each value may optionally be
2132
+ prefixed with one of console, syslog, kmsg or journal followed by a colon to set the maximum log
2133
+ level for that specific log target (e.g. SYSTEMD_LOG_LEVEL=debug,console:info specifies to log at
2134
+ debug level except when logging to the console which should be at info level). Note that the
2135
+ global maximum log level takes priority over any per target maximum log levels.
2136
+ $SYSTEMD_LOG_COLOR :: A boolean. If true, messages written to the tty will be colored according to priority. This
2137
+ setting is only useful when messages are written directly to the terminal, because journalctl(1)
2138
+ and other tools that display logs will color messages based on the log level on their own.
2139
+ $SYSTEMD_LOG_TIME :: A boolean. If true, console log messages will be prefixed with a timestamp. This setting is only
2140
+ useful when messages are written directly to the terminal or a file, because journalctl(1) and
2141
+ other tools that display logs will attach timestamps based on the entry metadata on their own.
2142
+ $SYSTEMD_LOG_LOCATION :: A boolean. If true, messages will be prefixed with a filename and line number in the source
2143
+ code where the message originates. Note that the log location is often attached as metadata to
2144
+ journal entries anyway. Including it directly in the message text can nevertheless be
2145
+ convenient when debugging programs.
2146
+ $SYSTEMD_LOG_TID :: A boolean. If true, messages will be prefixed with the current numerical thread ID (TID). Note that
2147
+ the this information is attached as metadata to journal entries anyway. Including it directly in the
2148
+ message text can nevertheless be convenient when debugging programs.
2149
+ $SYSTEMD_LOG_TARGET :: The destination for log messages. One of console (log to the attached tty), console-prefixed (log
2150
+ to the attached tty but with prefixes encoding the log level and "facility", see syslog(3), kmsg
2151
+ (log to the kernel circular log buffer), journal (log to the journal), journal-or-kmsg (log to
2152
+ the journal if available, and to kmsg otherwise), auto (determine the appropriate log target
2153
+ automatically, the default), null (disable log output).
2154
+ $SYSTEMD_LOG_RATELIMIT_KMSG :: Whether to ratelimit kmsg or not. Takes a boolean. Defaults to "true". If disabled,
2155
+ systemd will not ratelimit messages written to kmsg.
2156
+ $SYSTEMD_PAGER :: Pager to use when --no-pager is not given; overrides $PAGER. If neither $SYSTEMD_PAGER nor $PAGER are
2157
+ set, a set of well-known pager implementations are tried in turn, including less(1) and more(1), until
2158
+ one is found. If no pager implementation is discovered no pager is invoked. Setting this environment
2159
+ variable to an empty string or the value "cat" is equivalent to passing --no-pager. Note: if
2160
+ $SYSTEMD_PAGERSECURE is not set, $SYSTEMD_PAGER (as well as $PAGER) will be silently ignored.
2161
+ $SYSTEMD_LESS :: Override the options passed to less (by default "FRSXMK"). Users might want to change two options in
2162
+ particular:
2163
+ K :: This option instructs the pager to exit immediately when Ctrl+C is pressed. To allow less to handle Ctrl+C itself
2164
+ to switch back to the pager command prompt, unset this option. If the value of $SYSTEMD_LESS does not include
2165
+ "K", and the pager that is invoked is less, Ctrl+C will be ignored by the executable, and needs to be handled by
2166
+ the pager.
2167
+ X :: This option instructs the pager to not send termcap initialization and deinitialization strings to the terminal.
2168
+ It is set by default to allow command output to remain visible in the terminal even after the pager exits.
2169
+ Nevertheless, this prevents some pager functionality from working, in particular paged output cannot be scrolled
2170
+ with the mouse. Note that setting the regular $LESS environment variable has no effect for less invocations by
2171
+ systemd tools.
2172
+ $SYSTEMD_LESSCHARSET :: Override the charset passed to less (by default "utf-8", if the invoking terminal is determined
2173
+ to be UTF-8 compatible). Note that setting the regular $LESSCHARSET environment variable has no
2174
+ effect for less invocations by systemd tools.
2175
+ $SYSTEMD_PAGERSECURE :: Takes a boolean argument. When true, the "secure" mode of the pager is enabled; if false,
2176
+ disabled. If $SYSTEMD_PAGERSECURE is not set at all, secure mode is enabled if the effective UID
2177
+ is not the same as the owner of the login session, see geteuid(2) and sd_pid_get_owner_uid(3).
2178
+ In secure mode, LESSSECURE=1 will be set when invoking the pager, and the pager shall disable
2179
+ commands that open or create new files or start new subprocesses. When $SYSTEMD_PAGERSECURE is
2180
+ not set at all, pagers which are not known to implement secure mode will not be used. (Currently
2181
+ only less(1) implements secure mode.) Note: when commands are invoked with elevated privileges,
2182
+ for example under sudo(8) or pkexec(1), care must be taken to ensure that unintended interactive
2183
+ features are not enabled. "Secure" mode for the pager may be enabled automatically as describe
2184
+ above. Setting SYSTEMD_PAGERSECURE=0 or not removing it from the inherited environment allows
2185
+ the user to invoke arbitrary commands. Note that if the $SYSTEMD_PAGER or $PAGER variables are
2186
+ to be honoured, $SYSTEMD_PAGERSECURE must be set too. It might be reasonable to completely
2187
+ disable the pager using --no-pager instead.
2188
+ $SYSTEMD_COLORS :: Takes a boolean argument. When true, systemd and related utilities will use colors in their output,
2189
+ otherwise the output will be monochrome. Additionally, the variable can take one of the following
2190
+ special values: "16", "256" to restrict the use of colors to the base 16 or 256 ANSI colors,
2191
+ respectively. This can be specified to override the automatic decision based on $TERM and what the
2192
+ console is connected to.
2193
+ $SYSTEMD_URLIFY :: The value must be a boolean. Controls whether clickable links should be generated in the output for
2194
+ terminal emulators supporting this. This can be specified to override the decision that systemd makes
2195
+ based on $TERM and other conditions.
2196
+ """
1820
2197
 
1821
2198
 
1822
2199
  class JournalctlTailerWorker(ThreadWorker):
@@ -1887,11 +2264,11 @@ class JournalctlTailerWorker(ThreadWorker):
1887
2264
 
1888
2265
  while True:
1889
2266
  if not self._heartbeat():
1890
- break
2267
+ return
1891
2268
 
1892
2269
  while stdout.readable():
1893
2270
  if not self._heartbeat():
1894
- break
2271
+ return
1895
2272
 
1896
2273
  buf = stdout.read(self._read_size)
1897
2274
  if not buf:
@@ -1901,11 +2278,18 @@ class JournalctlTailerWorker(ThreadWorker):
1901
2278
  log.debug('Journalctl read buffer: %r', buf)
1902
2279
  msgs = self._mb.feed(buf)
1903
2280
  if msgs:
1904
- self._output.put(msgs)
2281
+ while True:
2282
+ try:
2283
+ self._output.put(msgs, timeout=1.)
2284
+ except queue.Full:
2285
+ if not self._heartbeat():
2286
+ return
2287
+ else:
2288
+ break
1905
2289
 
1906
2290
  if self._proc.poll() is not None:
1907
2291
  log.critical('Journalctl process terminated')
1908
- break
2292
+ return
1909
2293
 
1910
2294
  log.debug('Journalctl readable')
1911
2295
  time.sleep(self._sleep_s)
@@ -2039,15 +2423,18 @@ class JournalctlToAws:
2039
2423
 
2040
2424
  @cached_nullary
2041
2425
  def _journalctl_tailer_worker(self) -> JournalctlTailerWorker:
2042
- ac: ta.Optional[str] = self._config.journalctl_after_cursor
2043
- if ac is None:
2044
- ac = self._read_cursor_file()
2045
- if ac is not None:
2046
- log.info('Starting from cursor %s', ac)
2426
+ ac: ta.Optional[str] = None
2047
2427
 
2048
2428
  if (since := self._config.journalctl_since):
2049
2429
  log.info('Starting since %s', since)
2050
2430
 
2431
+ else:
2432
+ ac = self._config.journalctl_after_cursor
2433
+ if ac is None:
2434
+ ac = self._read_cursor_file()
2435
+ if ac is not None:
2436
+ log.info('Starting from cursor %s', ac)
2437
+
2051
2438
  return JournalctlTailerWorker(
2052
2439
  self._journalctl_message_queue(),
2053
2440
 
@@ -2063,9 +2450,9 @@ class JournalctlToAws:
2063
2450
  def run(self) -> None:
2064
2451
  self._ensure_locked()
2065
2452
 
2066
- q = self._journalctl_message_queue()
2067
- jtw = self._journalctl_tailer_worker()
2068
- mp = self._aws_log_message_poster()
2453
+ q = self._journalctl_message_queue() # type: queue.Queue[ta.Sequence[JournalctlMessage]]
2454
+ jtw = self._journalctl_tailer_worker() # type: JournalctlTailerWorker
2455
+ mp = self._aws_log_message_poster() # type: AwsLogMessagePoster
2069
2456
 
2070
2457
  jtw.start()
2071
2458
 
@@ -2075,7 +2462,13 @@ class JournalctlToAws:
2075
2462
  log.critical('Journalctl tailer worker died')
2076
2463
  break
2077
2464
 
2078
- msgs: ta.Sequence[JournalctlMessage] = q.get()
2465
+ try:
2466
+ msgs: ta.Sequence[JournalctlMessage] = q.get(timeout=1.)
2467
+ except queue.Empty:
2468
+ msgs = []
2469
+ if not msgs:
2470
+ continue
2471
+
2079
2472
  log.debug('%r', msgs)
2080
2473
 
2081
2474
  cur_cursor: ta.Optional[str] = None
@@ -2088,10 +2481,14 @@ class JournalctlToAws:
2088
2481
  log.warning('Empty queue chunk')
2089
2482
  continue
2090
2483
 
2091
- [post] = mp.feed([mp.Message(
2092
- message=json.dumps(m.dct),
2093
- ts_ms=int(time.time() * 1000.),
2094
- ) for m in msgs])
2484
+ feed_msgs = []
2485
+ for m in msgs:
2486
+ feed_msgs.append(mp.Message(
2487
+ message=json.dumps(m.dct, sort_keys=True),
2488
+ ts_ms=int((m.ts_us / 1000.) if m.ts_us is not None else (time.time() * 1000.)),
2489
+ ))
2490
+
2491
+ [post] = mp.feed(feed_msgs)
2095
2492
  log.debug('%r', post)
2096
2493
 
2097
2494
  if not self._config.dry_run:
@@ -2149,7 +2546,7 @@ def _main() -> None:
2149
2546
  if not args.real:
2150
2547
  config = dc.replace(config, journalctl_cmd=[
2151
2548
  sys.executable,
2152
- os.path.join(os.path.dirname(__file__), 'journald', 'genmessages.py'),
2549
+ os.path.join(os.path.dirname(__file__), '..', '..', '..', 'journald', 'genmessages.py'),
2153
2550
  '--sleep-n', '2',
2154
2551
  '--sleep-s', '.5',
2155
2552
  *(['--message', args.message] if args.message else []),
@@ -2158,9 +2555,13 @@ def _main() -> None:
2158
2555
 
2159
2556
  #
2160
2557
 
2161
- for a in ['after_cursor', 'since', 'dry_run']:
2162
- if (pa := getattr(args, a)):
2163
- config = dc.replace(config, **{a: pa})
2558
+ for ca, pa in [
2559
+ ('journalctl_after_cursor', 'after_cursor'),
2560
+ ('journalctl_since', 'since'),
2561
+ ('dry_run', 'dry_run'),
2562
+ ]:
2563
+ if (av := getattr(args, pa)):
2564
+ config = dc.replace(config, **{ca: av})
2164
2565
 
2165
2566
  #
2166
2567