add labels/env log option for gelf

this allows gelf logger to collect extra metadata from containers with
`--log-opt labels=label1,label2 --log-opt env=env1,env2`

Additional log field will be prefixed with `_` as per gelf protocol
https://www.graylog.org/resources/gelf/

Signed-off-by: Daniel Dao <dqminh@cloudflare.com>
This commit is contained in:
Daniel Dao 2015-10-04 21:03:11 +00:00 committed by Vincent Demeester
parent 656cdbb0e9
commit 5794a0190d
2 changed files with 68 additions and 36 deletions

View File

@ -22,6 +22,44 @@ type Context struct {
LogPath string
}
// ExtraAttributes returns the user-defined extra attributes (labels,
// environment variables) in key-value format. This can be used by log drivers
// that support metadata to add more context to a log.
func (ctx *Context) ExtraAttributes(keyMod func(string) string) map[string]string {
extra := make(map[string]string)
labels, ok := ctx.Config["labels"]
if ok && len(labels) > 0 {
for _, l := range strings.Split(labels, ",") {
if v, ok := ctx.ContainerLabels[l]; ok {
if keyMod != nil {
l = keyMod(l)
}
extra[l] = v
}
}
}
env, ok := ctx.Config["env"]
if ok && len(env) > 0 {
envMapping := make(map[string]string)
for _, e := range ctx.ContainerEnv {
if kv := strings.SplitN(e, "=", 2); len(kv) == 2 {
envMapping[kv[0]] = kv[1]
}
}
for _, l := range strings.Split(env, ",") {
if v, ok := envMapping[l]; ok {
if keyMod != nil {
l = keyMod(l)
}
extra[l] = v
}
}
}
return extra
}
// Hostname returns the hostname from the underlying OS.
func (ctx *Context) Hostname() (string, error) {
hostname, err := os.Hostname()

View File

@ -21,20 +21,10 @@ import (
const name = "gelf"
type gelfLogger struct {
writer *gelf.Writer
ctx logger.Context
fields gelfFields
}
type gelfFields struct {
hostname string
containerID string
containerName string
imageID string
imageName string
command string
tag string
created time.Time
writer *gelf.Writer
ctx logger.Context
hostname string
extra map[string]interface{}
}
func init() {
@ -71,15 +61,24 @@ func New(ctx logger.Context) (logger.Logger, error) {
return nil, err
}
fields := gelfFields{
hostname: hostname,
containerID: ctx.ContainerID,
containerName: string(containerName),
imageID: ctx.ContainerImageID,
imageName: ctx.ContainerImageName,
command: ctx.Command(),
tag: tag,
created: ctx.ContainerCreated,
extra := map[string]interface{}{
"_container_id": ctx.ContainerID,
"_container_name": string(containerName),
"_image_id": ctx.ContainerImageID,
"_image_name": ctx.ContainerImageName,
"_command": ctx.Command(),
"_tag": tag,
"_created": ctx.ContainerCreated,
}
extraAttrs := ctx.ExtraAttributes(func(key string) string {
if key[0] == '_' {
return key
}
return "_" + key
})
for k, v := range extraAttrs {
extra[k] = v
}
// create new gelfWriter
@ -89,9 +88,10 @@ func New(ctx logger.Context) (logger.Logger, error) {
}
return &gelfLogger{
writer: gelfWriter,
ctx: ctx,
fields: fields,
writer: gelfWriter,
ctx: ctx,
hostname: hostname,
extra: extra,
}, nil
}
@ -106,19 +106,11 @@ func (s *gelfLogger) Log(msg *logger.Message) error {
m := gelf.Message{
Version: "1.1",
Host: s.fields.hostname,
Host: s.hostname,
Short: string(short),
TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0,
Level: level,
Extra: map[string]interface{}{
"_container_id": s.fields.containerID,
"_container_name": s.fields.containerName,
"_image_id": s.fields.imageID,
"_image_name": s.fields.imageName,
"_command": s.fields.command,
"_tag": s.fields.tag,
"_created": s.fields.created,
},
Extra: s.extra,
}
if err := s.writer.WriteMessage(&m); err != nil {
@ -143,6 +135,8 @@ func ValidateLogOpt(cfg map[string]string) error {
case "gelf-address":
case "gelf-tag":
case "tag":
case "labels":
case "env":
default:
return fmt.Errorf("unknown log opt '%s' for gelf log driver", key)
}