923a60
From 32244f8d21a3e06f6519c47234289da696f6b151 Mon Sep 17 00:00:00 2001
923a60
From: Lennart Poettering <lennart@poettering.net>
923a60
Date: Sun, 8 Oct 2017 09:05:59 +0200
923a60
Subject: [PATCH] journald: make maximum size of stream log lines configurable
923a60
 and bump it to 48K (#6838)
923a60
923a60
This adds a new setting LineMax= to journald.conf, and sets it by
923a60
default to 48K. When we convert stream-based stdout/stderr logging into
923a60
record-based log entries, read up to the specified amount of bytes
923a60
before forcing a line-break.
923a60
923a60
This also makes three related changes:
923a60
923a60
- When a NUL byte is read we'll not recognize this as alternative line
923a60
  break, instead of silently dropping everything after it. (see #4863)
923a60
923a60
- The reason for a line-break is now encoded in the log record, if it
923a60
  wasn't a plain newline. Specifically, we distuingish "nul",
923a60
  "line-max" and "eof", for line breaks due to NUL byte, due to the
923a60
  maximum line length as configured with LineMax= or due to end of
923a60
  stream. This data is stored in the new implicit _LINE_BREAK= field.
923a60
  It's not synthesized for plain \n line breaks.
923a60
923a60
- A randomized 128bit ID is assigned to each log stream.
923a60
923a60
With these three changes in place it's (mostly) possible to reconstruct
923a60
the original byte streams from log data, as (most) of the context of
923a60
the conversion from the byte stream to log records is saved now. (So,
923a60
the only bits we still drop are empty lines. Which might be something to
923a60
look into in a future change, and which is outside of the scope of this
923a60
work)
923a60
923a60
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=86465
923a60
See: #4863
923a60
Replaces: #4875
923a60
923a60
(cherry picked from commit ec20fe5ffb8a00469bab209fff6c069bb93c6db2)
923a60
923a60
Resolves: #1442262
923a60
923a60
[msekleta: I had to manually rewrite upstream commit, because git
923a60
did very poor job merging old and new code and identified a lot of merge
923a60
conflicts in a code that was totally unrelated.]
923a60
---
923a60
 man/journald.conf.xml            |  18 +++++
923a60
 man/systemd.journal-fields.xml   |  22 ++++++
923a60
 src/journal/journald-gperf.gperf |   1 +
923a60
 src/journal/journald-server.c    |  68 ++++++++++++++++++
923a60
 src/journal/journald-server.h    |   3 +
923a60
 src/journal/journald-stream.c    | 116 +++++++++++++++++++++++++------
923a60
 src/journal/journald.conf        |   1 +
923a60
 7 files changed, 209 insertions(+), 20 deletions(-)
923a60
923a60
diff --git a/man/journald.conf.xml b/man/journald.conf.xml
923a60
index 46a498b673..e2d6a12252 100644
923a60
--- a/man/journald.conf.xml
923a60
+++ b/man/journald.conf.xml
923a60
@@ -354,6 +354,24 @@
923a60
         <filename>/dev/console</filename>.</para></listitem>
923a60
       </varlistentry>
923a60
 
923a60
+      <varlistentry>
923a60
+        <term><varname>LineMax=</varname></term>
923a60
+
923a60
+        <listitem><para>The maximum line length to permit when converting stream logs into record logs. When a systemd
923a60
+        unit's standard output/error are connected to the journal via a stream socket, the data read is split into
923a60
+        individual log records at newline (<literal>\n</literal>, ASCII 10) and NUL characters. If no such delimiter is
923a60
+        read for the specified number of bytes a hard log record boundary is artifically inserted, breaking up overly
923a60
+        long lines into multiple log records. Selecting overly large values increases the possible memory usage of the
923a60
+        Journal daemon for each stream client, as in the worst case the journal daemon needs to buffer the specified
923a60
+        number of bytes in memory before it can flush a new log record to disk. Also note that permitting overly large
923a60
+        line maximum line lengths affects compatibility with traditional log protocols as log records might not fit
923a60
+        anymore into a single <constant>AF_UNIX</constant> or <constant>AF_INET</constant> datagram. Takes a size in
923a60
+        bytes. If the value is suffixed with K, M, G or T, the specified size is parsed as Kilobytes, Megabytes,
923a60
+        Gigabytes, or Terabytes (with the base 1024), respectively. Defaults to 48K, which is relatively large but
923a60
+        still small enough so that log records likely fit into network datagrams along with extra room for
923a60
+        metadata. Note that values below 79 are not accepted and will be bumped to 79.</para></listitem>
923a60
+      </varlistentry>
923a60
+
923a60
     </variablelist>
923a60
 
923a60
   </refsect1>
923a60
diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml
923a60
index 7d6c5c715f..a53f8def2d 100644
923a60
--- a/man/systemd.journal-fields.xml
923a60
+++ b/man/systemd.journal-fields.xml
923a60
@@ -311,6 +311,28 @@
923a60
           </variablelist>
923a60
         </listitem>
923a60
       </varlistentry>
923a60
+      <varlistentry>
923a60
+        <term><varname>_STREAM_ID=</varname></term>
923a60
+        <listitem>
923a60
+          <para>Only applies to <literal>_TRANSPORT=stream</literal> records: specifies a randomized 128bit ID assigned
923a60
+          to the stream connection when it was first created. This ID is useful to reconstruct individual log streams
923a60
+          from the log records: all log records carrying the same stream ID originate from the same stream.</para>
923a60
+        </listitem>
923a60
+      </varlistentry>
923a60
+      <varlistentry>
923a60
+        <term><varname>_LINE_BREAK=</varname></term>
923a60
+        <listitem>
923a60
+          <para>Only applies to <literal>_TRANSPORT=stream</literal> records: indicates that the log message in the
923a60
+          standard output/error stream was not terminated with a normal newline character (<literal>\n</literal>,
923a60
+          i.e. ASCII 10). Specifically, when set this field is one of <option>nul</option> (in case the line was
923a60
+          terminated by a NUL byte), <option>line-max</option> (in case the maximum log line length was reached, as
923a60
+          configured with <varname>LineMax=</varname> in
923a60
+          <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>) or
923a60
+          <option>eof</option> (if this was the last log record of a stream and the stream ended without a final
923a60
+          newline character). Note that this record is not generated when a normal newline character was used for
923a60
+          marking the log line end.</para>
923a60
+        </listitem>
923a60
+      </varlistentry>
923a60
     </variablelist>
923a60
   </refsect1>
923a60
 
923a60
diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf
923a60
index 74554c1c34..73327c10e9 100644
923a60
--- a/src/journal/journald-gperf.gperf
923a60
+++ b/src/journal/journald-gperf.gperf
923a60
@@ -40,3 +40,4 @@ Journal.MaxLevelKMsg,       config_parse_log_level,  0, offsetof(Server, max_lev
923a60
 Journal.MaxLevelConsole,    config_parse_log_level,  0, offsetof(Server, max_level_console)
923a60
 Journal.MaxLevelWall,       config_parse_log_level,  0, offsetof(Server, max_level_wall)
923a60
 Journal.SplitMode,          config_parse_split_mode, 0, offsetof(Server, split_mode)
923a60
+Journal.LineMax,            config_parse_line_max,   0, offsetof(Server, line_max)
923a60
\ No newline at end of file
923a60
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
923a60
index f6f8c30eb2..daeecd5191 100644
923a60
--- a/src/journal/journald-server.c
923a60
+++ b/src/journal/journald-server.c
923a60
@@ -67,6 +67,10 @@
923a60
 
923a60
 #define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC)
923a60
 
923a60
+/* Pick a good default that is likely to fit into AF_UNIX and AF_INET SOCK_DGRAM datagrams, and even leaves some room
923a60
++ * for a bit of additional metadata. */
923a60
+#define DEFAULT_LINE_MAX (48*1024)
923a60
+
923a60
 static const char* const storage_table[_STORAGE_MAX] = {
923a60
         [STORAGE_AUTO] = "auto",
923a60
         [STORAGE_VOLATILE] = "volatile",
923a60
@@ -83,9 +87,71 @@ static const char* const split_mode_table[_SPLIT_MAX] = {
923a60
         [SPLIT_NONE] = "none",
923a60
 };
923a60
 
923a60
+
923a60
 DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
923a60
 DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
923a60
 
923a60
+int config_parse_line_max(
923a60
+                const char* unit,
923a60
+                const char *filename,
923a60
+                unsigned line,
923a60
+                const char *section,
923a60
+                unsigned section_line,
923a60
+                const char *lvalue,
923a60
+                int ltype,
923a60
+                const char *rvalue,
923a60
+                void *data,
923a60
+                void *userdata) {
923a60
+
923a60
+        size_t *sz = data;
923a60
+        int r;
923a60
+
923a60
+        assert(filename);
923a60
+        assert(lvalue);
923a60
+        assert(rvalue);
923a60
+        assert(data);
923a60
+
923a60
+        if (isempty(rvalue))
923a60
+                /* Empty assignment means default */
923a60
+                *sz = DEFAULT_LINE_MAX;
923a60
+        else {
923a60
+                uint64_t v;
923a60
+                off_t u;
923a60
+
923a60
+                r = parse_size(rvalue, 1024, &u);
923a60
+                if (r < 0) {
923a60
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse LineMax= value, ignoring: %s", rvalue);
923a60
+                        return 0;
923a60
+                }
923a60
+
923a60
+                /* Backport note */
923a60
+                /* Upstream ditched use of off_t however our parse_size implementation still takes off_t*
923a60
+                 * as an argument. Since we compile with -Werror, we have two choices, either disable sign-compare
923a60
+                 * warning or do this casting so we don't have to change rest of the code. I think it is
923a60
+                 * better to do cast here instead of rewriting the code so it deals with off_t instead of
923a60
+                 * uint64_t. Doing conversion off_t -> uint64_t is something that we should think about. */
923a60
+                v = (uint64_t) u;
923a60
+
923a60
+                if (v < 79) {
923a60
+                        /* Why specify 79 here as minimum line length? Simply, because the most common traditional
923a60
+                         * terminal size is 80ch, and it might make sense to break one character before the natural
923a60
+                         * line break would occur on that. */
923a60
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too small, clamping to 79: %s", rvalue);
923a60
+                        *sz = 79;
923a60
+                } else if (v > (uint64_t) (SSIZE_MAX-1)) {
923a60
+                        /* So, why specify SSIZE_MAX-1 here? Because that's one below the largest size value read()
923a60
+                         * can return, and we need one extra byte for the trailing NUL byte. Of course IRL such large
923a60
+                         * memory allocations will fail anyway, hence this limit is mostly theoretical anyway, as we'll
923a60
+                         * fail much earlier anyway. */
923a60
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "LineMax= too large, clamping to %" PRIu64 ": %s", (uint64_t) (SSIZE_MAX-1), rvalue);
923a60
+                        *sz = SSIZE_MAX-1;
923a60
+                } else
923a60
+                        *sz = (size_t) v;
923a60
+        }
923a60
+
923a60
+        return 0;
923a60
+}
923a60
+
923a60
 static uint64_t available_space(Server *s, bool verbose) {
923a60
         char ids[33];
923a60
         _cleanup_free_ char *p = NULL;
923a60
@@ -1518,6 +1584,8 @@ int server_init(Server *s) {
923a60
         s->max_level_console = LOG_INFO;
923a60
         s->max_level_wall = LOG_EMERG;
923a60
 
923a60
+        s->line_max = DEFAULT_LINE_MAX;
923a60
+
923a60
         memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics));
923a60
         memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics));
923a60
 
923a60
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
923a60
index 7a456c2d54..b294107788 100644
923a60
--- a/src/journal/journald-server.h
923a60
+++ b/src/journal/journald-server.h
923a60
@@ -143,6 +143,8 @@ typedef struct Server {
923a60
 
923a60
         /* Cached cgroup root, so that we don't have to query that all the time */
923a60
         char *cgroup_root;
923a60
+
923a60
+        size_t line_max;
923a60
 } Server;
923a60
 
923a60
 #define N_IOVEC_META_FIELDS 20
923a60
@@ -157,6 +159,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
923a60
 const struct ConfigPerfItem* journald_gperf_lookup(const char *key, unsigned length);
923a60
 
923a60
 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);
923a60
+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);
923a60
 
923a60
 const char *storage_to_string(Storage s) _const_;
923a60
 Storage storage_from_string(const char *s) _pure_;
923a60
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
923a60
index 15c9110c0f..4d6b7ad189 100644
923a60
--- a/src/journal/journald-stream.c
923a60
+++ b/src/journal/journald-stream.c
923a60
@@ -53,6 +53,16 @@ typedef enum StdoutStreamState {
923a60
         STDOUT_STREAM_RUNNING
923a60
 } StdoutStreamState;
923a60
 
923a60
+/* The different types of log record terminators: a real \n was read, a NUL character was read, the maximum line length
923a60
+ * was reached, or the end of the stream was reached */
923a60
+
923a60
+typedef enum LineBreak {
923a60
+        LINE_BREAK_NEWLINE,
923a60
+        LINE_BREAK_NUL,
923a60
+        LINE_BREAK_LINE_MAX,
923a60
+        LINE_BREAK_EOF,
923a60
+} LineBreak;
923a60
+
923a60
 struct StdoutStream {
923a60
         Server *server;
923a60
         StdoutStreamState state;
923a60
@@ -71,14 +81,17 @@ struct StdoutStream {
923a60
 
923a60
         bool fdstore:1;
923a60
 
923a60
-        char buffer[LINE_MAX+1];
923a60
+        char *buffer;
923a60
         size_t length;
923a60
+        size_t allocated;
923a60
 
923a60
         sd_event_source *event_source;
923a60
 
923a60
         char *state_file;
923a60
 
923a60
         LIST_FIELDS(StdoutStream, stdout_stream);
923a60
+
923a60
+        char id_field[sizeof("_STREAM_ID=")-1 + SD_ID128_STRING_MAX];
923a60
 };
923a60
 
923a60
 void stdout_stream_free(StdoutStream *s) {
923a60
@@ -101,6 +114,7 @@ void stdout_stream_free(StdoutStream *s) {
923a60
         free(s->identifier);
923a60
         free(s->unit_id);
923a60
         free(s->state_file);
923a60
+        free(s->buffer);
923a60
 
923a60
         free(s);
923a60
 }
923a60
@@ -151,12 +165,14 @@ static int stdout_stream_save(StdoutStream *s) {
923a60
                 "LEVEL_PREFIX=%i\n"
923a60
                 "FORWARD_TO_SYSLOG=%i\n"
923a60
                 "FORWARD_TO_KMSG=%i\n"
923a60
-                "FORWARD_TO_CONSOLE=%i\n",
923a60
+                "FORWARD_TO_CONSOLE=%i\n"
923a60
+                "STREAM_ID=%s\n",
923a60
                 s->priority,
923a60
                 s->level_prefix,
923a60
                 s->forward_to_syslog,
923a60
                 s->forward_to_kmsg,
923a60
-                s->forward_to_console);
923a60
+                s->forward_to_console,
923a60
+                s->id_field + strlen("_STREAM_ID="));
923a60
 
923a60
         if (!isempty(s->identifier)) {
923a60
                 _cleanup_free_ char *escaped;
923a60
@@ -211,8 +227,8 @@ finish:
923a60
         return r;
923a60
 }
923a60
 
923a60
-static int stdout_stream_log(StdoutStream *s, const char *p) {
923a60
-        struct iovec iovec[N_IOVEC_META_FIELDS + 5];
923a60
+static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_break) {
923a60
+        struct iovec iovec[N_IOVEC_META_FIELDS + 7];
923a60
         int priority;
923a60
         char syslog_priority[] = "PRIORITY=\0";
923a60
         char syslog_facility[sizeof("SYSLOG_FACILITY=")-1 + DECIMAL_STR_MAX(int) + 1];
923a60
@@ -245,6 +261,8 @@ static int stdout_stream_log(StdoutStream *s, const char *p) {
923a60
 
923a60
         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout");
923a60
 
923a60
+        IOVEC_SET_STRING(iovec[n++], s->id_field);
923a60
+
923a60
         syslog_priority[strlen("PRIORITY=")] = '0' + LOG_PRI(priority);
923a60
         IOVEC_SET_STRING(iovec[n++], syslog_priority);
923a60
 
923a60
@@ -259,6 +277,18 @@ static int stdout_stream_log(StdoutStream *s, const char *p) {
923a60
                         IOVEC_SET_STRING(iovec[n++], syslog_identifier);
923a60
         }
923a60
 
923a60
+        if (line_break != LINE_BREAK_NEWLINE) {
923a60
+                const char *c;
923a60
+
923a60
+                /* If this log message was generated due to an uncommon line break then mention this in the log
923a60
+                 * entry */
923a60
+
923a60
+                c =     line_break == LINE_BREAK_NUL ?      "_LINE_BREAK=nul" :
923a60
+                        line_break == LINE_BREAK_LINE_MAX ? "_LINE_BREAK=line-max" :
923a60
+                                                            "_LINE_BREAK=eof";
923a60
+                IOVEC_SET_STRING(iovec[n++], c);
923a60
+        }
923a60
+
923a60
         message = strappend("MESSAGE=", p);
923a60
         if (message)
923a60
                 IOVEC_SET_STRING(iovec[n++], message);
923a60
@@ -268,12 +298,18 @@ static int stdout_stream_log(StdoutStream *s, const char *p) {
923a60
         return 0;
923a60
 }
923a60
 
923a60
-static int stdout_stream_line(StdoutStream *s, char *p) {
923a60
+static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
923a60
         int r;
923a60
 
923a60
         assert(s);
923a60
         assert(p);
923a60
 
923a60
+        /* line breaks by NUL, line max length or EOF are not permissible during the negotiation part of the protocol */
923a60
+        if (line_break != LINE_BREAK_NEWLINE && s->state != STDOUT_STREAM_RUNNING) {
923a60
+                log_warning("Control protocol line not properly terminated.");
923a60
+                return -EINVAL;
923a60
+        }
923a60
+
923a60
         p = strstrip(p);
923a60
 
923a60
         switch (s->state) {
923a60
@@ -362,7 +398,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
923a60
                 return 0;
923a60
 
923a60
         case STDOUT_STREAM_RUNNING:
923a60
-                return stdout_stream_log(s, p);
923a60
+                return stdout_stream_log(s, p, line_break);
923a60
         }
923a60
 
923a60
         assert_not_reached("Unknown stream state");
923a60
@@ -378,21 +414,32 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
923a60
         p = s->buffer;
923a60
         remaining = s->length;
923a60
         for (;;) {
923a60
-                char *end;
923a60
+                LineBreak line_break;
923a60
                 size_t skip;
923a60
 
923a60
-                end = memchr(p, '\n', remaining);
923a60
-                if (end)
923a60
-                        skip = end - p + 1;
923a60
-                else if (remaining >= sizeof(s->buffer) - 1) {
923a60
-                        end = p + sizeof(s->buffer) - 1;
923a60
+                char *end1, *end2;
923a60
+
923a60
+                end1 = memchr(p, '\n', remaining);
923a60
+                end2 = memchr(p, 0, end1 ? (size_t) (end1 - p) : remaining);
923a60
+
923a60
+                if (end2) {
923a60
+                        /* We found a NUL terminator */
923a60
+                        skip = end2 - p + 1;
923a60
+                        line_break = LINE_BREAK_NUL;
923a60
+                } else if (end1) {
923a60
+                        /* We found a \n terminator */
923a60
+                        *end1 = 0;
923a60
+                        skip = end1 - p + 1;
923a60
+                        line_break = LINE_BREAK_NEWLINE;
923a60
+                } else if (remaining >= s->server->line_max) {
923a60
+                        /* Force a line break after the maximum line length */
923a60
+                        *(p + s->server->line_max) = 0;
923a60
                         skip = remaining;
923a60
+                        line_break = LINE_BREAK_LINE_MAX;
923a60
                 } else
923a60
                         break;
923a60
 
923a60
-                *end = 0;
923a60
-
923a60
-                r = stdout_stream_line(s, p);
923a60
+                r = stdout_stream_line(s, p, line_break);
923a60
                 if (r < 0)
923a60
                         return r;
923a60
 
923a60
@@ -402,7 +449,7 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
923a60
 
923a60
         if (force_flush && remaining > 0) {
923a60
                 p[remaining] = 0;
923a60
-                r = stdout_stream_line(s, p);
923a60
+                r = stdout_stream_line(s, p, LINE_BREAK_EOF);
923a60
                 if (r < 0)
923a60
                         return r;
923a60
 
923a60
@@ -420,6 +467,7 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
923a60
 
923a60
 static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
923a60
         StdoutStream *s = userdata;
923a60
+        size_t limit;
923a60
         ssize_t l;
923a60
         int r;
923a60
 
923a60
@@ -430,9 +478,20 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
923a60
                 goto terminate;
923a60
         }
923a60
 
923a60
-        l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length);
923a60
-        if (l < 0) {
923a60
+        /* If the buffer is full already (discounting the extra NUL we need), add room for another 1K */
923a60
+        if (s->length + 1 >= s->allocated) {
923a60
+                if (!GREEDY_REALLOC(s->buffer, s->allocated, s->length + 1 + 1024)) {
923a60
+                        log_oom();
923a60
+                        goto terminate;
923a60
+                }
923a60
+        }
923a60
+
923a60
+        /* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also,
923a60
+         * always leave room for a terminating NUL we might need to add. */
923a60
+        limit = MIN(s->allocated - 1, s->server->line_max);
923a60
 
923a60
+        l = read(s->fd, s->buffer + s->length, limit - s->length);
923a60
+        if (l < 0) {
923a60
                 if (errno == EAGAIN)
923a60
                         return 0;
923a60
 
923a60
@@ -459,11 +518,16 @@ terminate:
923a60
 
923a60
 static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
923a60
         _cleanup_(stdout_stream_freep) StdoutStream *stream = NULL;
923a60
+        sd_id128_t id;
923a60
         int r;
923a60
 
923a60
         assert(s);
923a60
         assert(fd >= 0);
923a60
 
923a60
+        r = sd_id128_randomize(&id;;
923a60
+        if (r < 0)
923a60
+                return log_error_errno(r, "Failed to generate stream ID: %m");
923a60
+
923a60
         stream = new0(StdoutStream, 1);
923a60
         if (!stream)
923a60
                 return log_oom();
923a60
@@ -471,6 +535,8 @@ static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
923a60
         stream->fd = -1;
923a60
         stream->priority = LOG_INFO;
923a60
 
923a60
+        xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
923a60
+
923a60
         r = getpeercred(fd, &stream->ucred);
923a60
         if (r < 0)
923a60
                 return log_error_errno(r, "Failed to determine peer credentials: %m");
923a60
@@ -545,7 +611,8 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
923a60
                 *level_prefix = NULL,
923a60
                 *forward_to_syslog = NULL,
923a60
                 *forward_to_kmsg = NULL,
923a60
-                *forward_to_console = NULL;
923a60
+                *forward_to_console = NULL,
923a60
+                *stream_id = NULL;
923a60
         int r;
923a60
 
923a60
         assert(stream);
923a60
@@ -565,6 +632,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
923a60
                            "FORWARD_TO_CONSOLE", &forward_to_console,
923a60
                            "IDENTIFIER", &stream->identifier,
923a60
                            "UNIT", &stream->unit_id,
923a60
+                           "STREAM_ID", &stream_id,
923a60
                            NULL);
923a60
         if (r < 0)
923a60
                 return log_error_errno(r, "Failed to read: %s", stream->state_file);
923a60
@@ -601,6 +669,14 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
923a60
                         stream->forward_to_console = r;
923a60
         }
923a60
 
923a60
+        if (stream_id) {
923a60
+                sd_id128_t id;
923a60
+
923a60
+                r = sd_id128_from_string(stream_id, &id;;
923a60
+                if (r >= 0)
923a60
+                        xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
923a60
+        }
923a60
+
923a60
         return 0;
923a60
 }
923a60
 
923a60
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
923a60
index 3907dfb7ff..5355ec2b2f 100644
923a60
--- a/src/journal/journald.conf
923a60
+++ b/src/journal/journald.conf
923a60
@@ -37,3 +37,4 @@
923a60
 #MaxLevelKMsg=notice
923a60
 #MaxLevelConsole=info
923a60
 #MaxLevelWall=emerg
923a60
+#LineMax=48K