Skip to content

Commit ec20fe5

Browse files
poetteringkeszybz
authored andcommitted
journald: make maximum size of stream log lines configurable and bump it to 48K (systemd#6838)
This adds a new setting LineMax= to journald.conf, and sets it by default to 48K. When we convert stream-based stdout/stderr logging into record-based log entries, read up to the specified amount of bytes before forcing a line-break. This also makes three related changes: - When a NUL byte is read we'll not recognize this as alternative line break, instead of silently dropping everything after it. (see systemd#4863) - The reason for a line-break is now encoded in the log record, if it wasn't a plain newline. Specifically, we distuingish "nul", "line-max" and "eof", for line breaks due to NUL byte, due to the maximum line length as configured with LineMax= or due to end of stream. This data is stored in the new implicit _LINE_BREAK= field. It's not synthesized for plain \n line breaks. - A randomized 128bit ID is assigned to each log stream. With these three changes in place it's (mostly) possible to reconstruct the original byte streams from log data, as (most) of the context of the conversion from the byte stream to log records is saved now. (So, the only bits we still drop are empty lines. Which might be something to look into in a future change, and which is outside of the scope of this work) Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=86465 See: systemd#4863 Replaces: systemd#4875
1 parent d82611c commit ec20fe5

File tree

8 files changed

+202
-23
lines changed

8 files changed

+202
-23
lines changed

man/journald.conf.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,24 @@
373373
<filename>/dev/console</filename>.</para></listitem>
374374
</varlistentry>
375375

376+
<varlistentry>
377+
<term><varname>LineMax=</varname></term>
378+
379+
<listitem><para>The maximum line length to permit when converting stream logs into record logs. When a systemd
380+
unit's standard output/error are connected to the journal via a stream socket, the data read is split into
381+
individual log records at newline (<literal>\n</literal>, ASCII 10) and NUL characters. If no such delimiter is
382+
read for the specified number of bytes a hard log record boundary is artifically inserted, breaking up overly
383+
long lines into multiple log records. Selecting overly large values increases the possible memory usage of the
384+
Journal daemon for each stream client, as in the worst case the journal daemon needs to buffer the specified
385+
number of bytes in memory before it can flush a new log record to disk. Also note that permitting overly large
386+
line maximum line lengths affects compatibility with traditional log protocols as log records might not fit
387+
anymore into a single <constant>AF_UNIX</constant> or <constant>AF_INET</constant> datagram. Takes a size in
388+
bytes. If the value is suffixed with K, M, G or T, the specified size is parsed as Kilobytes, Megabytes,
389+
Gigabytes, or Terabytes (with the base 1024), respectively. Defaults to 48K, which is relatively large but
390+
still small enough so that log records likely fit into network datagrams along with extra room for
391+
metadata. Note that values below 79 are not accepted and will be bumped to 79.</para></listitem>
392+
</varlistentry>
393+
376394
</variablelist>
377395

378396
</refsect1>

man/systemd-journald.service.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,9 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
149149
via the <citerefentry><refentrytitle>systemd-cat</refentrytitle><manvolnum>1</manvolnum></citerefentry> command
150150
line tool.</para>
151151

152-
<para> Currently, the number of parallel log streams <filename>systemd-journald</filename> will accept is limited
153-
to 4096.</para>
152+
<para>Currently, the number of parallel log streams <filename>systemd-journald</filename> will accept is limited to
153+
4096. When this limit is reached further log streams may be established but will receieve
154+
<constant>EPIPE</constant> right from the beginning.</para>
154155
</refsect1>
155156

156157
<refsect1>

man/systemd.journal-fields.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,28 @@
333333
</variablelist>
334334
</listitem>
335335
</varlistentry>
336+
<varlistentry>
337+
<term><varname>_STREAM_ID=</varname></term>
338+
<listitem>
339+
<para>Only applies to <literal>_TRANSPORT=stream</literal> records: specifies a randomized 128bit ID assigned
340+
to the stream connection when it was first created. This ID is useful to reconstruct individual log streams
341+
from the log records: all log records carrying the same stream ID originate from the same stream.</para>
342+
</listitem>
343+
</varlistentry>
344+
<varlistentry>
345+
<term><varname>_LINE_BREAK=</varname></term>
346+
<listitem>
347+
<para>Only applies to <literal>_TRANSPORT=stream</literal> records: indicates that the log message in the
348+
standard output/error stream was not terminated with a normal newline character (<literal>\n</literal>,
349+
i.e. ASCII 10). Specifically, when set this field is one of <option>nul</option> (in case the line was
350+
terminated by a NUL byte), <option>line-max</option> (in case the maximum log line length was reached, as
351+
configured with <varname>LineMax=</varname> in
352+
<citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>) or
353+
<option>eof</option> (if this was the last log record of a stream and the stream ended without a final
354+
newline character). Note that this record is not generated when a normal newline character was used for
355+
marking the log line end.</para>
356+
</listitem>
357+
</varlistentry>
336358
</variablelist>
337359
</refsect1>
338360

src/journal/journald-gperf.gperf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,4 @@ Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(Server, max_lev
4545
Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(Server, max_level_console)
4646
Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(Server, max_level_wall)
4747
Journal.SplitMode, config_parse_split_mode, 0, offsetof(Server, split_mode)
48+
Journal.LineMax, config_parse_line_max, 0, offsetof(Server, line_max)

src/journal/journald-server.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@
8888
/* The period to insert between posting changes for coalescing */
8989
#define POST_CHANGE_TIMER_INTERVAL_USEC (250*USEC_PER_MSEC)
9090

91+
/* Pick a good default that is likely to fit into AF_UNIX and AF_INET SOCK_DGRAM datagrams, and even leaves some room
92+
* for a bit of additional metadata. */
93+
#define DEFAULT_LINE_MAX (48*1024)
94+
9195
static int determine_path_usage(Server *s, const char *path, uint64_t *ret_used, uint64_t *ret_free) {
9296
_cleanup_closedir_ DIR *d = NULL;
9397
struct dirent *de;
@@ -1689,6 +1693,8 @@ int server_init(Server *s) {
16891693
s->max_level_console = LOG_INFO;
16901694
s->max_level_wall = LOG_EMERG;
16911695

1696+
s->line_max = DEFAULT_LINE_MAX;
1697+
16921698
journal_reset_metrics(&s->system_storage.metrics);
16931699
journal_reset_metrics(&s->runtime_storage.metrics);
16941700

@@ -1963,3 +1969,55 @@ static const char* const split_mode_table[_SPLIT_MAX] = {
19631969

19641970
DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
19651971
DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
1972+
1973+
int config_parse_line_max(
1974+
const char* unit,
1975+
const char *filename,
1976+
unsigned line,
1977+
const char *section,
1978+
unsigned section_line,
1979+
const char *lvalue,
1980+
int ltype,
1981+
const char *rvalue,
1982+
void *data,
1983+
void *userdata) {
1984+
1985+
size_t *sz = data;
1986+
int r;
1987+
1988+
assert(filename);
1989+
assert(lvalue);
1990+
assert(rvalue);
1991+
assert(data);
1992+
1993+
if (isempty(rvalue))
1994+
/* Empty assignment means default */
1995+
*sz = DEFAULT_LINE_MAX;
1996+
else {
1997+
uint64_t v;
1998+
1999+
r = parse_size(rvalue, 1024, &v);
2000+
if (r < 0) {
2001+
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse LineMax= value, ignoring: %s", rvalue);
2002+
return 0;
2003+
}
2004+
2005+
if (v < 79) {
2006+
/* Why specify 79 here as minimum line length? Simply, because the most common traditional
2007+
* terminal size is 80ch, and it might make sense to break one character before the natural
2008+
* line break would occur on that. */
2009+
log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too small, clamping to 79: %s", rvalue);
2010+
*sz = 79;
2011+
} else if (v > (uint64_t) (SSIZE_MAX-1)) {
2012+
/* So, why specify SSIZE_MAX-1 here? Because that's one below the largest size value read()
2013+
* can return, and we need one extra byte for the trailing NUL byte. Of course IRL such large
2014+
* memory allocations will fail anyway, hence this limit is mostly theoretical anyway, as we'll
2015+
* fail much earlier anyway. */
2016+
log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too large, clamping to %" PRIu64 ": %s", (uint64_t) (SSIZE_MAX-1), rvalue);
2017+
*sz = SSIZE_MAX-1;
2018+
} else
2019+
*sz = (size_t) v;
2020+
}
2021+
2022+
return 0;
2023+
}

src/journal/journald-server.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ struct Server {
169169

170170
usec_t last_realtime_clock;
171171

172+
size_t line_max;
173+
172174
/* Caching of client metadata */
173175
Hashmap *client_contexts;
174176
Prioq *client_contexts_lru;
@@ -192,6 +194,7 @@ void server_driver_message(Server *s, const char *message_id, const char *format
192194
const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
193195

194196
int config_parse_storage(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
197+
int config_parse_line_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
195198

196199
const char *storage_to_string(Storage s) _const_;
197200
Storage storage_from_string(const char *s) _pure_;

0 commit comments

Comments
 (0)