Merge pull request #15384 from phil-monroe/15058-include-name-in-syslog-tag
log driver - Interpolate fields into log tag Upstream-commit: 4dfa996cc246691de5e6f509939b96e8643ac228 Component: engine
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Context provides enough information for a logging driver to do its function.
|
||||
type Context struct {
|
||||
Config map[string]string
|
||||
ContainerID string
|
||||
ContainerName string
|
||||
ContainerEntrypoint string
|
||||
ContainerArgs []string
|
||||
ContainerImageID string
|
||||
ContainerImageName string
|
||||
ContainerCreated time.Time
|
||||
LogPath string
|
||||
}
|
||||
|
||||
// Hostname returns the hostname from the underlying OS.
|
||||
func (ctx *Context) Hostname() (string, error) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("logger: can not resolve hostname: %v", err)
|
||||
}
|
||||
return hostname, nil
|
||||
}
|
||||
|
||||
// Command returns the command that the container being logged was
|
||||
// started with. The Entrypoint is prepended to the container
|
||||
// arguments.
|
||||
func (ctx *Context) Command() string {
|
||||
terms := []string{ctx.ContainerEntrypoint}
|
||||
for _, arg := range ctx.ContainerArgs {
|
||||
terms = append(terms, arg)
|
||||
}
|
||||
command := strings.Join(terms, " ")
|
||||
return command
|
||||
}
|
||||
|
||||
// ID Returns the Container ID shortened to 12 characters.
|
||||
func (ctx *Context) ID() string {
|
||||
return ctx.ContainerID[:12]
|
||||
}
|
||||
|
||||
// FullID is an alias of ContainerID.
|
||||
func (ctx *Context) FullID() string {
|
||||
return ctx.ContainerID
|
||||
}
|
||||
|
||||
// Name returns the ContainerName without a preceding '/'.
|
||||
func (ctx *Context) Name() string {
|
||||
return ctx.ContainerName[1:]
|
||||
}
|
||||
|
||||
// ImageID returns the ContainerImageID shortened to 12 characters.
|
||||
func (ctx *Context) ImageID() string {
|
||||
return ctx.ContainerImageID[:12]
|
||||
}
|
||||
|
||||
// ImageFullID is an alias of ContainerID.
|
||||
func (ctx *Context) ImageFullID() string {
|
||||
return ctx.ContainerImageID
|
||||
}
|
||||
|
||||
// ImageName is an alias of ContainerImageName
|
||||
func (ctx *Context) ImageName() string {
|
||||
return ctx.ContainerImageName
|
||||
}
|
||||
@@ -2,10 +2,7 @@ package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Creator builds a logging driver instance with given context.
|
||||
@@ -15,40 +12,6 @@ type Creator func(Context) (Logger, error)
|
||||
// logging implementation.
|
||||
type LogOptValidator func(cfg map[string]string) error
|
||||
|
||||
// Context provides enough information for a logging driver to do its function.
|
||||
type Context struct {
|
||||
Config map[string]string
|
||||
ContainerID string
|
||||
ContainerName string
|
||||
ContainerEntrypoint string
|
||||
ContainerArgs []string
|
||||
ContainerImageID string
|
||||
ContainerImageName string
|
||||
ContainerCreated time.Time
|
||||
LogPath string
|
||||
}
|
||||
|
||||
// Hostname returns the hostname from the underlying OS.
|
||||
func (ctx *Context) Hostname() (string, error) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("logger: can not resolve hostname: %v", err)
|
||||
}
|
||||
return hostname, nil
|
||||
}
|
||||
|
||||
// Command returns the command that the container being logged was
|
||||
// started with. The Entrypoint is prepended to the container
|
||||
// arguments.
|
||||
func (ctx *Context) Command() string {
|
||||
terms := []string{ctx.ContainerEntrypoint}
|
||||
for _, arg := range ctx.ContainerArgs {
|
||||
terms = append(terms, arg)
|
||||
}
|
||||
command := strings.Join(terms, " ")
|
||||
return command
|
||||
}
|
||||
|
||||
type logdriverFactory struct {
|
||||
registry map[string]Creator
|
||||
optValidator map[string]LogOptValidator
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
package fluentd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/loggerutils"
|
||||
"github.com/fluent/fluent-logger-golang/fluent"
|
||||
)
|
||||
|
||||
@@ -23,12 +22,6 @@ type fluentd struct {
|
||||
writer *fluent.Fluent
|
||||
}
|
||||
|
||||
type receiver struct {
|
||||
ID string
|
||||
FullID string
|
||||
Name string
|
||||
}
|
||||
|
||||
const (
|
||||
name = "fluentd"
|
||||
defaultHostName = "localhost"
|
||||
@@ -48,10 +41,14 @@ func init() {
|
||||
func parseConfig(ctx logger.Context) (string, int, string, error) {
|
||||
host := defaultHostName
|
||||
port := defaultPort
|
||||
tag := "docker." + ctx.ContainerID[:12]
|
||||
|
||||
config := ctx.Config
|
||||
|
||||
tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}")
|
||||
if err != nil {
|
||||
return "", 0, "", err
|
||||
}
|
||||
|
||||
if address := config["fluentd-address"]; address != "" {
|
||||
if h, p, err := net.SplitHostPort(address); err != nil {
|
||||
if !strings.Contains(err.Error(), "missing port in address") {
|
||||
@@ -68,23 +65,6 @@ func parseConfig(ctx logger.Context) (string, int, string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if config["fluentd-tag"] != "" {
|
||||
receiver := &receiver{
|
||||
ID: ctx.ContainerID[:12],
|
||||
FullID: ctx.ContainerID,
|
||||
Name: ctx.ContainerName,
|
||||
}
|
||||
tmpl, err := template.New("tag").Parse(config["fluentd-tag"])
|
||||
if err != nil {
|
||||
return "", 0, "", err
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if err := tmpl.Execute(buf, receiver); err != nil {
|
||||
return "", 0, "", err
|
||||
}
|
||||
tag = buf.String()
|
||||
}
|
||||
|
||||
return host, port, tag, nil
|
||||
}
|
||||
|
||||
@@ -130,6 +110,7 @@ func ValidateLogOpt(cfg map[string]string) error {
|
||||
switch key {
|
||||
case "fluentd-address":
|
||||
case "fluentd-tag":
|
||||
case "tag":
|
||||
default:
|
||||
return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/Graylog2/go-gelf/gelf"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/loggerutils"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
)
|
||||
|
||||
@@ -64,6 +65,12 @@ func New(ctx logger.Context) (logger.Logger, error) {
|
||||
// remove trailing slash from container name
|
||||
containerName := bytes.TrimLeft([]byte(ctx.ContainerName), "/")
|
||||
|
||||
// parse log tag
|
||||
tag, err := loggerutils.ParseLogTag(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fields := gelfFields{
|
||||
hostname: hostname,
|
||||
containerID: ctx.ContainerID,
|
||||
@@ -71,7 +78,7 @@ func New(ctx logger.Context) (logger.Logger, error) {
|
||||
imageID: ctx.ContainerImageID,
|
||||
imageName: ctx.ContainerImageName,
|
||||
command: ctx.Command(),
|
||||
tag: ctx.Config["gelf-tag"],
|
||||
tag: tag,
|
||||
created: ctx.ContainerCreated,
|
||||
}
|
||||
|
||||
@@ -135,6 +142,7 @@ func ValidateLogOpt(cfg map[string]string) error {
|
||||
switch key {
|
||||
case "gelf-address":
|
||||
case "gelf-tag":
|
||||
case "tag":
|
||||
default:
|
||||
return fmt.Errorf("unknown log opt '%s' for gelf log driver", key)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package loggerutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
)
|
||||
|
||||
// ParseLogTag generates a context aware tag for consistency across different
|
||||
// log drivers based on the context of the running container.
|
||||
func ParseLogTag(ctx logger.Context, defaultTemplate string) (string, error) {
|
||||
tagTemplate := lookupTagTemplate(ctx, defaultTemplate)
|
||||
|
||||
tmpl, err := template.New("log-tag").Parse(tagTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if err := tmpl.Execute(buf, &ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func lookupTagTemplate(ctx logger.Context, defaultTemplate string) string {
|
||||
tagTemplate := ctx.Config["tag"]
|
||||
|
||||
deprecatedConfigs := []string{"syslog-tag", "gelf-tag", "fluentd-tag"}
|
||||
for i := 0; tagTemplate == "" && i < len(deprecatedConfigs); i++ {
|
||||
cfg := deprecatedConfigs[i]
|
||||
if ctx.Config[cfg] != "" {
|
||||
tagTemplate = ctx.Config[cfg]
|
||||
logrus.Warn(fmt.Sprintf("Using log tag from deprecated log-opt '%s'. Please use: --log-opt tag=\"%s\"", cfg, tagTemplate))
|
||||
}
|
||||
}
|
||||
|
||||
if tagTemplate == "" {
|
||||
tagTemplate = defaultTemplate
|
||||
}
|
||||
|
||||
return tagTemplate
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package loggerutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
)
|
||||
|
||||
func TestParseLogTagDefaultTag(t *testing.T) {
|
||||
ctx := buildContext(map[string]string{})
|
||||
tag, e := ParseLogTag(ctx, "{{.ID}}")
|
||||
assertTag(t, e, tag, ctx.ID())
|
||||
}
|
||||
|
||||
func TestParseLogTag(t *testing.T) {
|
||||
ctx := buildContext(map[string]string{"tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"})
|
||||
tag, e := ParseLogTag(ctx, "{{.ID}}")
|
||||
assertTag(t, e, tag, "test-image/test-container/container-ab")
|
||||
}
|
||||
|
||||
func TestParseLogTagSyslogTag(t *testing.T) {
|
||||
ctx := buildContext(map[string]string{"syslog-tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"})
|
||||
tag, e := ParseLogTag(ctx, "{{.ID}}")
|
||||
assertTag(t, e, tag, "test-image/test-container/container-ab")
|
||||
}
|
||||
|
||||
func TestParseLogTagGelfTag(t *testing.T) {
|
||||
ctx := buildContext(map[string]string{"gelf-tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"})
|
||||
tag, e := ParseLogTag(ctx, "{{.ID}}")
|
||||
assertTag(t, e, tag, "test-image/test-container/container-ab")
|
||||
}
|
||||
|
||||
func TestParseLogTagFluentdTag(t *testing.T) {
|
||||
ctx := buildContext(map[string]string{"fluentd-tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"})
|
||||
tag, e := ParseLogTag(ctx, "{{.ID}}")
|
||||
assertTag(t, e, tag, "test-image/test-container/container-ab")
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
func buildContext(cfg map[string]string) logger.Context {
|
||||
return logger.Context{
|
||||
ContainerID: "container-abcdefghijklmnopqrstuvwxyz01234567890",
|
||||
ContainerName: "/test-container",
|
||||
ContainerImageID: "image-abcdefghijklmnopqrstuvwxyz01234567890",
|
||||
ContainerImageName: "test-image",
|
||||
Config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func assertTag(t *testing.T, e error, tag string, expected string) {
|
||||
if e != nil {
|
||||
t.Fatalf("Error generating tag: %q", e)
|
||||
}
|
||||
if tag != expected {
|
||||
t.Fatalf("Wrong tag: %q, should be %q", tag, expected)
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/loggerutils"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
)
|
||||
|
||||
@@ -61,9 +62,9 @@ func init() {
|
||||
// the context. Supported context configuration variables are
|
||||
// syslog-address, syslog-facility, & syslog-tag.
|
||||
func New(ctx logger.Context) (logger.Logger, error) {
|
||||
tag := ctx.Config["syslog-tag"]
|
||||
if tag == "" {
|
||||
tag = ctx.ContainerID[:12]
|
||||
tag, err := loggerutils.ParseLogTag(ctx, "{{.ID}}")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proto, address, err := parseAddress(ctx.Config["syslog-address"])
|
||||
@@ -146,6 +147,7 @@ func ValidateLogOpt(cfg map[string]string) error {
|
||||
case "syslog-address":
|
||||
case "syslog-facility":
|
||||
case "syslog-tag":
|
||||
case "tag":
|
||||
default:
|
||||
return fmt.Errorf("unknown log opt '%s' for syslog log driver", key)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user