diff --git a/daemon/logger/jsonfilelog/jsonfilelog.go b/daemon/logger/jsonfilelog/jsonfilelog.go index 38ff7171b3..490e84ca9c 100644 --- a/daemon/logger/jsonfilelog/jsonfilelog.go +++ b/daemon/logger/jsonfilelog/jsonfilelog.go @@ -41,6 +41,7 @@ type JSONFileLogger struct { ctx logger.Context readers map[*logger.LogWatcher]struct{} // stores the active log followers notifyRotate *pubsub.Publisher + extra []byte // json-encoded extra attributes } func init() { @@ -77,6 +78,16 @@ func New(ctx logger.Context) (logger.Logger, error) { return nil, fmt.Errorf("max-file cannot be less than 1") } } + + var extra []byte + if attrs := ctx.ExtraAttributes(nil); len(attrs) > 0 { + var err error + extra, err = json.Marshal(attrs) + if err != nil { + return nil, err + } + } + return &JSONFileLogger{ f: log, buf: bytes.NewBuffer(nil), @@ -85,6 +96,7 @@ func New(ctx logger.Context) (logger.Logger, error) { n: maxFiles, readers: make(map[*logger.LogWatcher]struct{}), notifyRotate: pubsub.NewPublisher(0, 1), + extra: extra, }, nil } @@ -97,7 +109,12 @@ func (l *JSONFileLogger) Log(msg *logger.Message) error { if err != nil { return err } - err = (&jsonlog.JSONLogs{Log: append(msg.Line, '\n'), Stream: msg.Source, Created: timestamp}).MarshalJSONBuf(l.buf) + err = (&jsonlog.JSONLogs{ + Log: append(msg.Line, '\n'), + Stream: msg.Source, + Created: timestamp, + RawAttrs: l.extra, + }).MarshalJSONBuf(l.buf) if err != nil { return err } @@ -181,6 +198,8 @@ func ValidateLogOpt(cfg map[string]string) error { switch key { case "max-file": case "max-size": + case "labels": + case "env": default: return fmt.Errorf("unknown log opt '%s' for json-file log driver", key) } diff --git a/daemon/logger/jsonfilelog/jsonfilelog_test.go b/daemon/logger/jsonfilelog/jsonfilelog_test.go index cd8a0f7113..162c685b24 100644 --- a/daemon/logger/jsonfilelog/jsonfilelog_test.go +++ b/daemon/logger/jsonfilelog/jsonfilelog_test.go @@ -1,9 +1,11 @@ package jsonfilelog import ( + "encoding/json" "io/ioutil" "os" "path/filepath" + "reflect" "strconv" "testing" "time" @@ -149,3 +151,51 @@ func TestJSONFileLoggerWithOpts(t *testing.T) { } } + +func TestJSONFileLoggerWithLabelsEnv(t *testing.T) { + cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" + tmp, err := ioutil.TempDir("", "docker-logger-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmp) + filename := filepath.Join(tmp, "container.log") + config := map[string]string{"labels": "rack,dc", "env": "environ,debug,ssl"} + l, err := New(logger.Context{ + ContainerID: cid, + LogPath: filename, + Config: config, + ContainerLabels: map[string]string{"rack": "101", "dc": "lhr"}, + ContainerEnv: []string{"environ=production", "debug=false", "port=10001", "ssl=true"}, + }) + if err != nil { + t.Fatal(err) + } + defer l.Close() + if err := l.Log(&logger.Message{ContainerID: cid, Line: []byte("line"), Source: "src1"}); err != nil { + t.Fatal(err) + } + res, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatal(err) + } + + var jsonLog jsonlog.JSONLogs + if err := json.Unmarshal(res, &jsonLog); err != nil { + t.Fatal(err) + } + extra := make(map[string]string) + if err := json.Unmarshal(jsonLog.RawAttrs, &extra); err != nil { + t.Fatal(err) + } + expected := map[string]string{ + "rack": "101", + "dc": "lhr", + "environ": "production", + "debug": "false", + "ssl": "true", + } + if !reflect.DeepEqual(extra, expected) { + t.Fatalf("Wrong log attrs: %q, expected %q", extra, expected) + } +} diff --git a/pkg/jsonlog/jsonlogbytes.go b/pkg/jsonlog/jsonlogbytes.go index b2b1f98489..ff7aaf16e3 100644 --- a/pkg/jsonlog/jsonlogbytes.go +++ b/pkg/jsonlog/jsonlogbytes.go @@ -2,6 +2,7 @@ package jsonlog import ( "bytes" + "encoding/json" "unicode/utf8" ) @@ -12,6 +13,9 @@ type JSONLogs struct { Log []byte `json:"log,omitempty"` Stream string `json:"stream,omitempty"` Created string `json:"time"` + + // json-encoded bytes + RawAttrs json.RawMessage `json:"attrs,omitempty"` } // MarshalJSONBuf is based on the same method from JSONLog @@ -34,6 +38,15 @@ func (mj *JSONLogs) MarshalJSONBuf(buf *bytes.Buffer) error { buf.WriteString(`"stream":`) ffjsonWriteJSONString(buf, mj.Stream) } + if len(mj.RawAttrs) > 0 { + if first == true { + first = false + } else { + buf.WriteString(`,`) + } + buf.WriteString(`"attrs":`) + buf.Write(mj.RawAttrs) + } if first == true { first = false } else { diff --git a/pkg/jsonlog/jsonlogbytes_test.go b/pkg/jsonlog/jsonlogbytes_test.go index 6c36828a03..6d6ad21583 100644 --- a/pkg/jsonlog/jsonlogbytes_test.go +++ b/pkg/jsonlog/jsonlogbytes_test.go @@ -21,6 +21,8 @@ func TestJSONLogsMarshalJSONBuf(t *testing.T) { &JSONLogs{Log: []byte("\u2028 \u2029")}: `^{\"log\":\"\\u2028 \\u2029\",\"time\":}$`, &JSONLogs{Log: []byte{0xaF}}: `^{\"log\":\"\\ufffd\",\"time\":}$`, &JSONLogs{Log: []byte{0x7F}}: `^{\"log\":\"\x7f\",\"time\":}$`, + // with raw attributes + &JSONLogs{Log: []byte("A log line"), RawAttrs: []byte(`{"hello":"world","value":1234}`)}: `^{\"log\":\"A log line\",\"attrs\":{\"hello\":\"world\",\"value\":1234},\"time\":}$`, } for jsonLog, expression := range logs { var buf bytes.Buffer