From 49365caca14dc373c1ee54f023aec4f9d3b0f41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Min=C3=A1=C5=99?= Date: Mon, 24 Jul 2017 12:29:59 +0200 Subject: [PATCH 1/2] bump(github.com/bshuster-repo/logrus-logstash-hook): 0e6d502573042a6563419fbcce4f284600bfe929 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A dependency for Sirupsen.logrus to enable logstash formatter. Signed-off-by: Michal Minář --- Godeps/Godeps.json | 5 + .../logrus-logstash-hook/.gitignore | 26 ++ .../logrus-logstash-hook/.travis.yml | 19 ++ .../logrus-logstash-hook/AUTHORS.md | 2 + .../logrus-logstash-hook/LICENSE | 21 ++ .../logrus-logstash-hook/README.md | 95 ++++++ .../logrus-logstash-hook/logstash.go | 129 ++++++++ .../logstash_formatter.go | 80 +++++ .../logstash_formatter_test.go | 72 +++++ .../logrus-logstash-hook/logstash_test.go | 299 ++++++++++++++++++ 10 files changed, 748 insertions(+) create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/.gitignore create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/.travis.yml create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/AUTHORS.md create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/LICENSE create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/README.md create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash.go create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter.go create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter_test.go create mode 100644 vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 1fe4a553e8f0..72f9c617a703 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -398,6 +398,11 @@ "Comment": "v1.3.0", "Rev": "583e8937c61f1af6513608ccc75c97b6abdf4ff9" }, + { + "ImportPath": "github.com/bshuster-repo/logrus-logstash-hook", + "Comment": "v0.3", + "Rev": "0e6d502573042a6563419fbcce4f284600bfe929" + }, { "ImportPath": "github.com/chai2010/gettext-go/gettext", "Rev": "c6fed771bfd517099caf0f7a961671fa8ed08723" diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/.gitignore b/vendor/github.com/bshuster-repo/logrus-logstash-hook/.gitignore new file mode 100644 index 000000000000..42067232927d --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/.gitignore @@ -0,0 +1,26 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test +.idea + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +*.iml diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/.travis.yml b/vendor/github.com/bshuster-repo/logrus-logstash-hook/.travis.yml new file mode 100644 index 000000000000..60c00ef66295 --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/.travis.yml @@ -0,0 +1,19 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.3 + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: tip + +install: + - # Skip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go tool vet . + - go test -v -race ./... diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/AUTHORS.md b/vendor/github.com/bshuster-repo/logrus-logstash-hook/AUTHORS.md new file mode 100644 index 000000000000..40edb335ee01 --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/AUTHORS.md @@ -0,0 +1,2 @@ +# AUTHORS +- Boaz Shuster [ripcurld00d](https://github.com/ripcurld00d) @[ripcurld0](https://twitter.com/ripcurld0) diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/LICENSE b/vendor/github.com/bshuster-repo/logrus-logstash-hook/LICENSE new file mode 100644 index 000000000000..3fb4442f8499 --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Boaz Shuster + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/README.md b/vendor/github.com/bshuster-repo/logrus-logstash-hook/README.md new file mode 100644 index 000000000000..0f281b500e60 --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/README.md @@ -0,0 +1,95 @@ +# Logstash hook for logrus :walrus: [![Build Status](https://travis-ci.org/bshuster-repo/logrus-logstash-hook.svg?branch=master)](https://travis-ci.org/bshuster-repo/logrus-logstash-hook) +Use this hook to send the logs to [Logstash](https://www.elastic.co/products/logstash) over both UDP and TCP. + +## Usage + +```go +package main + +import ( + "github.com/Sirupsen/logrus" + "github.com/bshuster-repo/logrus-logstash-hook" +) + +func main() { + log := logrus.New() + hook, err := logrus_logstash.NewHook("tcp", "172.17.0.2:9999", "myappName") + + if err != nil { + log.Fatal(err) + } + log.Hooks.Add(hook) + ctx := log.WithFields(logrus.Fields{ + "method": "main", + }) + ... + ctx.Info("Hello World!") +} +``` + +This is how it will look like: + +```ruby +{ + "@timestamp" => "2016-02-29T16:57:23.000Z", + "@version" => "1", + "level" => "info", + "message" => "Hello World!", + "method" => "main", + "host" => "172.17.0.1", + "port" => 45199, + "type" => "myappName" +} +``` +## Hook Fields +Fields can be added to the hook, which will always be in the log context. +This can be done when creating the hook: + +```go + +hook, err := logrus_logstash.NewHookWithFields("tcp", "172.17.0.2:9999", "myappName", logrus.Fields{ + "hostname": os.Hostname(), + "serviceName": "myServiceName", +}) +``` + +Or afterwards: + +```go + +hook.WithFields(logrus.Fields{ + "hostname": os.Hostname(), + "serviceName": "myServiceName", +}) +``` +This allows you to set up the hook so logging is available immediately, and add important fields as they become available. + +Single fields can be added/updated using 'WithField': + +```go + +hook.WithField("status", "running") +``` + + + +## Field prefix + +The hook allows you to send logging to logstash and also retain the default std output in text format. +However to keep this console output readable some fields might need to be omitted from the default non-hooked log output. +Each hook can be configured with a prefix used to identify fields which are only to be logged to the logstash connection. +For example if you don't want to see the hostname and serviceName on each log line in the console output you can add a prefix: + +```go + + +hook, err := logrus_logstash.NewHookWithFields("tcp", "172.17.0.2:9999", "myappName", logrus.Fields{ + "_hostname": os.Hostname(), + "_serviceName": "myServiceName", +}) +... +hook.WithPrefix("_") +``` + +There are also constructors available which allow you to specify the prefix from the start. +The std-out will not have the '\_hostname' and '\_servicename' fields, and the logstash output will, but the prefix will be dropped from the name. diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash.go b/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash.go new file mode 100644 index 000000000000..baa61dc25b98 --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash.go @@ -0,0 +1,129 @@ +package logrus_logstash + +import ( + "net" + "strings" + + "github.com/Sirupsen/logrus" +) + +// Hook represents a connection to a Logstash instance +type Hook struct { + conn net.Conn + appName string + alwaysSentFields logrus.Fields + hookOnlyPrefix string +} + +// NewHook creates a new hook to a Logstash instance, which listens on +// `protocol`://`address`. +func NewHook(protocol, address, appName string) (*Hook, error) { + return NewHookWithFields(protocol, address, appName, make(logrus.Fields)) +} + +// NewHookWithConn creates a new hook to a Logstash instance, using the supplied connection +func NewHookWithConn(conn net.Conn, appName string) (*Hook, error) { + return NewHookWithFieldsAndConn(conn, appName, make(logrus.Fields)) +} + +// NewHookWithFields creates a new hook to a Logstash instance, which listens on +// `protocol`://`address`. alwaysSentFields will be sent with every log entry. +func NewHookWithFields(protocol, address, appName string, alwaysSentFields logrus.Fields) (*Hook, error) { + return NewHookWithFieldsAndPrefix(protocol, address, appName, alwaysSentFields, "") +} + +// NewHookWithFieldsAndPrefix creates a new hook to a Logstash instance, which listens on +// `protocol`://`address`. alwaysSentFields will be sent with every log entry. prefix is used to select fields to filter +func NewHookWithFieldsAndPrefix(protocol, address, appName string, alwaysSentFields logrus.Fields, prefix string) (*Hook, error) { + conn, err := net.Dial(protocol, address) + if err != nil { + return nil, err + } + return NewHookWithFieldsAndConnAndPrefix(conn, appName, alwaysSentFields, prefix) +} + +// NewHookWithFieldsAndConn creates a new hook to a Logstash instance using the supplied connection +func NewHookWithFieldsAndConn(conn net.Conn, appName string, alwaysSentFields logrus.Fields) (*Hook, error) { + return NewHookWithFieldsAndConnAndPrefix(conn, appName, alwaysSentFields, "") +} + +//NewHookWithFieldsAndConnAndPrefix creates a new hook to a Logstash instance using the suppolied connection and prefix +func NewHookWithFieldsAndConnAndPrefix(conn net.Conn, appName string, alwaysSentFields logrus.Fields, prefix string) (*Hook, error) { + return &Hook{conn: conn, appName: appName, alwaysSentFields: alwaysSentFields, hookOnlyPrefix: prefix}, nil +} + +//NewFilterHook makes a new hook which does not forward to logstash, but simply enforces the prefix rules +func NewFilterHook() *Hook { + return NewFilterHookWithPrefix("") +} + +//NewFilterHookWithPrefix make a new hook which does not forward to logstash, but simply enforces the specified prefix +func NewFilterHookWithPrefix(prefix string) *Hook { + return &Hook{conn: nil, appName: "", alwaysSentFields: make(logrus.Fields), hookOnlyPrefix: prefix} +} + +func (h *Hook) filterHookOnly(entry *logrus.Entry) { + if h.hookOnlyPrefix != "" { + for key := range entry.Data { + if strings.HasPrefix(key, h.hookOnlyPrefix) { + delete(entry.Data, key) + } + } + } + +} + +//WithPrefix sets a prefix filter to use in all subsequent logging +func (h *Hook) WithPrefix(prefix string) { + h.hookOnlyPrefix = prefix +} + +func (h *Hook) WithField(key string, value interface{}) { + h.alwaysSentFields[key] = value +} + +func (h *Hook) WithFields(fields logrus.Fields) { + //Add all the new fields to the 'alwaysSentFields', possibly overwriting exising fields + for key, value := range fields { + h.alwaysSentFields[key] = value + } +} + +func (h *Hook) Fire(entry *logrus.Entry) error { + //make sure we always clear the hookonly fields from the entry + defer h.filterHookOnly(entry) + + // Add in the alwaysSentFields. We don't override fields that are already set. + for k, v := range h.alwaysSentFields { + if _, inMap := entry.Data[k]; !inMap { + entry.Data[k] = v + } + } + + //For a filteringHook, stop here + if h.conn == nil { + return nil + } + + formatter := LogstashFormatter{Type: h.appName} + + dataBytes, err := formatter.FormatWithPrefix(entry, h.hookOnlyPrefix) + if err != nil { + return err + } + if _, err = h.conn.Write(dataBytes); err != nil { + return err + } + return nil +} + +func (h *Hook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + logrus.WarnLevel, + logrus.InfoLevel, + logrus.DebugLevel, + } +} diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter.go b/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter.go new file mode 100644 index 000000000000..0ca0fc959bb6 --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter.go @@ -0,0 +1,80 @@ +package logrus_logstash + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/Sirupsen/logrus" +) + +// Formatter generates json in logstash format. +// Logstash site: http://logstash.net/ +type LogstashFormatter struct { + Type string // if not empty use for logstash type field. + + // TimestampFormat sets the format used for timestamps. + TimestampFormat string +} + +func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { + return f.FormatWithPrefix(entry, "") +} + +func (f *LogstashFormatter) FormatWithPrefix(entry *logrus.Entry, prefix string) ([]byte, error) { + fields := make(logrus.Fields) + for k, v := range entry.Data { + //remvove the prefix when sending the fields to logstash + if prefix != "" && strings.HasPrefix(k, prefix) { + k = strings.TrimPrefix(k, prefix) + } + + switch v := v.(type) { + case error: + // Otherwise errors are ignored by `encoding/json` + // https://github.com/Sirupsen/logrus/issues/377 + fields[k] = v.Error() + default: + fields[k] = v + } + } + + fields["@version"] = "1" + + timeStampFormat := f.TimestampFormat + + if timeStampFormat == "" { + timeStampFormat = logrus.DefaultTimestampFormat + } + + fields["@timestamp"] = entry.Time.Format(timeStampFormat) + + // set message field + v, ok := entry.Data["message"] + if ok { + fields["fields.message"] = v + } + fields["message"] = entry.Message + + // set level field + v, ok = entry.Data["level"] + if ok { + fields["fields.level"] = v + } + fields["level"] = entry.Level.String() + + // set type field + if f.Type != "" { + v, ok = entry.Data["type"] + if ok { + fields["fields.type"] = v + } + fields["type"] = f.Type + } + + serialized, err := json.Marshal(fields) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter_test.go b/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter_test.go new file mode 100644 index 000000000000..a355ac04326a --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter_test.go @@ -0,0 +1,72 @@ +package logrus_logstash + +import ( + "bytes" + "encoding/json" + "fmt" + "net/url" + "testing" + + "github.com/Sirupsen/logrus" +) + +func TestLogstashFormatter(t *testing.T) { + lf := LogstashFormatter{Type: "abc"} + + fields := logrus.Fields{ + "message": "def", + "level": "ijk", + "type": "lmn", + "one": 1, + "pi": 3.14, + "bool": true, + "error": &url.Error{Op: "Get", URL: "http://example.com", Err: fmt.Errorf("The error")}, + } + + entry := logrus.WithFields(fields) + entry.Message = "msg" + entry.Level = logrus.InfoLevel + + b, _ := lf.Format(entry) + + var data map[string]interface{} + dec := json.NewDecoder(bytes.NewReader(b)) + dec.UseNumber() + dec.Decode(&data) + + // base fields + if data["@timestamp"] == "" { + t.Error("expected @timestamp to be not empty") + } + tt := []struct { + expected string + key string + }{ + // base fields + {"1", "@version"}, + {"abc", "type"}, + {"msg", "message"}, + {"info", "level"}, + {"Get http://example.com: The error", "error"}, + // substituted fields + {"def", "fields.message"}, + {"ijk", "fields.level"}, + {"lmn", "fields.type"}, + } + for _, te := range tt { + if te.expected != data[te.key] { + t.Errorf("expected data[%s] to be '%s' but got '%s'", te.key, te.expected, data[te.key]) + } + } + + // formats + if json.Number("1") != data["one"] { + t.Errorf("expected one to be '%v' but got '%v'", json.Number("1"), data["one"]) + } + if json.Number("3.14") != data["pi"] { + t.Errorf("expected pi to be '%v' but got '%v'", json.Number("3.14"), data["pi"]) + } + if true != data["bool"] { + t.Errorf("expected bool to be '%v' but got '%v'", true, data["bool"]) + } +} diff --git a/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_test.go b/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_test.go new file mode 100644 index 000000000000..136a1bf3fec0 --- /dev/null +++ b/vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_test.go @@ -0,0 +1,299 @@ +package logrus_logstash + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "reflect" + "testing" + "time" + + "github.com/Sirupsen/logrus" +) + +func TestLegostashHook(t *testing.T) { + type Expct struct { + appName string + hookOnlyPrefix string + alwaysSentFields logrus.Fields + } + tt := []struct { + expected Expct + initFunc func() (*Hook, error) + }{ + {Expct{"bla", "", nil}, func() (*Hook, error) { + return NewHook("udp", "localhost:9999", "bla") + }}, + {Expct{"bzz", "", nil}, func() (*Hook, error) { + udpConn, err := net.Dial("udp", "localhost:9999") + if err != nil { + return nil, err + } + return NewHookWithConn(udpConn, "bzz") + }}, + {Expct{"blk", "", logrus.Fields{"id": 1}}, func() (*Hook, error) { + return NewHookWithFields("udp", "localhost:9999", "blk", logrus.Fields{"id": 1}) + }}, + {Expct{"prefix", "-->", logrus.Fields{"id": 1}}, func() (*Hook, error) { + return NewHookWithFieldsAndPrefix("udp", "localhost:9999", "prefix", logrus.Fields{"id": 1}, "-->") + }}, + {Expct{"fieldsconn", "", logrus.Fields{"id": 5}}, func() (*Hook, error) { + udpConn, err := net.Dial("udp", "localhost:9999") + if err != nil { + return nil, err + } + return NewHookWithFieldsAndConn(udpConn, "fieldsconn", logrus.Fields{"id": 5}) + }}, + {Expct{"zz", "~~>", logrus.Fields{"id": "bal"}}, func() (*Hook, error) { + udpConn, err := net.Dial("udp", "localhost:9999") + if err != nil { + return nil, err + } + return NewHookWithFieldsAndConnAndPrefix(udpConn, "zz", logrus.Fields{"id": "bal"}, "~~>") + }}, + } + + for _, te := range tt { + h, err := te.initFunc() + if err != nil { + t.Error(err) + } + if h == nil { + t.Error("expected hook to be not nil") + } + + if h.conn == nil { + t.Error("expected conn to be not nil") + } + if h.appName != te.expected.appName { + t.Errorf("expected appName to be '%s' but got '%s'", te.expected.appName, h.appName) + } + if h.alwaysSentFields == nil { + t.Error("expected alwaysSentFields to be not nil") + } + if te.expected.alwaysSentFields != nil && !reflect.DeepEqual(te.expected.alwaysSentFields, h.alwaysSentFields) { + t.Errorf("expected alwaysSentFields to be '%v' but got '%v'", te.expected.alwaysSentFields, h.alwaysSentFields) + } + if h.hookOnlyPrefix != te.expected.hookOnlyPrefix { + t.Error("expected hookOnlyPrefix to be an empty string") + } + } +} + +func TestNewFiltering(t *testing.T) { + type Expct struct { + prefix string + appName string + } + tt := []struct { + expected Expct + initFunc func() *Hook + }{ + {Expct{"", ""}, func() *Hook { + return NewFilterHook() + }}, + {Expct{"~~~>", ""}, func() *Hook { + return NewFilterHookWithPrefix("~~~>") + }}, + } + + for _, te := range tt { + h := te.initFunc() + if h.conn != nil { + t.Error("expected conn to be nil") + } + if h.alwaysSentFields == nil { + t.Error("expected alwaysSentFields to be not nil") + } + if h.hookOnlyPrefix != te.expected.prefix { + t.Errorf("expected prefix to be '%s' but got '%s'", te.expected.prefix, h.hookOnlyPrefix) + } + } +} + +func TestSettingAttributes(t *testing.T) { + tt := []struct { + setFunc func(*Hook) + expctFunc func(*Hook) error + }{ + {func(h *Hook) { + h.WithPrefix("mprefix1") + }, func(h *Hook) error { + if h.hookOnlyPrefix != "mprefix1" { + return fmt.Errorf("expected hookOnlyPrefix to be '%s' but got '%s'", "mprefix1", h.hookOnlyPrefix) + } + return nil + }}, + {func(h *Hook) { + h.WithField("name", "muha") + }, func(h *Hook) error { + nField := logrus.Fields{"name": "muha"} + if !reflect.DeepEqual(h.alwaysSentFields, nField) { + return fmt.Errorf("expected hookOnlyPrefix to be '%s' but got '%s'", nField, h.hookOnlyPrefix) + } + return nil + }}, + {func(h *Hook) { + h.WithFields(logrus.Fields{"filename": "app.log", "owner": "mick"}) + }, func(h *Hook) error { + nField := logrus.Fields{"name": "test-me!", "filename": "app.log", "owner": "mick"} + if !reflect.DeepEqual(h.alwaysSentFields, nField) { + return fmt.Errorf("expected hookOnlyPrefix to be '%s' but got '%s'", nField, h.hookOnlyPrefix) + } + return nil + }}, + } + + for _, te := range tt { + hook := NewFilterHook() + hook.alwaysSentFields = logrus.Fields{"name": "test-me!"} + te.setFunc(hook) + if err := te.expctFunc(hook); err != nil { + t.Error(err) + } + } +} + +func TestFilterHookOnly(t *testing.T) { + tt := []struct { + entry *logrus.Entry + prefix string + expected logrus.Fields + }{ + {&logrus.Entry{Data: logrus.Fields{"name": "slimshady"}}, "", logrus.Fields{"name": "slimshady"}}, + {&logrus.Entry{Data: logrus.Fields{"_name": "slimshady", "nick": "blabla"}}, "_", logrus.Fields{"nick": "blabla"}}, + } + + for _, te := range tt { + hook := NewFilterHookWithPrefix(te.prefix) + hook.filterHookOnly(te.entry) + if !reflect.DeepEqual(te.entry.Data, te.expected) { + t.Errorf("expected entry data to be '%v' but got '%v'", te.expected, te.entry.Data) + } + } +} + +type AddrMock struct { +} + +func (a AddrMock) Network() string { + return "" +} + +func (a AddrMock) String() string { + return "" +} + +type ConnMock struct { + buff *bytes.Buffer +} + +func (c ConnMock) Read(b []byte) (int, error) { + return c.buff.Read(b) +} + +func (c ConnMock) Write(b []byte) (int, error) { + return c.buff.Write(b) +} + +func (c ConnMock) Close() error { + return nil +} + +func (c ConnMock) LocalAddr() net.Addr { + return AddrMock{} +} + +func (c ConnMock) RemoteAddr() net.Addr { + return AddrMock{} +} + +func (c ConnMock) SetDeadline(t time.Time) error { + return nil +} + +func (c ConnMock) SetReadDeadline(t time.Time) error { + return nil +} + +func (c ConnMock) SetWriteDeadline(t time.Time) error { + return nil +} + +func TestFire(t *testing.T) { + conn := ConnMock{buff: bytes.NewBufferString("")} + hook := &Hook{ + conn: conn, + appName: "fire_test", + alwaysSentFields: logrus.Fields{"test-name": "fire-test", "->ignore": "haaa", "override": "no"}, + hookOnlyPrefix: "->", + } + entry := &logrus.Entry{ + Message: "hello world!", + Data: logrus.Fields{"override": "yes"}, + Level: logrus.DebugLevel, + } + if err := hook.Fire(entry); err != nil { + t.Error(err) + } + var res map[string]string + if err := json.NewDecoder(conn.buff).Decode(&res); err != nil { + t.Error(err) + } + expected := map[string]string{ + "@timestamp": "0001-01-01T00:00:00Z", + "@version": "1", + "ignore": "haaa", + "level": "debug", + "message": "hello world!", + "override": "yes", + "test-name": "fire-test", + "type": "fire_test", + } + if !reflect.DeepEqual(expected, res) { + t.Errorf("expected message to be '%v' but got '%v'", expected, res) + } +} + +func TestFireFilterHook(t *testing.T) { + hook := &Hook{ + appName: "fire_hook_test", + alwaysSentFields: logrus.Fields{"test-name": "fire-test-hook", "_ignore": "haaa", "override": "no"}, + hookOnlyPrefix: "_", + } + entry := &logrus.Entry{ + Message: "hello world!", + Data: logrus.Fields{"override": "yes"}, + Level: logrus.DebugLevel, + } + if err := hook.Fire(entry); err != nil { + t.Error(err) + } + expected := &logrus.Entry{ + Message: "hello world!", + Data: logrus.Fields{"test-name": "fire-test-hook", "override": "yes"}, + Level: logrus.DebugLevel, + } + + if !reflect.DeepEqual(expected, entry) { + t.Errorf("expected message to be '%v' but got '%v'", expected, entry) + } +} + +func TestLevels(t *testing.T) { + hook := &Hook{} + expected := []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + logrus.WarnLevel, + logrus.InfoLevel, + logrus.DebugLevel, + } + res := hook.Levels() + if !reflect.DeepEqual(expected, res) { + t.Errorf("expected levels to be '%v' but got '%v'", expected, res) + } + +} From 30541774fa679b47f9a03909b5ad7ec618b381c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Min=C3=A1=C5=99?= Date: Mon, 24 Jul 2017 12:30:57 +0200 Subject: [PATCH 2/2] registry: reenable logstash formatter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #15296 Signed-off-by: Michal Minář --- pkg/cmd/dockerregistry/dockerregistry.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/dockerregistry/dockerregistry.go b/pkg/cmd/dockerregistry/dockerregistry.go index 794bc82dabaf..a9970e63d387 100644 --- a/pkg/cmd/dockerregistry/dockerregistry.go +++ b/pkg/cmd/dockerregistry/dockerregistry.go @@ -11,6 +11,7 @@ import ( "time" log "github.com/Sirupsen/logrus" + logrus_logstash "github.com/bshuster-repo/logrus-logstash-hook" gorillahandlers "github.com/gorilla/handlers" "github.com/docker/distribution/configuration" @@ -238,14 +239,9 @@ func configureLogging(ctx context.Context, config *configuration.Configuration) TimestampFormat: time.RFC3339Nano, }) case "logstash": - // just let the library use default on empty string. - if config.Log.Formatter != "" { - return ctx, fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter) - } - // "github.com/Sirupsen/logrus/formatters/logstash" - // log.SetFormatter(&logstash.LogstashFormatter{ - // TimestampFormat: time.RFC3339Nano, - // }) + log.SetFormatter(&logrus_logstash.LogstashFormatter{ + TimestampFormat: time.RFC3339Nano, + }) default: // just let the library use default on empty string. if config.Log.Formatter != "" {