mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #36522 from IRCody/awslogs-non-blocking
Allow awslogs to use non-blocking mode
This commit is contained in:
commit
fe2d3a1551
2 changed files with 185 additions and 22 deletions
|
@ -61,6 +61,7 @@ type logStream struct {
|
||||||
logStreamName string
|
logStreamName string
|
||||||
logGroupName string
|
logGroupName string
|
||||||
logCreateGroup bool
|
logCreateGroup bool
|
||||||
|
logNonBlocking bool
|
||||||
multilinePattern *regexp.Regexp
|
multilinePattern *regexp.Regexp
|
||||||
client api
|
client api
|
||||||
messages chan *logger.Message
|
messages chan *logger.Message
|
||||||
|
@ -129,6 +130,8 @@ func New(info logger.Info) (logger.Logger, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logNonBlocking := info.Config["mode"] == "non-blocking"
|
||||||
|
|
||||||
if info.Config[logStreamKey] != "" {
|
if info.Config[logStreamKey] != "" {
|
||||||
logStreamName = info.Config[logStreamKey]
|
logStreamName = info.Config[logStreamKey]
|
||||||
}
|
}
|
||||||
|
@ -142,19 +145,54 @@ func New(info logger.Info) (logger.Logger, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
containerStream := &logStream{
|
containerStream := &logStream{
|
||||||
logStreamName: logStreamName,
|
logStreamName: logStreamName,
|
||||||
logGroupName: logGroupName,
|
logGroupName: logGroupName,
|
||||||
logCreateGroup: logCreateGroup,
|
logCreateGroup: logCreateGroup,
|
||||||
|
logNonBlocking: logNonBlocking,
|
||||||
multilinePattern: multilinePattern,
|
multilinePattern: multilinePattern,
|
||||||
client: client,
|
client: client,
|
||||||
messages: make(chan *logger.Message, 4096),
|
messages: make(chan *logger.Message, 4096),
|
||||||
}
|
}
|
||||||
err = containerStream.create()
|
|
||||||
if err != nil {
|
creationDone := make(chan bool)
|
||||||
return nil, err
|
if logNonBlocking {
|
||||||
|
go func() {
|
||||||
|
backoff := 1
|
||||||
|
maxBackoff := 32
|
||||||
|
for {
|
||||||
|
// If logger is closed we are done
|
||||||
|
containerStream.lock.RLock()
|
||||||
|
if containerStream.closed {
|
||||||
|
containerStream.lock.RUnlock()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
containerStream.lock.RUnlock()
|
||||||
|
err := containerStream.create()
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(backoff) * time.Second)
|
||||||
|
if backoff < maxBackoff {
|
||||||
|
backoff *= 2
|
||||||
|
}
|
||||||
|
logrus.
|
||||||
|
WithError(err).
|
||||||
|
WithField("container-id", info.ContainerID).
|
||||||
|
WithField("container-name", info.ContainerName).
|
||||||
|
Error("Error while trying to initialize awslogs. Retrying in: ", backoff, " seconds")
|
||||||
|
}
|
||||||
|
close(creationDone)
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
if err = containerStream.create(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
close(creationDone)
|
||||||
}
|
}
|
||||||
go containerStream.collectBatch()
|
go containerStream.collectBatch(creationDone)
|
||||||
|
|
||||||
return containerStream, nil
|
return containerStream, nil
|
||||||
}
|
}
|
||||||
|
@ -296,9 +334,18 @@ func (l *logStream) BufSize() int {
|
||||||
func (l *logStream) Log(msg *logger.Message) error {
|
func (l *logStream) Log(msg *logger.Message) error {
|
||||||
l.lock.RLock()
|
l.lock.RLock()
|
||||||
defer l.lock.RUnlock()
|
defer l.lock.RUnlock()
|
||||||
if !l.closed {
|
if l.closed {
|
||||||
l.messages <- msg
|
return errors.New("awslogs is closed")
|
||||||
}
|
}
|
||||||
|
if l.logNonBlocking {
|
||||||
|
select {
|
||||||
|
case l.messages <- msg:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.New("awslogs buffer is full")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.messages <- msg
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +371,9 @@ func (l *logStream) create() error {
|
||||||
return l.createLogStream()
|
return l.createLogStream()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -401,7 +450,9 @@ var newTicker = func(freq time.Duration) *time.Ticker {
|
||||||
// seconds. When events are ready to be processed for submission to CloudWatch
|
// seconds. When events are ready to be processed for submission to CloudWatch
|
||||||
// Logs, the processEvents method is called. If a multiline pattern is not
|
// Logs, the processEvents method is called. If a multiline pattern is not
|
||||||
// configured, log events are submitted to the processEvents method immediately.
|
// configured, log events are submitted to the processEvents method immediately.
|
||||||
func (l *logStream) collectBatch() {
|
func (l *logStream) collectBatch(created chan bool) {
|
||||||
|
// Wait for the logstream/group to be created
|
||||||
|
<-created
|
||||||
ticker := newTicker(batchPublishFrequency)
|
ticker := newTicker(batchPublishFrequency)
|
||||||
var eventBuffer []byte
|
var eventBuffer []byte
|
||||||
var eventBufferTimestamp int64
|
var eventBufferTimestamp int64
|
||||||
|
|
|
@ -201,6 +201,93 @@ func TestCreateAlreadyExists(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLogClosed(t *testing.T) {
|
||||||
|
mockClient := newMockClient()
|
||||||
|
stream := &logStream{
|
||||||
|
client: mockClient,
|
||||||
|
closed: true,
|
||||||
|
}
|
||||||
|
err := stream.Log(&logger.Message{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected non-nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogBlocking(t *testing.T) {
|
||||||
|
mockClient := newMockClient()
|
||||||
|
stream := &logStream{
|
||||||
|
client: mockClient,
|
||||||
|
messages: make(chan *logger.Message),
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCh := make(chan error, 1)
|
||||||
|
started := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
started <- true
|
||||||
|
err := stream.Log(&logger.Message{})
|
||||||
|
errorCh <- err
|
||||||
|
}()
|
||||||
|
<-started
|
||||||
|
select {
|
||||||
|
case err := <-errorCh:
|
||||||
|
t.Fatal("Expected stream.Log to block: ", err)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-stream.messages:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
t.Fatal("Expected to be able to read from stream.messages but was unable to")
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case err := <-errorCh:
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
case <-time.After(30 * time.Second):
|
||||||
|
t.Fatal("timed out waiting for read")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogNonBlockingBufferEmpty(t *testing.T) {
|
||||||
|
mockClient := newMockClient()
|
||||||
|
stream := &logStream{
|
||||||
|
client: mockClient,
|
||||||
|
messages: make(chan *logger.Message, 1),
|
||||||
|
logNonBlocking: true,
|
||||||
|
}
|
||||||
|
err := stream.Log(&logger.Message{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogNonBlockingBufferFull(t *testing.T) {
|
||||||
|
mockClient := newMockClient()
|
||||||
|
stream := &logStream{
|
||||||
|
client: mockClient,
|
||||||
|
messages: make(chan *logger.Message, 1),
|
||||||
|
logNonBlocking: true,
|
||||||
|
}
|
||||||
|
stream.messages <- &logger.Message{}
|
||||||
|
errorCh := make(chan error)
|
||||||
|
started := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
started <- true
|
||||||
|
err := stream.Log(&logger.Message{})
|
||||||
|
errorCh <- err
|
||||||
|
}()
|
||||||
|
<-started
|
||||||
|
select {
|
||||||
|
case err := <-errorCh:
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected non-nil error")
|
||||||
|
}
|
||||||
|
case <-time.After(30 * time.Second):
|
||||||
|
t.Fatal("Expected Log call to not block")
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestPublishBatchSuccess(t *testing.T) {
|
func TestPublishBatchSuccess(t *testing.T) {
|
||||||
mockClient := newMockClient()
|
mockClient := newMockClient()
|
||||||
stream := &logStream{
|
stream := &logStream{
|
||||||
|
@ -410,8 +497,9 @@ func TestCollectBatchSimple(t *testing.T) {
|
||||||
C: ticks,
|
C: ticks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
d := make(chan bool)
|
||||||
go stream.collectBatch()
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
stream.Log(&logger.Message{
|
stream.Log(&logger.Message{
|
||||||
Line: []byte(logline),
|
Line: []byte(logline),
|
||||||
|
@ -454,7 +542,9 @@ func TestCollectBatchTicker(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
stream.Log(&logger.Message{
|
stream.Log(&logger.Message{
|
||||||
Line: []byte(logline + " 1"),
|
Line: []byte(logline + " 1"),
|
||||||
|
@ -526,7 +616,9 @@ func TestCollectBatchMultilinePattern(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
stream.Log(&logger.Message{
|
stream.Log(&logger.Message{
|
||||||
Line: []byte(logline),
|
Line: []byte(logline),
|
||||||
|
@ -580,7 +672,9 @@ func BenchmarkCollectBatch(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
stream.logGenerator(10, 100)
|
stream.logGenerator(10, 100)
|
||||||
ticks <- time.Time{}
|
ticks <- time.Time{}
|
||||||
stream.Close()
|
stream.Close()
|
||||||
|
@ -610,7 +704,9 @@ func BenchmarkCollectBatchMultilinePattern(b *testing.B) {
|
||||||
C: ticks,
|
C: ticks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
stream.logGenerator(10, 100)
|
stream.logGenerator(10, 100)
|
||||||
ticks <- time.Time{}
|
ticks <- time.Time{}
|
||||||
stream.Close()
|
stream.Close()
|
||||||
|
@ -640,7 +736,9 @@ func TestCollectBatchMultilinePatternMaxEventAge(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
stream.Log(&logger.Message{
|
stream.Log(&logger.Message{
|
||||||
Line: []byte(logline),
|
Line: []byte(logline),
|
||||||
|
@ -702,7 +800,9 @@ func TestCollectBatchMultilinePatternNegativeEventAge(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
stream.Log(&logger.Message{
|
stream.Log(&logger.Message{
|
||||||
Line: []byte(logline),
|
Line: []byte(logline),
|
||||||
|
@ -750,7 +850,9 @@ func TestCollectBatchMultilinePatternMaxEventSize(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
// Log max event size
|
// Log max event size
|
||||||
longline := strings.Repeat("A", maximumBytesPerEvent)
|
longline := strings.Repeat("A", maximumBytesPerEvent)
|
||||||
|
@ -801,7 +903,9 @@ func TestCollectBatchClose(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
stream.Log(&logger.Message{
|
stream.Log(&logger.Message{
|
||||||
Line: []byte(logline),
|
Line: []byte(logline),
|
||||||
|
@ -844,7 +948,9 @@ func TestCollectBatchLineSplit(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
longline := strings.Repeat("A", maximumBytesPerEvent)
|
longline := strings.Repeat("A", maximumBytesPerEvent)
|
||||||
stream.Log(&logger.Message{
|
stream.Log(&logger.Message{
|
||||||
|
@ -891,7 +997,9 @@ func TestCollectBatchMaxEvents(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
line := "A"
|
line := "A"
|
||||||
for i := 0; i <= maximumLogEventsPerPut; i++ {
|
for i := 0; i <= maximumLogEventsPerPut; i++ {
|
||||||
|
@ -946,7 +1054,9 @@ func TestCollectBatchMaxTotalBytes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
numPayloads := maximumBytesPerPut / (maximumBytesPerEvent + perEventBytes)
|
numPayloads := maximumBytesPerPut / (maximumBytesPerEvent + perEventBytes)
|
||||||
// maxline is the maximum line that could be submitted after
|
// maxline is the maximum line that could be submitted after
|
||||||
|
@ -1025,7 +1135,9 @@ func TestCollectBatchWithDuplicateTimestamps(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go stream.collectBatch()
|
d := make(chan bool)
|
||||||
|
close(d)
|
||||||
|
go stream.collectBatch(d)
|
||||||
|
|
||||||
times := maximumLogEventsPerPut
|
times := maximumLogEventsPerPut
|
||||||
expectedEvents := []*cloudwatchlogs.InputLogEvent{}
|
expectedEvents := []*cloudwatchlogs.InputLogEvent{}
|
||||||
|
|
Loading…
Add table
Reference in a new issue