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

Merge pull request #34174 from aaronlehmann/logattributes

Avoid using a map for log attributes
This commit is contained in:
Sebastiaan van Stijn 2017-07-21 22:31:51 +02:00 committed by GitHub
commit 901fe35bd3
8 changed files with 70 additions and 50 deletions

View file

@ -5,7 +5,6 @@ import (
"io" "io"
"net/url" "net/url"
"sort" "sort"
"strings"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -53,7 +52,8 @@ func WriteLogStream(ctx context.Context, w io.Writer, msgs <-chan *backend.LogMe
} }
logLine := msg.Line logLine := msg.Line
if config.Details { if config.Details {
logLine = append([]byte(stringAttrs(msg.Attrs)+" "), logLine...) logLine = append(attrsByteSlice(msg.Attrs), ' ')
logLine = append(logLine, msg.Line...)
} }
if config.Timestamps { if config.Timestamps {
// TODO(dperny) the format is defined in // TODO(dperny) the format is defined in
@ -71,24 +71,26 @@ func WriteLogStream(ctx context.Context, w io.Writer, msgs <-chan *backend.LogMe
} }
} }
type byKey []string type byKey []backend.LogAttr
func (s byKey) Len() int { return len(s) } func (b byKey) Len() int { return len(b) }
func (s byKey) Less(i, j int) bool { func (b byKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
keyI := strings.Split(s[i], "=") func (b byKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
keyJ := strings.Split(s[j], "=")
return keyI[0] < keyJ[0]
}
func (s byKey) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func stringAttrs(a backend.LogAttributes) string { func attrsByteSlice(a []backend.LogAttr) []byte {
var ss byKey // Note this sorts "a" in-place. That is fine here - nothing else is
for k, v := range a { // going to use Attrs or care about the order.
k, v := url.QueryEscape(k), url.QueryEscape(v) sort.Sort(byKey(a))
ss = append(ss, k+"="+v)
var ret []byte
for i, pair := range a {
k, v := url.QueryEscape(pair.Key), url.QueryEscape(pair.Value)
ret = append(ret, []byte(k)...)
ret = append(ret, '=')
ret = append(ret, []byte(v)...)
if i != len(a)-1 {
ret = append(ret, ',')
}
} }
sort.Sort(ss) return ret
return strings.Join(ss, ",")
} }

View file

@ -35,7 +35,7 @@ type LogMessage struct {
Line []byte Line []byte
Source string Source string
Timestamp time.Time Timestamp time.Time
Attrs LogAttributes Attrs []LogAttr
Partial bool Partial bool
// Err is an error associated with a message. Completeness of a message // Err is an error associated with a message. Completeness of a message
@ -44,9 +44,11 @@ type LogMessage struct {
Err error Err error
} }
// LogAttributes is used to hold the extra attributes available in the log message // LogAttr is used to hold the extra attributes available in the log message.
// Primarily used for converting the map type to string and sorting. type LogAttr struct {
type LogAttributes map[string]string Key string
Value string
}
// LogSelector is a list of services and tasks that should be returned as part // LogSelector is a list of services and tasks that should be returned as part
// of a log stream. It is similar to swarmapi.LogSelector, with the difference // of a log stream. It is similar to swarmapi.LogSelector, with the difference

View file

@ -524,10 +524,12 @@ func (r *controller) Logs(ctx context.Context, publisher exec.LogPublisher, opti
} }
// parse the details out of the Attrs map // parse the details out of the Attrs map
attrs := []api.LogAttr{} var attrs []api.LogAttr
for k, v := range msg.Attrs { if len(msg.Attrs) != 0 {
attr := api.LogAttr{Key: k, Value: v} attrs = make([]api.LogAttr, 0, len(msg.Attrs))
attrs = append(attrs, attr) for _, attr := range msg.Attrs {
attrs = append(attrs, api.LogAttr{Key: attr.Key, Value: attr.Value})
}
} }
if err := publisher.Publish(ctx, api.LogMessage{ if err := publisher.Publish(ctx, api.LogMessage{

View file

@ -458,22 +458,33 @@ func (c *Cluster) ServiceLogs(ctx context.Context, selector *backend.LogSelector
for _, msg := range subscribeMsg.Messages { for _, msg := range subscribeMsg.Messages {
// make a new message // make a new message
m := new(backend.LogMessage) m := new(backend.LogMessage)
m.Attrs = make(backend.LogAttributes) m.Attrs = make([]backend.LogAttr, 0, len(msg.Attrs)+3)
// add the timestamp, adding the error if it fails // add the timestamp, adding the error if it fails
m.Timestamp, err = gogotypes.TimestampFromProto(msg.Timestamp) m.Timestamp, err = gogotypes.TimestampFromProto(msg.Timestamp)
if err != nil { if err != nil {
m.Err = err m.Err = err
} }
nodeKey := contextPrefix + ".node.id"
serviceKey := contextPrefix + ".service.id"
taskKey := contextPrefix + ".task.id"
// copy over all of the details // copy over all of the details
for _, d := range msg.Attrs { for _, d := range msg.Attrs {
m.Attrs[d.Key] = d.Value switch d.Key {
case nodeKey, serviceKey, taskKey:
// we have the final say over context details (in case there
// is a conflict (if the user added a detail with a context's
// key for some reason))
default:
m.Attrs = append(m.Attrs, backend.LogAttr{Key: d.Key, Value: d.Value})
}
} }
// we have the final say over context details (in case there m.Attrs = append(m.Attrs,
// is a conflict (if the user added a detail with a context's backend.LogAttr{Key: nodeKey, Value: msg.Context.NodeID},
// key for some reason)) backend.LogAttr{Key: serviceKey, Value: msg.Context.ServiceID},
m.Attrs[contextPrefix+".node.id"] = msg.Context.NodeID backend.LogAttr{Key: taskKey, Value: msg.Context.TaskID},
m.Attrs[contextPrefix+".service.id"] = msg.Context.ServiceID )
m.Attrs[contextPrefix+".task.id"] = msg.Context.TaskID
switch msg.Stream { switch msg.Stream {
case swarmapi.LogStreamStdout: case swarmapi.LogStreamStdout:

View file

@ -157,6 +157,7 @@ import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/coreos/go-systemd/journal" "github.com/coreos/go-systemd/journal"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger"
) )
@ -213,14 +214,11 @@ drain:
source = "stdout" source = "stdout"
} }
// Retrieve the values of any variables we're adding to the journal. // Retrieve the values of any variables we're adding to the journal.
attrs := make(map[string]string) var attrs []backend.LogAttr
C.sd_journal_restart_data(j) C.sd_journal_restart_data(j)
for C.get_attribute_field(j, &data, &length) > C.int(0) { for C.get_attribute_field(j, &data, &length) > C.int(0) {
kv := strings.SplitN(C.GoStringN(data, C.int(length)), "=", 2) kv := strings.SplitN(C.GoStringN(data, C.int(length)), "=", 2)
attrs[kv[0]] = kv[1] attrs = append(attrs, backend.LogAttr{Key: kv[0], Value: kv[1]})
}
if len(attrs) == 0 {
attrs = nil
} }
// Send the log message. // Send the log message.
logWatcher.Msg <- &logger.Message{ logWatcher.Msg <- &logger.Message{

View file

@ -12,6 +12,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger"
"github.com/docker/docker/daemon/logger/jsonfilelog/multireader" "github.com/docker/docker/daemon/logger/jsonfilelog/multireader"
"github.com/docker/docker/pkg/filenotify" "github.com/docker/docker/pkg/filenotify"
@ -27,11 +28,18 @@ func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, erro
if err := dec.Decode(l); err != nil { if err := dec.Decode(l); err != nil {
return nil, err return nil, err
} }
var attrs []backend.LogAttr
if len(l.Attrs) != 0 {
attrs = make([]backend.LogAttr, 0, len(l.Attrs))
for k, v := range l.Attrs {
attrs = append(attrs, backend.LogAttr{Key: k, Value: v})
}
}
msg := &logger.Message{ msg := &logger.Message{
Source: l.Stream, Source: l.Stream,
Timestamp: l.Created, Timestamp: l.Created,
Line: []byte(l.Log), Line: []byte(l.Log),
Attrs: l.Attrs, Attrs: attrs,
} }
return msg, nil return msg, nil
} }

View file

@ -68,11 +68,6 @@ func (m *Message) AsLogMessage() *backend.LogMessage {
return (*backend.LogMessage)(m) return (*backend.LogMessage)(m)
} }
// LogAttributes is used to hold the extra attributes available in the log message
// Primarily used for converting the map type to string and sorting.
// Imported here so it can be used internally with less refactoring
type LogAttributes backend.LogAttributes
// Logger is the interface for docker logging drivers. // Logger is the interface for docker logging drivers.
type Logger interface { type Logger interface {
Log(*Message) error Log(*Message) error

View file

@ -1,5 +1,9 @@
package logger package logger
import (
"github.com/docker/docker/api/types/backend"
)
func (m *Message) copy() *Message { func (m *Message) copy() *Message {
msg := &Message{ msg := &Message{
Source: m.Source, Source: m.Source,
@ -8,10 +12,8 @@ func (m *Message) copy() *Message {
} }
if m.Attrs != nil { if m.Attrs != nil {
msg.Attrs = make(map[string]string, len(m.Attrs)) msg.Attrs = make([]backend.LogAttr, len(m.Attrs))
for k, v := range m.Attrs { copy(msg.Attrs, m.Attrs)
msg.Attrs[k] = v
}
} }
msg.Line = append(make([]byte, 0, len(m.Line)), m.Line...) msg.Line = append(make([]byte, 0, len(m.Line)), m.Line...)