Skip to content

Rewrite journald LogReader and unit test it#43219

Merged
thaJeztah merged 8 commits intomoby:masterfrom
corhere:sdjournal
Aug 2, 2022
Merged

Rewrite journald LogReader and unit test it#43219
thaJeztah merged 8 commits intomoby:masterfrom
corhere:sdjournal

Conversation

@corhere
Copy link
Copy Markdown
Contributor

@corhere corhere commented Feb 9, 2022

- What I did
I rewrote the journald LogReader using a thin cgo wrapper around the sd_journal C API and performing all the heavy lifting in idiomatic Go code. I eliminated the need to use journal cursors to check for duplicate log messages by carefully managing the movement of the journal read pointer. The rewritten implementation passes the full LogReader test suite and implements the --follow flag correctly and performantly. (The claim in #38859 that this is impossible was slightly exaggerated.)

I fixed edge cases in the handling of --since with non-monotonic message timestamps in both the journald and logfile readers and extended the LogReader suite to regression test it.

- How I did it
libsystemd's sd_journal API has some known bugs around read-pointer movement. The existing implementation triggered these bugs and worked around them with brute force using cursors. I was able to work around the bugs in the reimplementation so that the read-pointer always moves where it is intended to go, no cursors required.

SystemD provides a systemd-journal-remote command which can be used to write arbitrary journal entries to journal files at arbitrary paths. I wrote a fake journal.Send which uses that command to write journal entries to isolated, temporary journal files which are independent from the system or user journals. This is what makes it possible to close the loop between the journal log writer, libsystemd and the journal log reader in unit tests.

- How to verify it

$ make BIND_DIR=. shell
# go test -tags journald ./daemon/logger/...

- Description for the changelog

  • Newlines are no longer lost from container logs when using the journald log driver.
  • The non-user-defined attributes CONTAINER_PARTIAL_ID, CONTAINER_PARTIAL_ORDINAL, CONTAINER_PARTIAL_MESSAGE, CONTAINER_PARTIAL_LAST and IMAGE_NAME are no longer output from docker logs --details when using the journald log driver.
  • Resolved an issue where log messages could be incorrectly filtered out when using docker logs --since on containers using the json-file, local or journald log drivers if the system clock had been rolled back to before the --since timestamp while the container was running.

- A picture of a cute animal (not mandatory but encouraged)

@corhere corhere force-pushed the sdjournal branch 2 times, most recently from 65cce27 to 4ad6604 Compare February 9, 2022 20:02
@corhere corhere force-pushed the sdjournal branch 2 times, most recently from 8d7d022 to 46fbcf5 Compare May 20, 2022 21:53
@corhere corhere marked this pull request as ready for review May 20, 2022 22:10
@corhere corhere requested a review from tianon as a code owner May 20, 2022 22:10
@corhere
Copy link
Copy Markdown
Contributor Author

corhere commented May 20, 2022

@thaJeztah @tianon @kolyshkin @cpuguy83 @rumpl @ndeloof PTAL

Note for reviewers: the sequence of commits escalates from minor refactorings up to a complete rewrite which clobbers large swathes of code touched in prior commits. I have done this to capture (a cleaned up version of) the steps I took which led up to the rewrite in git history and so that we have multiple bisection points to revert to in case I have introduced a regression. In light of this, I recommend starting by reviewing the squashed diff rather than the individual commits.

Comment thread daemon/logger/journald/read.go Outdated
@thaJeztah thaJeztah added area/logging status/2-code-review kind/refactor PR's that refactor, or clean-up code labels Jul 20, 2022
@corhere corhere added kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. kind/bugfix PR's that fix bugs labels Jul 20, 2022
Comment thread daemon/logger/journald/read.go Outdated
corhere added 7 commits July 25, 2022 16:37
Ensure the package can be imported, no matter the build constratints, by
adding an unconstrained doc.go containing a package statement.

Signed-off-by: Cory Snider <[email protected]>
Wrap the libsystemd journal reading functionality in a more idiomatic Go
API and refactor the journald logging driver's ReadLogs implementation
to use the wrapper. Rewrite the parts of the ReadLogs implementation in
Go which were previously implemented in C as part of the cgo preamble.
Separating the business logic from the cgo minutiae should hopefully
make the code more accessible to a wider audience of developers for
reviewing the code and contributing improvements.

The structure of the ReadLogs implementation is retained with few
modifications. Any ignored errors were also ignored before the refactor;
the explicit error return values afforded by the sdjournal wrapper makes
this more obvious.

The package github.com/coreos/go-systemd/v22/sdjournal also provides a
more idiomatic Go wrapper around libsystemd. It is unsuitable for our
needs as it does not expose wrappers for the sd_journal_process and
sd_journal_get_fd functions.

Signed-off-by: Cory Snider <[email protected]>
Careful management of the journal read pointer is sufficient to ensure
that no entry is read more than once.

Unit test the journald logger without requiring a running journald by
using the systemd-journal-remote command to write arbitrary entries to
journal files.

Signed-off-by: Cory Snider <[email protected]>
Fix journald and logfile-powered (jsonfile, local) log readers
incorrectly filtering out messages with timestamps < Since which were
preceded by a message with a timestamp >= Since.

Signed-off-by: Cory Snider <[email protected]>
Implement --follow entirely correctly for the journald log reader, such
that it exits immediately upon reading back the last log message written
to the journal before the logger was closed. The impossibility of doing
so has been slightly exaggerated.

Signed-off-by: Cory Snider <[email protected]>
@thaJeztah thaJeztah added this to the v-next milestone Aug 2, 2022
Copy link
Copy Markdown
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

did not look into all bits fully in-depth, but went through the commits a couple of times; looks great!

Comment thread Dockerfile
Comment on lines -389 to -391
RUN mkdir -p hack \
&& curl -o hack/dind-systemd https://raw.githubusercontent.com/AkihiroSuda/containerized-systemd/b70bac0daeea120456764248164c21684ade7d0d/docker-entrypoint.sh \
&& chmod +x hack/dind-systemd
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

./cc @AkihiroSuda (FYI - this commit adds a copy/variation of the above in hack/)

(I recall there originally was some discussion about this in #40493 (comment))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copying the script SGTM, but what is the modification here?

// well-known paths and the directories named in the $PATH environment variable.
func JournalRemoteCmdPath() (string, error) {
for _, p := range cmdPaths {
if path, err := exec.LookPath(p); err == nil {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if (in a follow-up) we should use golang.org/x/sys/execabs for this; https://github.com/golang/sys/blob/b6088ccd6cba9272b1e5639ef2df5b5731b85abc/execabs/execabs.go#L5-L13

edit: Ignore me; this is an internal package and only for testing

Comment on lines +30 to +33
// The systemd-journal-remote command is not conventionally installed on $PATH.
// The manpage from upstream systemd lists the command as
// /usr/lib/systemd/systemd-journal-remote, but Debian installs it to
// /lib/systemd instead.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awkward. Well.. it is what it is 🤷‍♂️

@thaJeztah thaJeztah merged commit 9100e3c into moby:master Aug 2, 2022
@corhere corhere deleted the sdjournal branch August 8, 2022 16:17
crazy-max pushed a commit to crazy-max/moby that referenced this pull request Sep 29, 2022
Rewrite journald LogReader and unit test it
Signed-off-by: CrazyMax <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/logging kind/bugfix PR's that fix bugs kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. kind/refactor PR's that refactor, or clean-up code status/4-merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants