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

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,7 +4,7 @@
4
4
  """
5
5
  TODO:
6
6
  - create log group
7
- - log stats - chunk sizes etc
7
+ - log stats - chunk sizes, byte count, num calls, etc
8
8
 
9
9
  ==
10
10
 
@@ -53,11 +53,11 @@ from omlish.lite.marshal import unmarshal_obj
53
53
  from omlish.lite.pidfile import Pidfile
54
54
  from omlish.lite.runtime import is_debugger_attached
55
55
 
56
+ from ....journald.messages import JournalctlMessage # noqa
57
+ from ....journald.tailer import JournalctlTailerWorker
56
58
  from ..auth import AwsSigner
57
59
  from ..logs import AwsLogMessagePoster
58
60
  from ..logs import AwsPutLogEventsResponse
59
- from .journald.messages import JournalctlMessage # noqa
60
- from .journald.tailer import JournalctlTailerWorker
61
61
 
62
62
 
63
63
  @dc.dataclass(frozen=True)
@@ -184,15 +184,18 @@ class JournalctlToAws:
184
184
 
185
185
  @cached_nullary
186
186
  def _journalctl_tailer_worker(self) -> JournalctlTailerWorker:
187
- ac: ta.Optional[str] = self._config.journalctl_after_cursor
188
- if ac is None:
189
- ac = self._read_cursor_file()
190
- if ac is not None:
191
- log.info('Starting from cursor %s', ac)
187
+ ac: ta.Optional[str] = None
192
188
 
193
189
  if (since := self._config.journalctl_since):
194
190
  log.info('Starting since %s', since)
195
191
 
192
+ else:
193
+ ac = self._config.journalctl_after_cursor
194
+ if ac is None:
195
+ ac = self._read_cursor_file()
196
+ if ac is not None:
197
+ log.info('Starting from cursor %s', ac)
198
+
196
199
  return JournalctlTailerWorker(
197
200
  self._journalctl_message_queue(),
198
201
 
@@ -208,9 +211,9 @@ class JournalctlToAws:
208
211
  def run(self) -> None:
209
212
  self._ensure_locked()
210
213
 
211
- q = self._journalctl_message_queue()
212
- jtw = self._journalctl_tailer_worker()
213
- mp = self._aws_log_message_poster()
214
+ q = self._journalctl_message_queue() # type: queue.Queue[ta.Sequence[JournalctlMessage]]
215
+ jtw = self._journalctl_tailer_worker() # type: JournalctlTailerWorker
216
+ mp = self._aws_log_message_poster() # type: AwsLogMessagePoster
214
217
 
215
218
  jtw.start()
216
219
 
@@ -220,7 +223,13 @@ class JournalctlToAws:
220
223
  log.critical('Journalctl tailer worker died')
221
224
  break
222
225
 
223
- msgs: ta.Sequence[JournalctlMessage] = q.get()
226
+ try:
227
+ msgs: ta.Sequence[JournalctlMessage] = q.get(timeout=1.)
228
+ except queue.Empty:
229
+ msgs = []
230
+ if not msgs:
231
+ continue
232
+
224
233
  log.debug('%r', msgs)
225
234
 
226
235
  cur_cursor: ta.Optional[str] = None
@@ -233,10 +242,14 @@ class JournalctlToAws:
233
242
  log.warning('Empty queue chunk')
234
243
  continue
235
244
 
236
- [post] = mp.feed([mp.Message(
237
- message=json.dumps(m.dct),
238
- ts_ms=int(time.time() * 1000.),
239
- ) for m in msgs])
245
+ feed_msgs = []
246
+ for m in msgs:
247
+ feed_msgs.append(mp.Message(
248
+ message=json.dumps(m.dct, sort_keys=True),
249
+ ts_ms=int((m.ts_us / 1000.) if m.ts_us is not None else (time.time() * 1000.)),
250
+ ))
251
+
252
+ [post] = mp.feed(feed_msgs)
240
253
  log.debug('%r', post)
241
254
 
242
255
  if not self._config.dry_run:
@@ -303,9 +316,13 @@ def _main() -> None:
303
316
 
304
317
  #
305
318
 
306
- for a in ['after_cursor', 'since', 'dry_run']:
307
- if (pa := getattr(args, a)):
308
- config = dc.replace(config, **{a: pa})
319
+ for ca, pa in [
320
+ ('journalctl_after_cursor', 'after_cursor'),
321
+ ('journalctl_since', 'since'),
322
+ ('dry_run', 'dry_run'),
323
+ ]:
324
+ if (av := getattr(args, pa)):
325
+ config = dc.replace(config, **{ca: av})
309
326
 
310
327
  #
311
328
 
@@ -68,7 +68,7 @@ class AwsLogMessagePoster:
68
68
  - max_items
69
69
  - max_bytes - manually build body
70
70
  - flush_interval
71
- - !! sort by timestamp
71
+ - split sorted chunks if span over 24h
72
72
  """
73
73
 
74
74
  DEFAULT_URL = 'https://logs.{region_name}.amazonaws.com/' # noqa
@@ -141,7 +141,7 @@ class AwsLogMessagePoster:
141
141
  message=m.message,
142
142
  timestamp=m.ts_ms,
143
143
  )
144
- for m in messages
144
+ for m in sorted(messages, key=lambda m: m.ts_ms)
145
145
  ],
146
146
  )
147
147
 
File without changes
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  import dataclasses as dc
3
4
  import json
4
5
  import typing as ta
@@ -23,7 +24,31 @@ class JournalctlMessageBuilder:
23
24
  self._buf = DelimitingBuffer(b'\n')
24
25
 
25
26
  _cursor_field = '__CURSOR'
26
- _timestamp_field = '_SOURCE_REALTIME_TIMESTAMP'
27
+
28
+ _timestamp_fields: ta.Sequence[str] = [
29
+ '_SOURCE_REALTIME_TIMESTAMP',
30
+ '__REALTIME_TIMESTAMP',
31
+ ]
32
+
33
+ def _get_message_timestamp(self, dct: ta.Mapping[str, ta.Any]) -> ta.Optional[int]:
34
+ for fld in self._timestamp_fields:
35
+ if (tsv := dct.get(fld)) is None:
36
+ continue
37
+
38
+ if isinstance(tsv, str):
39
+ try:
40
+ return int(tsv)
41
+ except ValueError:
42
+ try:
43
+ return int(float(tsv))
44
+ except ValueError:
45
+ log.exception('Failed to parse timestamp: %r', tsv)
46
+
47
+ elif isinstance(tsv, (int, float)):
48
+ return int(tsv)
49
+
50
+ log.error('Invalid timestamp: %r', dct)
51
+ return None
27
52
 
28
53
  def _make_message(self, raw: bytes) -> JournalctlMessage:
29
54
  dct = None
@@ -37,20 +62,7 @@ class JournalctlMessageBuilder:
37
62
 
38
63
  else:
39
64
  cursor = dct.get(self._cursor_field)
40
-
41
- if tsv := dct.get(self._timestamp_field):
42
- if isinstance(tsv, str):
43
- try:
44
- ts = int(tsv)
45
- except ValueError:
46
- try:
47
- ts = int(float(tsv))
48
- except ValueError:
49
- log.exception('Failed to parse timestamp: %r', tsv)
50
- elif isinstance(tsv, (int, float)):
51
- ts = int(tsv)
52
- else:
53
- log.exception('Invalid timestamp: %r', tsv)
65
+ ts = self._get_message_timestamp(dct)
54
66
 
55
67
  return JournalctlMessage(
56
68
  raw=raw,
@@ -0,0 +1,453 @@
1
+ # ruff: noqa: UP007
2
+ # @omlish-lite
3
+ """
4
+ TODO:
5
+ - https://www.rootusers.com/how-to-change-log-rate-limiting-in-linux/
6
+
7
+ ==
8
+
9
+ https://www.freedesktop.org/software/systemd/man/latest/journalctl.html
10
+
11
+ Source Options
12
+ --system, --user :: Show messages from system services and the kernel (with --system). Show messages from service of
13
+ current user (with --user). If neither is specified, show all messages that the user can see. The
14
+ --user option affects how --unit= arguments are treated. See --unit=. Note that --user only works if
15
+ persistent logging is enabled, via the Storage= setting in journald.conf(5).
16
+ -M, --machine= :: Show messages from a running, local container. Specify a container name to connect to.
17
+ -m, --merge :: Show entries interleaved from all available journals, including remote ones.
18
+ -D DIR, --directory=DIR :: Takes a directory path as argument. If specified, journalctl will operate on the specified
19
+ journal directory DIR instead of the default runtime and system journal paths.
20
+ -i GLOB, --file=GLOB :: Takes a file glob as an argument. If specified, journalctl will operate on the specified journal
21
+ files matching GLOB instead of the default runtime and system journal paths. May be specified
22
+ multiple times, in which case files will be suitably interleaved.
23
+ --root=ROOT :: Takes a directory path as an argument. If specified, journalctl will operate on journal directories and
24
+ catalog file hierarchy underneath the specified directory instead of the root directory (e.g.
25
+ --update-catalog will create ROOT/var/lib/systemd/catalog/database, and journal files under
26
+ ROOT/run/journal/ or ROOT/var/log/journal/ will be displayed).
27
+ --image=IMAGE :: Takes a path to a disk image file or block device node. If specified, journalctl will operate on the
28
+ file system in the indicated disk image. This option is similar to --root=, but operates on file
29
+ systems stored in disk images or block devices, thus providing an easy way to extract log data from
30
+ disk images. The disk image should either contain just a file system or a set of file systems within a
31
+ GPT partition table, following the Discoverable Partitions Specification. For further information on
32
+ supported disk images, see systemd-nspawn(1)'s switch of the same name.
33
+ --image-policy=policy :: Takes an image policy string as argument, as per systemd.image-policy(7). The policy is
34
+ enforced when operating on the disk image specified via --image=, see above. If not specified
35
+ defaults to the "*" policy, i.e. all recognized file systems in the image are used.
36
+ --namespace=NAMESPACE :: Takes a journal namespace identifier string as argument. If not specified the data collected by
37
+ the default namespace is shown. If specified shows the log data of the specified namespace
38
+ instead. If the namespace is specified as "*" data from all namespaces is shown, interleaved.
39
+ If the namespace identifier is prefixed with "+" data from the specified namespace and the
40
+ default namespace is shown, interleaved, but no other. For details about journal namespaces see
41
+ systemd-journald.service(8).
42
+
43
+ Filtering Options
44
+ -S, --since=, -U, --until= :: Start showing entries on or newer than the specified date, or on or older than the
45
+ specified date, respectively. Date specifications should be of the format
46
+ "2012-10-30 18:17:16". If the time part is omitted, "00:00:00" is assumed. If only the
47
+ seconds component is omitted, ":00" is assumed. If the date component is omitted, the
48
+ current day is assumed. Alternatively the strings "yesterday", "today", "tomorrow" are
49
+ understood, which refer to 00:00:00 of the day before the current day, the current day, or
50
+ the day after the current day, respectively. "now" refers to the current time. Finally,
51
+ relative times may be specified, prefixed with "-" or "+", referring to times before or
52
+ after the current time, respectively. For complete time and date specification, see
53
+ systemd.time(7). Note that --output=short-full prints timestamps that follow precisely
54
+ this format.
55
+ -c, --cursor= :: Start showing entries from the location in the journal specified by the passed cursor.
56
+ --after-cursor= :: Start showing entries from the location in the journal after the location specified by the passed
57
+ cursor. The cursor is shown when the --show-cursor option is used.
58
+ --cursor-file=FILE :: If FILE exists and contains a cursor, start showing entries after this location. Otherwise show
59
+ entries according to the other given options. At the end, write the cursor of the last entry to
60
+ FILE. Use this option to continually read the journal by sequentially calling journalctl.
61
+ -b [[ID][±offset]|all], --boot[=[ID][±offset]|all] :: Show messages from a specific boot. This will add a match for
62
+ "_BOOT_ID=". The argument may be empty, in which case logs for the
63
+ current boot will be shown. If the boot ID is omitted, a positive
64
+ offset will look up the boots starting from the beginning of the
65
+ journal, and an equal-or-less-than zero offset will look up boots
66
+ starting from the end of the journal. Thus, 1 means the first boot
67
+ found in the journal in chronological order, 2 the second and so
68
+ on; while -0 is the last boot, -1 the boot before last, and so on.
69
+ An empty offset is equivalent to specifying -0, except when the
70
+ current boot is not the last boot (e.g. because --directory= was
71
+ specified to look at logs from a different machine). If the
72
+ 32-character ID is specified, it may optionally be followed by
73
+ offset which identifies the boot relative to the one given by boot
74
+ ID. Negative values mean earlier boots and positive values mean
75
+ later boots. If offset is not specified, a value of zero is
76
+ assumed, and the logs for the boot given by ID are shown. The
77
+ special argument all can be used to negate the effect of an
78
+ earlier use of -b.
79
+ -u, --unit=UNIT|PATTERN :: Show messages for the specified systemd unit UNIT (such as a service unit), or for any of the
80
+ units matched by PATTERN. If a pattern is specified, a list of unit names found in the
81
+ journal is compared with the specified pattern and all that match are used. For each unit
82
+ name, a match is added for messages from the unit ("_SYSTEMD_UNIT=UNIT"), along with
83
+ additional matches for messages from systemd and messages about coredumps for the specified
84
+ unit. A match is also added for "_SYSTEMD_SLICE=UNIT", such that if the provided UNIT is a
85
+ systemd.slice(5) unit, all logs of children of the slice will be shown. With --user, all
86
+ --unit= arguments will be converted to match user messages as if specified with --user-unit=.
87
+ This parameter can be specified multiple times.
88
+ --user-unit= :: Show messages for the specified user session unit. This will add a match for messages from the unit
89
+ ("_SYSTEMD_USER_UNIT=" and "_UID=") and additional matches for messages from session systemd and
90
+ messages about coredumps for the specified unit. A match is also added for "_SYSTEMD_USER_SLICE=UNIT",
91
+ such that if the provided UNIT is a systemd.slice(5) unit, all logs of children of the unit will be
92
+ shown. This parameter can be specified multiple times.
93
+ -t, --identifier=SYSLOG_IDENTIFIER :: Show messages for the specified syslog identifier SYSLOG_IDENTIFIER. This
94
+ parameter can be specified multiple times.
95
+ -T, --exclude-identifier=SYSLOG_IDENTIFIER :: Exclude messages for the specified syslog identifier SYSLOG_IDENTIFIER.
96
+ This parameter can be specified multiple times.
97
+ -p, --priority= :: Filter output by message priorities or priority ranges. Takes either a single numeric or textual log
98
+ level (i.e. between 0/"emerg" and 7/"debug"), or a range of numeric/text log levels in the form
99
+ FROM..TO. The log levels are the usual syslog log levels as documented in syslog(3), i.e. "emerg"
100
+ (0), "alert" (1), "crit" (2), "err" (3), "warning" (4), "notice" (5), "info" (6), "debug" (7). If a
101
+ single log level is specified, all messages with this log level or a lower (hence more important) log
102
+ level are shown. If a range is specified, all messages within the range are shown, including both the
103
+ start and the end value of the range. This will add "PRIORITY=" matches for the specified priorities.
104
+ --facility= :: Filter output by syslog facility. Takes a comma-separated list of numbers or facility names. The names
105
+ are the usual syslog facilities as documented in syslog(3). --facility=help may be used to display a list
106
+ of known facility names and exit.
107
+ -g, --grep= :: Filter output to entries where the MESSAGE= field matches the specified regular expression.
108
+ PERL-compatible regular expressions are used, see pcre2pattern(3) for a detailed description of the
109
+ syntax. If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case
110
+ sensitive. This can be overridden with the --case-sensitive option, see below. When used with --lines=
111
+ (not prefixed with "+"), --reverse is implied.
112
+ --case-sensitive[=BOOLEAN] :: Make pattern matching case sensitive or case insensitive.
113
+ -k, --dmesg :: Show only kernel messages. This implies -b and adds the match "_TRANSPORT=kernel".
114
+
115
+ Output Options
116
+ -o, --output= :: Controls the formatting of the journal entries that are shown. Takes one of the following options:
117
+ short :: is the default and generates an output that is mostly identical to the formatting of classic syslog files,
118
+ showing one line per journal entry.
119
+ short-full :: is very similar, but shows timestamps in the format the --since= and --until= options accept. Unlike the
120
+ timestamp information shown in short output mode this mode includes weekday, year and timezone
121
+ information in the output, and is locale-independent.
122
+ short-iso :: is very similar, but shows timestamps in the RFC 3339 profile of ISO 8601.
123
+ short-iso-precise :: as for short-iso but includes full microsecond precision.
124
+ short-precise :: is very similar, but shows classic syslog timestamps with full microsecond precision.
125
+ short-monotonic :: is very similar, but shows monotonic timestamps instead of wallclock timestamps.
126
+ short-delta :: as for short-monotonic but includes the time difference to the previous entry. Maybe unreliable time
127
+ differences are marked by a "*".
128
+ short-unix :: is very similar, but shows seconds passed since January 1st 1970 UTC instead of wallclock timestamps
129
+ ("UNIX time"). The time is shown with microsecond accuracy.
130
+ verbose :: shows the full-structured entry items with all fields.
131
+ export :: serializes the journal into a binary (but mostly text-based) stream suitable for backups and network
132
+ transfer (see Journal Export Format for more information). To import the binary stream back into native
133
+ journald format use systemd-journal-remote(8).
134
+ json :: formats entries as JSON objects, separated by newline characters (see Journal JSON Format for more
135
+ information). Field values are generally encoded as JSON strings, with three exceptions: Fields larger than
136
+ 4096 bytes are encoded as null values. (This may be turned off by passing --all, but be aware that this may
137
+ allocate overly long JSON objects.) Journal entries permit non-unique fields within the same log entry. JSON
138
+ does not allow non-unique fields within objects. Due to this, if a non-unique field is encountered a JSON
139
+ array is used as field value, listing all field values as elements. Fields containing non-printable or
140
+ non-UTF8 bytes are encoded as arrays containing the raw bytes individually formatted as unsigned numbers. Note
141
+ that this encoding is reversible (with the exception of the size limit).
142
+ json-pretty :: formats entries as JSON data structures, but formats them in multiple lines in order to make them more
143
+ readable by humans.
144
+ json-sse :: formats entries as JSON data structures, but wraps them in a format suitable for Server-Sent Events.
145
+ json-seq :: formats entries as JSON data structures, but prefixes them with an ASCII Record Separator character (0x1E)
146
+ and suffixes them with an ASCII Line Feed character (0x0A), in accordance with JavaScript Object Notation
147
+ (JSON) Text Sequences ("application/json-seq").
148
+ cat :: generates a very terse output, only showing the actual message of each journal entry with no metadata, not even
149
+ a timestamp. If combined with the --output-fields= option will output the listed fields for each log record,
150
+ instead of the message.
151
+ with-unit :: similar to short-full, but prefixes the unit and user unit names instead of the traditional syslog
152
+ identifier. Useful when using templated instances, as it will include the arguments in the unit names.
153
+ --truncate-newline :: Truncate each log message at the first newline character on output, so that only the first line of
154
+ each message is displayed.
155
+ --output-fields= :: A comma separated list of the fields which should be included in the output. This has an effect only
156
+ for the output modes which would normally show all fields (verbose, export, json, json-pretty,
157
+ json-sse and json-seq), as well as on cat. For the former, the "__CURSOR", "__REALTIME_TIMESTAMP",
158
+ "__MONOTONIC_TIMESTAMP", and "_BOOT_ID" fields are always printed.
159
+ -n, --lines= :: Show the most recent journal events and limit the number of events shown. The argument is a positive
160
+ integer or "all" to disable the limit. Additionally, if the number is prefixed with "+", the oldest
161
+ journal events are used instead. The default value is 10 if no argument is given. If --follow is used,
162
+ this option is implied. When not prefixed with "+" and used with --grep=, --reverse is implied.
163
+ -r, --reverse :: Reverse output so that the newest entries are displayed first.
164
+ --show-cursor :: The cursor is shown after the last entry after two dashes:
165
+ -- cursor: s=0639… :: The format of the cursor is private and subject to change.
166
+ --utc :: Express time in Coordinated Universal Time (UTC).
167
+ -x, --catalog :: Augment log lines with explanation texts from the message catalog. This will add explanatory help texts
168
+ to log messages in the output where this is available. These short help texts will explain the context
169
+ of an error or log event, possible solutions, as well as pointers to support forums, developer
170
+ documentation, and any other relevant manuals. Note that help texts are not available for all messages,
171
+ but only for selected ones. For more information on the message catalog, see Journal Message Catalogs.
172
+ Note: when attaching journalctl output to bug reports, please do not use -x.
173
+ --no-hostname :: Don't show the hostname field of log messages originating from the local host. This switch has an
174
+ effect only on the short family of output modes (see above). Note: this option does not remove
175
+ occurrences of the hostname from log entries themselves, so it does not prevent the hostname from being
176
+ visible in the logs.
177
+ --no-full, --full, -l :: Ellipsize fields when they do not fit in available columns. The default is to show full fields,
178
+ allowing them to wrap or be truncated by the pager, if one is used. The old options -l/--full
179
+ are not useful anymore, except to undo --no-full.
180
+ -a, --all :: Show all fields in full, even if they include unprintable characters or are very long. By default, fields
181
+ with unprintable characters are abbreviated as "blob data". (Note that the pager may escape unprintable
182
+ characters again.)
183
+ -f, --follow :: Show only the most recent journal entries, and continuously print new entries as they are appended to
184
+ the journal.
185
+ --no-tail :: Show all stored output lines, even in follow mode. Undoes the effect of --lines=.
186
+ -q, --quiet :: Suppresses all informational messages (i.e. "-- Journal begins at …", "-- Reboot --"), any warning
187
+ messages regarding inaccessible system journals when run as a normal user.
188
+
189
+ Pager Control Options
190
+ --no-pager :: Do not pipe output into a pager.
191
+ -e, --pager-end :: Immediately jump to the end of the journal inside the implied pager tool. This implies -n1000 to
192
+ guarantee that the pager will not buffer logs of unbounded size. This may be overridden with an
193
+ explicit -n with some other numeric value, while -nall will disable this cap. Note that this option
194
+ is only supported for the less(1) pager.
195
+
196
+ Forward Secure Sealing (FSS) Options
197
+ --interval= :: Specifies the change interval for the sealing key when generating an FSS key pair with --setup-keys.
198
+ Shorter intervals increase CPU consumption but shorten the time range of undetectable journal
199
+ alterations. Defaults to 15min.
200
+ --verify-key= :: Specifies the FSS verification key to use for the --verify operation.
201
+ --force :: When --setup-keys is passed and Forward Secure Sealing (FSS) has already been configured, recreate FSS keys.
202
+
203
+ Commands
204
+ -N, --fields :: Print all field names currently used in all entries of the journal.
205
+ -F, --field= :: Print all possible data values the specified field can take in all entries of the journal.
206
+ --list-boots :: Show a tabular list of boot numbers (relative to the current boot), their IDs, and the timestamps of the
207
+ first and last message pertaining to the boot. When specified with -n/--lines=[+]N option, only the
208
+ first (when the number prefixed with "+") or the last (without prefix) N entries will be shown. When
209
+ specified with -r/--reverse, the list will be shown in the reverse order.
210
+ --disk-usage :: Shows the current disk usage of all journal files. This shows the sum of the disk usage of all archived
211
+ and active journal files.
212
+ --vacuum-size=, --vacuum-time=, --vacuum-files=
213
+ --vacuum-size= :: removes the oldest archived journal files until the disk space they use falls below the specified
214
+ size. Accepts the usual "K", "M", "G" and "T" suffixes (to the base of 1024).
215
+ --vacuum-time= :: removes archived journal files older than the specified timespan. Accepts the usual "s" (default),
216
+ "m", "h", "days", "weeks", "months", and "years" suffixes, see systemd.time(7) for details.
217
+ --vacuum-files= :: leaves only the specified number of separate journal files.
218
+ Note that running --vacuum-size= has only an indirect effect on the output shown by --disk-usage, as the latter
219
+ includes active journal files, while the vacuuming operation only operates on archived journal files. Similarly,
220
+ --vacuum-files= might not actually reduce the number of journal files to below the specified number, as it will not
221
+ remove active journal files.
222
+ --vacuum-size=, --vacuum-time= and --vacuum-files= may be combined in a single invocation to enforce any combination
223
+ of a size, a time and a number of files limit on the archived journal files. Specifying any of these three parameters
224
+ as zero is equivalent to not enforcing the specific limit, and is thus redundant.
225
+ These three switches may also be combined with --rotate into one command. If so, all active files are rotated first,
226
+ and the requested vacuuming operation is executed right after. The rotation has the effect that all currently active
227
+ files are archived (and potentially new, empty journal files opened as replacement), and hence the vacuuming operation
228
+ has the greatest effect as it can take all log data written so far into account.
229
+ --verify :: Check the journal file for internal consistency. If the file has been generated with FSS enabled and the FSS
230
+ verification key has been specified with --verify-key=, authenticity of the journal file is verified.
231
+ --sync :: Asks the journal daemon to write all yet unwritten journal data to the backing file system and synchronize all
232
+ journals. This call does not return until the synchronization operation is complete. This command guarantees
233
+ that any log messages written before its invocation are safely stored on disk at the time it returns.
234
+ --relinquish-var :: Asks the journal daemon for the reverse operation to --flush: if requested the daemon will write
235
+ further log data to /run/log/journal/ and stops writing to /var/log/journal/. A subsequent call to
236
+ --flush causes the log output to switch back to /var/log/journal/, see above.
237
+ --smart-relinquish-var :: Similar to --relinquish-var, but executes no operation if the root file system and
238
+ /var/log/journal/ reside on the same mount point. This operation is used during system
239
+ shutdown in order to make the journal daemon stop writing data to /var/log/journal/ in case
240
+ that directory is located on a mount point that needs to be unmounted.
241
+ --flush :: Asks the journal daemon to flush any log data stored in /run/log/journal/ into /var/log/journal/, if
242
+ persistent storage is enabled. This call does not return until the operation is complete. Note that this call
243
+ is idempotent: the data is only flushed from /run/log/journal/ into /var/log/journal/ once during system
244
+ runtime (but see --relinquish-var below), and this command exits cleanly without executing any operation if
245
+ this has already happened. This command effectively guarantees that all data is flushed to /var/log/journal/
246
+ at the time it returns.
247
+ --rotate :: Asks the journal daemon to rotate journal files. This call does not return until the rotation operation is
248
+ complete. Journal file rotation has the effect that all currently active journal files are marked as
249
+ archived and renamed, so that they are never written to in future. New (empty) journal files are then
250
+ created in their place. This operation may be combined with --vacuum-size=, --vacuum-time= and
251
+ --vacuum-file= into a single command, see above.
252
+ --header :: Instead of showing journal contents, show internal header information of the journal fields accessed. This
253
+ option is particularly useful when trying to identify out-of-order journal entries, as happens for example
254
+ when the machine is booted with the wrong system time.
255
+ --list-catalog [128-bit-ID…] :: List the contents of the message catalog as a table of message IDs, plus their short
256
+ description strings. If any 128-bit-IDs are specified, only those entries are shown.
257
+ --dump-catalog [128-bit-ID…] :: Show the contents of the message catalog, with entries separated by a line consisting of
258
+ two dashes and the ID (the format is the same as .catalog files). If any 128-bit-IDs are
259
+ specified, only those entries are shown.
260
+ --update-catalog :: Update the message catalog index. This command needs to be executed each time new catalog files are
261
+ installed, removed, or updated to rebuild the binary catalog index.
262
+ --setup-keys :: Instead of showing journal contents, generate a new key pair for Forward Secure Sealing (FSS). This will
263
+ generate a sealing key and a verification key. The sealing key is stored in the journal data directory
264
+ and shall remain on the host. The verification key should be stored externally. Refer to the Seal=
265
+ option in journald.conf(5) for information on Forward Secure Sealing and for a link to a refereed
266
+ scholarly paper detailing the cryptographic theory it is based on.
267
+ -h, --help :: Print a short help text and exit.
268
+ --version :: Print a short version string and exit.
269
+
270
+ Environment
271
+ $SYSTEMD_LOG_LEVEL :: The maximum log level of emitted messages (messages with a higher log level, i.e. less important
272
+ ones, will be suppressed). Takes a comma-separated list of values. A value may be either one of
273
+ (in order of decreasing importance) emerg, alert, crit, err, warning, notice, info, debug, or an
274
+ integer in the range 0…7. See syslog(3) for more information. Each value may optionally be
275
+ prefixed with one of console, syslog, kmsg or journal followed by a colon to set the maximum log
276
+ level for that specific log target (e.g. SYSTEMD_LOG_LEVEL=debug,console:info specifies to log at
277
+ debug level except when logging to the console which should be at info level). Note that the
278
+ global maximum log level takes priority over any per target maximum log levels.
279
+ $SYSTEMD_LOG_COLOR :: A boolean. If true, messages written to the tty will be colored according to priority. This
280
+ setting is only useful when messages are written directly to the terminal, because journalctl(1)
281
+ and other tools that display logs will color messages based on the log level on their own.
282
+ $SYSTEMD_LOG_TIME :: A boolean. If true, console log messages will be prefixed with a timestamp. This setting is only
283
+ useful when messages are written directly to the terminal or a file, because journalctl(1) and
284
+ other tools that display logs will attach timestamps based on the entry metadata on their own.
285
+ $SYSTEMD_LOG_LOCATION :: A boolean. If true, messages will be prefixed with a filename and line number in the source
286
+ code where the message originates. Note that the log location is often attached as metadata to
287
+ journal entries anyway. Including it directly in the message text can nevertheless be
288
+ convenient when debugging programs.
289
+ $SYSTEMD_LOG_TID :: A boolean. If true, messages will be prefixed with the current numerical thread ID (TID). Note that
290
+ the this information is attached as metadata to journal entries anyway. Including it directly in the
291
+ message text can nevertheless be convenient when debugging programs.
292
+ $SYSTEMD_LOG_TARGET :: The destination for log messages. One of console (log to the attached tty), console-prefixed (log
293
+ to the attached tty but with prefixes encoding the log level and "facility", see syslog(3), kmsg
294
+ (log to the kernel circular log buffer), journal (log to the journal), journal-or-kmsg (log to
295
+ the journal if available, and to kmsg otherwise), auto (determine the appropriate log target
296
+ automatically, the default), null (disable log output).
297
+ $SYSTEMD_LOG_RATELIMIT_KMSG :: Whether to ratelimit kmsg or not. Takes a boolean. Defaults to "true". If disabled,
298
+ systemd will not ratelimit messages written to kmsg.
299
+ $SYSTEMD_PAGER :: Pager to use when --no-pager is not given; overrides $PAGER. If neither $SYSTEMD_PAGER nor $PAGER are
300
+ set, a set of well-known pager implementations are tried in turn, including less(1) and more(1), until
301
+ one is found. If no pager implementation is discovered no pager is invoked. Setting this environment
302
+ variable to an empty string or the value "cat" is equivalent to passing --no-pager. Note: if
303
+ $SYSTEMD_PAGERSECURE is not set, $SYSTEMD_PAGER (as well as $PAGER) will be silently ignored.
304
+ $SYSTEMD_LESS :: Override the options passed to less (by default "FRSXMK"). Users might want to change two options in
305
+ particular:
306
+ K :: This option instructs the pager to exit immediately when Ctrl+C is pressed. To allow less to handle Ctrl+C itself
307
+ to switch back to the pager command prompt, unset this option. If the value of $SYSTEMD_LESS does not include
308
+ "K", and the pager that is invoked is less, Ctrl+C will be ignored by the executable, and needs to be handled by
309
+ the pager.
310
+ X :: This option instructs the pager to not send termcap initialization and deinitialization strings to the terminal.
311
+ It is set by default to allow command output to remain visible in the terminal even after the pager exits.
312
+ Nevertheless, this prevents some pager functionality from working, in particular paged output cannot be scrolled
313
+ with the mouse. Note that setting the regular $LESS environment variable has no effect for less invocations by
314
+ systemd tools.
315
+ $SYSTEMD_LESSCHARSET :: Override the charset passed to less (by default "utf-8", if the invoking terminal is determined
316
+ to be UTF-8 compatible). Note that setting the regular $LESSCHARSET environment variable has no
317
+ effect for less invocations by systemd tools.
318
+ $SYSTEMD_PAGERSECURE :: Takes a boolean argument. When true, the "secure" mode of the pager is enabled; if false,
319
+ disabled. If $SYSTEMD_PAGERSECURE is not set at all, secure mode is enabled if the effective UID
320
+ is not the same as the owner of the login session, see geteuid(2) and sd_pid_get_owner_uid(3).
321
+ In secure mode, LESSSECURE=1 will be set when invoking the pager, and the pager shall disable
322
+ commands that open or create new files or start new subprocesses. When $SYSTEMD_PAGERSECURE is
323
+ not set at all, pagers which are not known to implement secure mode will not be used. (Currently
324
+ only less(1) implements secure mode.) Note: when commands are invoked with elevated privileges,
325
+ for example under sudo(8) or pkexec(1), care must be taken to ensure that unintended interactive
326
+ features are not enabled. "Secure" mode for the pager may be enabled automatically as describe
327
+ above. Setting SYSTEMD_PAGERSECURE=0 or not removing it from the inherited environment allows
328
+ the user to invoke arbitrary commands. Note that if the $SYSTEMD_PAGER or $PAGER variables are
329
+ to be honoured, $SYSTEMD_PAGERSECURE must be set too. It might be reasonable to completely
330
+ disable the pager using --no-pager instead.
331
+ $SYSTEMD_COLORS :: Takes a boolean argument. When true, systemd and related utilities will use colors in their output,
332
+ otherwise the output will be monochrome. Additionally, the variable can take one of the following
333
+ special values: "16", "256" to restrict the use of colors to the base 16 or 256 ANSI colors,
334
+ respectively. This can be specified to override the automatic decision based on $TERM and what the
335
+ console is connected to.
336
+ $SYSTEMD_URLIFY :: The value must be a boolean. Controls whether clickable links should be generated in the output for
337
+ terminal emulators supporting this. This can be specified to override the decision that systemd makes
338
+ based on $TERM and other conditions.
339
+ """
340
+ import fcntl
341
+ import os.path
342
+ import queue # noqa
343
+ import subprocess
344
+ import time
345
+ import typing as ta
346
+
347
+ from omlish.lite.cached import cached_nullary
348
+ from omlish.lite.check import check_not_none
349
+ from omlish.lite.logs import log
350
+ from omlish.lite.subprocesses import subprocess_shell_wrap_exec
351
+
352
+ from ..threadworker import ThreadWorker
353
+ from .messages import JournalctlMessage # noqa
354
+ from .messages import JournalctlMessageBuilder
355
+
356
+
357
+ class JournalctlTailerWorker(ThreadWorker):
358
+ DEFAULT_CMD: ta.ClassVar[ta.Sequence[str]] = ['journalctl']
359
+
360
+ def __init__(
361
+ self,
362
+ output, # type: queue.Queue[ta.Sequence[JournalctlMessage]]
363
+ *,
364
+ since: ta.Optional[str] = None,
365
+ after_cursor: ta.Optional[str] = None,
366
+
367
+ cmd: ta.Optional[ta.Sequence[str]] = None,
368
+ shell_wrap: bool = False,
369
+
370
+ read_size: int = 0x4000,
371
+ sleep_s: float = 1.,
372
+
373
+ **kwargs: ta.Any,
374
+ ) -> None:
375
+ super().__init__(**kwargs)
376
+
377
+ self._output = output
378
+
379
+ self._since = since
380
+ self._after_cursor = after_cursor
381
+
382
+ self._cmd = cmd or self.DEFAULT_CMD
383
+ self._shell_wrap = shell_wrap
384
+
385
+ self._read_size = read_size
386
+ self._sleep_s = sleep_s
387
+
388
+ self._mb = JournalctlMessageBuilder()
389
+
390
+ self._proc: ta.Optional[subprocess.Popen] = None
391
+
392
+ @cached_nullary
393
+ def _full_cmd(self) -> ta.Sequence[str]:
394
+ cmd = [
395
+ *self._cmd,
396
+ '--output', 'json',
397
+ '--show-cursor',
398
+ '--follow',
399
+ ]
400
+
401
+ if self._since is not None:
402
+ cmd.extend(['--since', self._since])
403
+
404
+ if self._after_cursor is not None:
405
+ cmd.extend(['--after-cursor', self._after_cursor])
406
+
407
+ if self._shell_wrap:
408
+ cmd = list(subprocess_shell_wrap_exec(*cmd))
409
+
410
+ return cmd
411
+
412
+ def _run(self) -> None:
413
+ with subprocess.Popen(
414
+ self._full_cmd(),
415
+ stdout=subprocess.PIPE,
416
+ ) as self._proc:
417
+ stdout = check_not_none(self._proc.stdout)
418
+
419
+ fd = stdout.fileno()
420
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
421
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
422
+
423
+ while True:
424
+ if not self._heartbeat():
425
+ return
426
+
427
+ while stdout.readable():
428
+ if not self._heartbeat():
429
+ return
430
+
431
+ buf = stdout.read(self._read_size)
432
+ if not buf:
433
+ log.debug('Journalctl empty read')
434
+ break
435
+
436
+ log.debug('Journalctl read buffer: %r', buf)
437
+ msgs = self._mb.feed(buf)
438
+ if msgs:
439
+ while True:
440
+ try:
441
+ self._output.put(msgs, timeout=1.)
442
+ except queue.Full:
443
+ if not self._heartbeat():
444
+ return
445
+ else:
446
+ break
447
+
448
+ if self._proc.poll() is not None:
449
+ log.critical('Journalctl process terminated')
450
+ return
451
+
452
+ log.debug('Journalctl readable')
453
+ time.sleep(self._sleep_s)