Skip to content

Implement optional ring buffer for container logs#28762

Merged
LK4D4 merged 2 commits intomoby:masterfrom
cpuguy83:logger_ring_buffer
Feb 1, 2017
Merged

Implement optional ring buffer for container logs#28762
LK4D4 merged 2 commits intomoby:masterfrom
cpuguy83:logger_ring_buffer

Conversation

@cpuguy83
Copy link
Copy Markdown
Member

This allows the user to set a logging mode to "blocking" (default), or
"non-blocking", which uses the ring buffer as a proxy to the real log
driver.

This allows a container to never be blocked on stdio at the cost of
dropping log messages.

Introduces a new flag to docker run and dockerd, --log-mode, which
takes a value of "blocking", or "non-blocking". I chose not to implement
this as a bool since it is difficult to determine if the mode was set to
false vs just not set... especially difficult when merging the default
daemon config with the container config.

One thing to think about here is logger.Message could be any size, it just depends on how long the log line was... so it could be almost nothing, or it could be several megabytes which could cause problems in just blindly buffering N number of messages in the ring buffer.
We could keep track of this, but this could be tricky and introduces locking into the implementation.

@justincormack
Copy link
Copy Markdown
Contributor

Seems like one reasonable solution. I wonder if the ring buffer should be a fixed size in bytes though (which can be specified). Then this would carry over simply to a compatible interfaces via a pipe, where you could set the pipe buffer size and discard if no space. There are some issues about message boundaries though, you could use datagrams and allow truncation.

@LK4D4
Copy link
Copy Markdown
Contributor

LK4D4 commented Nov 23, 2016

@cpuguy83 maybe embed it to log-opt?

@cpuguy83
Copy link
Copy Markdown
Member Author

@cpuguy83 maybe embed it to log-opt?

@LK4D4 The reason I didn't want to do that is log-opt tends to be options for the driver, where as this is changing how docker itself handles logs.

Comment thread daemon/logger/ring.go Outdated
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

May need to add a lock to protect against another log message coming in right after this but before we get a chance to queue below.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd better try to implement messages ring buffer as a separate data structure with tests and stuff. Probably that was your plan after design review.

@LK4D4
Copy link
Copy Markdown
Contributor

LK4D4 commented Nov 23, 2016

I agree with @justincormack about specifying size. However, computing size might be challenging. The number of messages might be okay, too for average use.
Design +1

@AkihiroSuda
Copy link
Copy Markdown
Member

Design SGTM 👍

@cpuguy83 cpuguy83 force-pushed the logger_ring_buffer branch 2 times, most recently from 20366c4 to 52626ee Compare November 25, 2016 23:32
@thaJeztah
Copy link
Copy Markdown
Member

One thing to think about here is logger.Message could be any size, it just depends on how long the log line was...

Perhaps silly comment, but https://github.com/docker/docker/pull/22982/files#diff-0e0dba78caf4a57795625b5ce9943c9eR13 introduced a buffer size, i.e., messages longer than that size are split (even if there's no newline); or is that not relevant here?

@cpuguy83
Copy link
Copy Markdown
Member Author

That's true, there is a max. But I think people want to control the buffer based on size not number of messages.
So I've pushed a new implementation doing just this.

@cpuguy83
Copy link
Copy Markdown
Member Author

@justincormack Implementing this in github.com/pkg/iotutils.BytesPipe is fairly trivial I think, but wondering what's better, drop random byte slices or full messages.

@cpuguy83
Copy link
Copy Markdown
Member Author

Although certainly implementing in BytesPipe is significantly faster since we don't need to do any extra allocations, where as each call to Log() as implemented here requires 1 allocation.

I went ahead and added an implementation in BytesPipe just to look at, presented without tests or benchmarks at the moment.

@justincormack
Copy link
Copy Markdown
Contributor

justincormack commented Nov 26, 2016 via email

Comment thread daemon/logger/ring.go Outdated
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.

nit: personally, I prefer zero-value for indicating the default

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, but I wanted to support a 0 size, which would essentially mean store no more than 1 message.

Comment thread runconfig/opts/parse.go Outdated
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.

unintended change, perhaps?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Intended, just re-ordered so log-opt is underneath log-driver.

@cpuguy83
Copy link
Copy Markdown
Member Author

Removed the commit with the bytespipe changes.

Comment thread daemon/logger/awslogs/cloudwatchlogs.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This doesn't clone underlying message([]byte), which was whole point IIRC.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We shouldn't need to clone the message bytes because that won't ever change.
It's the passed in *Message which gets changed by the log copier since it keeps a single message pointer for all messages.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copier uses parts of its own buffer for Line:
https://github.com/docker/docker/blob/master/daemon/logger/copier.go#L81
And drivers like fluentd can collect messages for later send, so their Line will be overriden by copier.
Also, I believe this was in comment to CopyMessage :D

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh you're right. I'll bring this back.

Comment thread daemon/logger/ring.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should be net/context

@LK4D4
Copy link
Copy Markdown
Contributor

LK4D4 commented Nov 28, 2016

Cond based buffer is kinda cool. Will review later today.

@cpuguy83 cpuguy83 deleted the logger_ring_buffer branch February 2, 2017 13:30
cpuguy83 added a commit to cpuguy83/docker.github.io that referenced this pull request Feb 2, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
mdlinville pushed a commit to docker/docs that referenced this pull request Mar 8, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
mdlinville pushed a commit to docker/docs that referenced this pull request Mar 17, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
JimGalasyn pushed a commit to docker/docs that referenced this pull request Mar 23, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
mdlinville pushed a commit to docker/docs that referenced this pull request Mar 30, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
mdlinville pushed a commit to docker/docs that referenced this pull request Mar 31, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
mdlinville pushed a commit to docker/docs that referenced this pull request Apr 5, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
mdlinville pushed a commit to docker/docs that referenced this pull request Apr 5, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
mdlinville pushed a commit to docker/docs that referenced this pull request Apr 6, 2017
This functionality was added in moby/moby#28762.
It allows a user to specify what to do in cases when a log message can't
be sent, block the container's stdio or drop messages.

Also removes an erronious entry about a `nats` log driver, which does
not exist.

Signed-off-by: Brian Goff <[email protected]>
@glasser
Copy link
Copy Markdown
Contributor

glasser commented Jun 26, 2017

Based on reading the source, it looks like there's a serious bug here in the journald and syslog drivers. (Haven't managed to verify yet with an actual built version.) In both of those drivers, you call logger.PutMessage(msg) and then afterwards read msg.Source. But PutMessage calls msg.reset() which clears Source. Am I missing something or do the drivers not need to save those values before calling PutMessage?

@cpuguy83
Copy link
Copy Markdown
Member Author

@glasser PutMessage should only be called after the value has already been copied.

For instance:

line := string(msg.Source)

This copies the value of msg.Source rather than the reference to msg.Source.

@glasser
Copy link
Copy Markdown
Contributor

glasser commented Jun 26, 2017

I agree. But look at this code this PR put into the syslog driver, which remains there to this day:

func (s *syslogger) Log(msg *logger.Message) error {
	line := string(msg.Line)
	logger.PutMessage(msg)
	if msg.Source == "stderr" {
		return s.writer.Err(line)
	}
	return s.writer.Info(line)
}

That sure seems to read from Source after calling PutMessage. Similar code exists in the journald driver.

@cpuguy83
Copy link
Copy Markdown
Member Author

@glasser Yeah, that looks like an issue!
Do you want to open a PR to fix this?

glasser added a commit to glasser/moby that referenced this pull request Jun 26, 2017
logger.PutMessage, added in moby#28762 (v17.04.0-ce), clears msg.Source. So journald
and syslog were treating stderr messages as if they were stdout.

Signed-off-by: David Glasser <[email protected]>
@glasser
Copy link
Copy Markdown
Contributor

glasser commented Jun 26, 2017

@cpuguy83 sure, #33832

skuenzli added a commit to skuenzli/docker.github.io that referenced this pull request Dec 24, 2017
skuenzli added a commit to skuenzli/docker.github.io that referenced this pull request Dec 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants