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

Fixes #18712. Add rfc5424 log format for syslog.

Previously docker used obsolete rfc3164 syslog format for syslog. rfc3164 explicitly
uses semicolon as a separator between 'TAG' and 'Content' section of the log message.
Docker uses semicolon as a separator between image name and version tag.
When {{.ImageName}} was used as a tag expression and contained ":" syslog parser mistreated
"tag" part of the image name as syslog message body, which resulted in incorrect "syslogtag" been reported by syslog
daemon.
Use of rfc5424 log format partually fixes the issue as it does not use semicolon as a separator.
However using default rfc5424 syslog format itroduces backward incompatability because rsyslog template keyword  %syslogtag%
is parsed differently. In rfc3164 it uses the "TAG" part reported before the "pid" part. In rfc5424 it uses "appname" part reported
before the pid part, while tag part is introduced by %msgid% part.
For more information on rsyslog configuration properties see: http://www.rsyslog.com/doc/master/configuration/properties.html

Added two options to specify logging in either rfc5424, rfc3164 format or unix format omitting hostname in order to keep backwards compatability with
previous versions.

Signed-off-by: Solganik Alexander <solganik@gmail.com>
This commit is contained in:
Solganik Alexander 2016-02-03 22:59:27 +02:00
parent f988741823
commit 1a40dd535f
3 changed files with 89 additions and 0 deletions

View file

@ -13,6 +13,7 @@ import (
"path" "path"
"strconv" "strconv"
"strings" "strings"
"time"
syslog "github.com/RackSec/srslog" syslog "github.com/RackSec/srslog"
@ -64,6 +65,17 @@ func init() {
} }
} }
// rsyslog uses appname part of syslog message to fill in an %syslogtag% template
// attribute in rsyslog.conf. In order to be backward compatible to rfc3164
// tag will be also used as an appname
func rfc5424formatterWithAppNameAsTag(p syslog.Priority, hostname, tag, content string) string {
timestamp := time.Now().Format(time.RFC3339)
pid := os.Getpid()
msg := fmt.Sprintf("<%d>%d %s %s %s %d %s %s",
p, 1, timestamp, hostname, tag, pid, tag, content)
return msg
}
// New creates a syslog logger using the configuration passed in on // New creates a syslog logger using the configuration passed in on
// the context. Supported context configuration variables are // the context. Supported context configuration variables are
// syslog-address, syslog-facility, & syslog-tag. // syslog-address, syslog-facility, & syslog-tag.
@ -83,6 +95,11 @@ func New(ctx logger.Context) (logger.Logger, error) {
return nil, err return nil, err
} }
syslogFormatter, syslogFramer, err := parseLogFormat(ctx.Config["syslog-format"])
if err != nil {
return nil, err
}
logTag := path.Base(os.Args[0]) + "/" + tag logTag := path.Base(os.Args[0]) + "/" + tag
var log *syslog.Writer var log *syslog.Writer
@ -100,6 +117,9 @@ func New(ctx logger.Context) (logger.Logger, error) {
return nil, err return nil, err
} }
log.SetFormatter(syslogFormatter)
log.SetFramer(syslogFramer)
return &syslogger{ return &syslogger{
writer: log, writer: log,
}, nil }, nil
@ -165,6 +185,7 @@ func ValidateLogOpt(cfg map[string]string) error {
case "syslog-tls-key": case "syslog-tls-key":
case "syslog-tls-skip-verify": case "syslog-tls-skip-verify":
case "tag": case "tag":
case "syslog-format":
default: default:
return fmt.Errorf("unknown log opt '%s' for syslog log driver", key) return fmt.Errorf("unknown log opt '%s' for syslog log driver", key)
} }
@ -175,6 +196,9 @@ func ValidateLogOpt(cfg map[string]string) error {
if _, err := parseFacility(cfg["syslog-facility"]); err != nil { if _, err := parseFacility(cfg["syslog-facility"]); err != nil {
return err return err
} }
if _, _, err := parseLogFormat(cfg["syslog-format"]); err != nil {
return err
}
return nil return nil
} }
@ -207,3 +231,17 @@ func parseTLSConfig(cfg map[string]string) (*tls.Config, error) {
return tlsconfig.Client(opts) return tlsconfig.Client(opts)
} }
func parseLogFormat(logFormat string) (syslog.Formatter, syslog.Framer, error) {
switch logFormat {
case "":
return syslog.UnixFormatter, syslog.DefaultFramer, nil
case "rfc3164":
return syslog.RFC3164Formatter, syslog.DefaultFramer, nil
case "rfc5424":
return rfc5424formatterWithAppNameAsTag, syslog.RFC5425MessageLengthFramer, nil
default:
return nil, nil, errors.New("Invalid syslog format")
}
}

View file

@ -0,0 +1,45 @@
// +build linux
package syslog
import (
syslog "github.com/RackSec/srslog"
"reflect"
"testing"
)
func functionMatches(expectedFun interface{}, actualFun interface{}) bool {
return reflect.ValueOf(expectedFun).Pointer() == reflect.ValueOf(actualFun).Pointer()
}
func TestParseLogFormat(t *testing.T) {
formatter, framer, err := parseLogFormat("rfc5424")
if err != nil || !functionMatches(rfc5424formatterWithAppNameAsTag, formatter) ||
!functionMatches(syslog.RFC5425MessageLengthFramer, framer) {
t.Fatal("Failed to parse rfc5424 format", err, formatter, framer)
}
formatter, framer, err = parseLogFormat("rfc3164")
if err != nil || !functionMatches(syslog.RFC3164Formatter, formatter) ||
!functionMatches(syslog.DefaultFramer, framer) {
t.Fatal("Failed to parse rfc3164 format", err, formatter, framer)
}
formatter, framer, err = parseLogFormat("")
if err != nil || !functionMatches(syslog.UnixFormatter, formatter) ||
!functionMatches(syslog.DefaultFramer, framer) {
t.Fatal("Failed to parse empty format", err, formatter, framer)
}
formatter, framer, err = parseLogFormat("invalid")
if err == nil {
t.Fatal("Failed to parse invalid format", err, formatter, framer)
}
}
func TestValidateLogOptEmpty(t *testing.T) {
emptyConfig := make(map[string]string)
if err := ValidateLogOpt(emptyConfig); err != nil {
t.Fatal("Failed to parse empty config", err)
}
}

View file

@ -80,6 +80,7 @@ The following logging options are supported for the `syslog` logging driver:
--log-opt syslog-tls-key=/etc/ca-certificates/custom/key.pem --log-opt syslog-tls-key=/etc/ca-certificates/custom/key.pem
--log-opt syslog-tls-skip-verify=true --log-opt syslog-tls-skip-verify=true
--log-opt tag="mailer" --log-opt tag="mailer"
--log-opt syslog-format=[rfc5424|rfc3164]
`syslog-address` specifies the remote syslog server address where the driver connects to. `syslog-address` specifies the remote syslog server address where the driver connects to.
If not specified it defaults to the local unix socket of the running system. If not specified it defaults to the local unix socket of the running system.
@ -131,6 +132,11 @@ By default, Docker uses the first 12 characters of the container ID to tag log m
Refer to the [log tag option documentation](log_tags.md) for customizing Refer to the [log tag option documentation](log_tags.md) for customizing
the log tag format. the log tag format.
`syslog-format` specifies syslog message format to use when logging.
If not specified it defaults to the local unix syslog format without hostname specification.
Specify rfc3164 to perform logging in RFC-3164 compatible format. Specify rfc5424 to perform
logging in RFC-5424 compatible format
## journald options ## journald options