1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Add an option to specify log format for awslogs driver

Added an option 'awslogs-format' to allow specifying
a log format for the logs sent CloudWatch from the aws log driver.
For now, only the 'json/emf' format is supported.
If no option is provided, the log format header in the
request to CloudWatch will be omitted as before.

Signed-off-by: James Sanders <james3sanders@gmail.com>
This commit is contained in:
James Sanders 2021-09-10 09:05:20 -07:00
parent 1430d849a4
commit 68e3034322
2 changed files with 106 additions and 0 deletions

View file

@ -42,6 +42,7 @@ const (
credentialsEndpointKey = "awslogs-credentials-endpoint"
forceFlushIntervalKey = "awslogs-force-flush-interval-seconds"
maxBufferedEventsKey = "awslogs-max-buffered-events"
logFormatKey = "awslogs-format"
defaultForceFlushInterval = 5 * time.Second
defaultMaxBufferedEvents = 4096
@ -66,6 +67,10 @@ const (
credentialsEndpoint = "http://169.254.170.2"
userAgentHeader = "User-Agent"
// See: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html
logsFormatHeader = "x-amzn-logs-format"
jsonEmfLogFormat = "json/emf"
)
type logStream struct {
@ -404,6 +409,16 @@ func newAWSLogsClient(info logger.Info) (api, error) {
dockerversion.Version, runtime.GOOS, currentAgent))
},
})
if info.Config[logFormatKey] != "" {
client.Handlers.Build.PushBackNamed(request.NamedHandler{
Name: "LogFormatHeaderHandler",
Fn: func(req *request.Request) {
req.HTTPRequest.Header.Set(logsFormatHeader, info.Config[logFormatKey])
},
})
}
return client, nil
}
@ -755,6 +770,7 @@ func ValidateLogOpt(cfg map[string]string) error {
case credentialsEndpointKey:
case forceFlushIntervalKey:
case maxBufferedEventsKey:
case logFormatKey:
default:
return fmt.Errorf("unknown log opt '%s' for %s log driver", key, name)
}
@ -782,6 +798,17 @@ func ValidateLogOpt(cfg map[string]string) error {
if datetimeFormatKeyExists && multilinePatternKeyExists {
return fmt.Errorf("you cannot configure log opt '%s' and '%s' at the same time", datetimeFormatKey, multilinePatternKey)
}
if cfg[logFormatKey] != "" {
// For now, only the "json/emf" log format is supported
if cfg[logFormatKey] != jsonEmfLogFormat {
return fmt.Errorf("unsupported log format '%s'", cfg[logFormatKey])
}
if datetimeFormatKeyExists || multilinePatternKeyExists {
return fmt.Errorf("you cannot configure log opt '%s' or '%s' when log opt '%s' is set to '%s'", datetimeFormatKey, multilinePatternKey, logFormatKey, jsonEmfLogFormat)
}
}
return nil
}

View file

@ -147,6 +147,48 @@ func TestNewAWSLogsClientUserAgentHandler(t *testing.T) {
}
}
func TestNewAWSLogsClientLogFormatHeaderHandler(t *testing.T) {
tests := []struct {
logFormat string
expectedHeaderValue string
}{
{
logFormat: jsonEmfLogFormat,
expectedHeaderValue: "json/emf",
},
{
logFormat: "",
expectedHeaderValue: "",
},
}
for _, tc := range tests {
t.Run(tc.logFormat, func(t *testing.T) {
info := logger.Info{
Config: map[string]string{
regionKey: "us-east-1",
logFormatKey: tc.logFormat,
},
}
client, err := newAWSLogsClient(info)
assert.NilError(t, err)
realClient, ok := client.(*cloudwatchlogs.CloudWatchLogs)
assert.Check(t, ok, "Could not cast client to cloudwatchlogs.CloudWatchLogs")
buildHandlerList := realClient.Handlers.Build
request := &request.Request{
HTTPRequest: &http.Request{
Header: http.Header{},
},
}
buildHandlerList.Run(request)
logFormatHeaderVal := request.HTTPRequest.Header.Get("x-amzn-logs-format")
assert.Equal(t, tc.expectedHeaderValue, logFormatHeaderVal)
})
}
}
func TestNewAWSLogsClientAWSLogsEndpoint(t *testing.T) {
endpoint := "mock-endpoint"
info := logger.Info{
@ -1559,6 +1601,43 @@ func TestValidateLogOptionsMaxBufferedEvents(t *testing.T) {
}
}
func TestValidateLogOptionsFormat(t *testing.T) {
tests := []struct {
format string
multiLinePattern string
datetimeFormat string
expErrMsg string
}{
{"json/emf", "", "", ""},
{"random", "", "", "unsupported log format 'random'"},
{"", "", "", ""},
{"json/emf", "---", "", "you cannot configure log opt 'awslogs-datetime-format' or 'awslogs-multiline-pattern' when log opt 'awslogs-format' is set to 'json/emf'"},
{"json/emf", "", "yyyy-dd-mm", "you cannot configure log opt 'awslogs-datetime-format' or 'awslogs-multiline-pattern' when log opt 'awslogs-format' is set to 'json/emf'"},
}
for i, tc := range tests {
t.Run(fmt.Sprintf("%d/%s", i, tc.format), func(t *testing.T) {
cfg := map[string]string{
logGroupKey: groupName,
logFormatKey: tc.format,
}
if tc.multiLinePattern != "" {
cfg[multilinePatternKey] = tc.multiLinePattern
}
if tc.datetimeFormat != "" {
cfg[datetimeFormatKey] = tc.datetimeFormat
}
err := ValidateLogOpt(cfg)
if tc.expErrMsg != "" {
assert.Error(t, err, tc.expErrMsg)
} else {
assert.NilError(t, err)
}
})
}
}
func TestCreateTagSuccess(t *testing.T) {
mockClient := newMockClient()
info := logger.Info{