Browse Source

Merge pull request #27471 from caarlos0/logentries

Added Logentries Driver
Sebastiaan van Stijn 8 năm trước cách đây
mục cha
commit
24582e8153

+ 6 - 1
contrib/completion/bash/docker

@@ -505,6 +505,7 @@ __docker_complete_log_drivers() {
 		gelf
 		journald
 		json-file
+		logentries
 		none
 		splunk
 		syslog
@@ -519,10 +520,11 @@ __docker_complete_log_options() {
 	local gelf_options="env gelf-address gelf-compression-level gelf-compression-type labels tag"
 	local journald_options="env labels tag"
 	local json_file_options="env labels max-file max-size"
+	local logentries_options="logentries-token"
 	local syslog_options="env labels syslog-address syslog-facility syslog-format syslog-tls-ca-cert syslog-tls-cert syslog-tls-key syslog-tls-skip-verify tag"
 	local splunk_options="env labels splunk-caname splunk-capath splunk-format splunk-gzip splunk-gzip-level splunk-index splunk-insecureskipverify splunk-source splunk-sourcetype splunk-token splunk-url splunk-verify-connection tag"
 
-	local all_options="$fluentd_options $gcplogs_options $gelf_options $journald_options $json_file_options $syslog_options $splunk_options"
+	local all_options="$fluentd_options $gcplogs_options $gelf_options $journald_options $logentries_options $json_file_options $syslog_options $splunk_options"
 
 	case $(__docker_value_of_option --log-driver) in
 		'')
@@ -546,6 +548,9 @@ __docker_complete_log_options() {
 		json-file)
 			COMPREPLY=( $( compgen -W "$json_file_options" -S = -- "$cur" ) )
 			;;
+		logentries)
+			COMPREPLY=( $( compgen -W "$logentries_options" -S = -- "$cur" ) )
+			;;
 		syslog)
 			COMPREPLY=( $( compgen -W "$syslog_options" -S = -- "$cur" ) )
 			;;

+ 3 - 1
contrib/completion/zsh/_docker

@@ -219,7 +219,7 @@ __docker_get_log_options() {
 
     integer ret=1
     local log_driver=${opt_args[--log-driver]:-"all"}
-    local -a awslogs_options fluentd_options gelf_options journald_options json_file_options syslog_options splunk_options
+    local -a awslogs_options fluentd_options gelf_options journald_options json_file_options logentries_options syslog_options splunk_options
 
     awslogs_options=("awslogs-region" "awslogs-group" "awslogs-stream")
     fluentd_options=("env" "fluentd-address" "fluentd-async-connect" "fluentd-buffer-limit" "fluentd-retry-wait" "fluentd-max-retries" "labels" "tag")
@@ -227,6 +227,7 @@ __docker_get_log_options() {
     gelf_options=("env" "gelf-address" "gelf-compression-level" "gelf-compression-type" "labels" "tag")
     journald_options=("env" "labels" "tag")
     json_file_options=("env" "labels" "max-file" "max-size")
+    logentries_options=("logentries-token")
     syslog_options=("env" "labels" "syslog-address" "syslog-facility" "syslog-format" "syslog-tls-ca-cert" "syslog-tls-cert" "syslog-tls-key" "syslog-tls-skip-verify" "tag")
     splunk_options=("env" "labels" "splunk-caname" "splunk-capath" "splunk-format" "splunk-gzip" "splunk-gzip-level" "splunk-index" "splunk-insecureskipverify" "splunk-source" "splunk-sourcetype" "splunk-token" "splunk-url" "splunk-verify-connection" "tag")
 
@@ -236,6 +237,7 @@ __docker_get_log_options() {
     [[ $log_driver = (gelf|all) ]] && _describe -t gelf-options "gelf options" gelf_options "$@" && ret=0
     [[ $log_driver = (journald|all) ]] && _describe -t journald-options "journald options" journald_options "$@" && ret=0
     [[ $log_driver = (json-file|all) ]] && _describe -t json-file-options "json-file options" json_file_options "$@" && ret=0
+    [[ $log_driver = (logentries|all) ]] && _describe -t logentries-options "logentries options" logentries_options "$@" && ret=0
     [[ $log_driver = (syslog|all) ]] && _describe -t syslog-options "syslog options" syslog_options "$@" && ret=0
     [[ $log_driver = (splunk|all) ]] && _describe -t splunk-options "splunk options" splunk_options "$@" && ret=0
 

+ 1 - 0
daemon/logdrivers_linux.go

@@ -9,6 +9,7 @@ import (
 	_ "github.com/docker/docker/daemon/logger/gelf"
 	_ "github.com/docker/docker/daemon/logger/journald"
 	_ "github.com/docker/docker/daemon/logger/jsonfilelog"
+	_ "github.com/docker/docker/daemon/logger/logentries"
 	_ "github.com/docker/docker/daemon/logger/splunk"
 	_ "github.com/docker/docker/daemon/logger/syslog"
 )

+ 1 - 0
daemon/logdrivers_windows.go

@@ -6,6 +6,7 @@ import (
 	_ "github.com/docker/docker/daemon/logger/awslogs"
 	_ "github.com/docker/docker/daemon/logger/etwlogs"
 	_ "github.com/docker/docker/daemon/logger/jsonfilelog"
+	_ "github.com/docker/docker/daemon/logger/logentries"
 	_ "github.com/docker/docker/daemon/logger/splunk"
 	_ "github.com/docker/docker/daemon/logger/syslog"
 )

+ 94 - 0
daemon/logger/logentries/logentries.go

@@ -0,0 +1,94 @@
+// Package logentries provides the log driver for forwarding server logs
+// to logentries endpoints.
+package logentries
+
+import (
+	"fmt"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/bsphere/le_go"
+	"github.com/docker/docker/daemon/logger"
+)
+
+type logentries struct {
+	tag           string
+	containerID   string
+	containerName string
+	writer        *le_go.Logger
+	extra         map[string]string
+}
+
+const (
+	name  = "logentries"
+	token = "logentries-token"
+)
+
+func init() {
+	if err := logger.RegisterLogDriver(name, New); err != nil {
+		logrus.Fatal(err)
+	}
+	if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
+		logrus.Fatal(err)
+	}
+}
+
+// New creates a logentries logger using the configuration passed in on
+// the context. The supported context configuration variable is
+// logentries-token.
+func New(ctx logger.Context) (logger.Logger, error) {
+	logrus.WithField("container", ctx.ContainerID).
+		WithField("token", ctx.Config[token]).
+		Debug("logging driver logentries configured")
+
+	log, err := le_go.Connect(ctx.Config[token])
+	if err != nil {
+		return nil, err
+	}
+	return &logentries{
+		containerID:   ctx.ContainerID,
+		containerName: ctx.ContainerName,
+		writer:        log,
+	}, nil
+}
+
+func (f *logentries) Log(msg *logger.Message) error {
+	data := map[string]string{
+		"container_id":   f.containerID,
+		"container_name": f.containerName,
+		"source":         msg.Source,
+		"log":            string(msg.Line),
+	}
+	for k, v := range f.extra {
+		data[k] = v
+	}
+	f.writer.Println(f.tag, msg.Timestamp, data)
+	return nil
+}
+
+func (f *logentries) Close() error {
+	return f.writer.Close()
+}
+
+func (f *logentries) Name() string {
+	return name
+}
+
+// ValidateLogOpt looks for logentries specific log option logentries-address.
+func ValidateLogOpt(cfg map[string]string) error {
+	for key := range cfg {
+		switch key {
+		case "env":
+		case "labels":
+		case "tag":
+		case key:
+		default:
+			return fmt.Errorf("unknown log opt '%s' for logentries log driver", key)
+		}
+	}
+
+	if cfg[token] == "" {
+		return fmt.Errorf("Missing logentries token")
+	}
+
+	return nil
+}

+ 3 - 0
hack/vendor.sh

@@ -131,6 +131,9 @@ clone git github.com/aws/aws-sdk-go v1.1.30
 clone git github.com/go-ini/ini 060d7da055ba6ec5ea7a31f116332fe5efa04ce0
 clone git github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
 
+# logentries
+clone git github.com/bsphere/le_go d3308aafe090956bc89a65f0769f58251a1b4f03 
+
 # gcplogs deps
 clone git golang.org/x/oauth2 2baa8a1b9338cf13d9eeb27696d761155fa480be https://github.com/golang/oauth2.git
 clone git google.golang.org/api dc6d2353af16e2a2b0ff6986af051d473a4ed468 https://code.googlesource.com/google-api-go-client

+ 23 - 0
vendor/src/github.com/bsphere/le_go/.gitignore

@@ -0,0 +1,23 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test

+ 4 - 0
vendor/src/github.com/bsphere/le_go/.travis.yml

@@ -0,0 +1,4 @@
+language: go
+
+go:
+  - 1.4

+ 37 - 0
vendor/src/github.com/bsphere/le_go/README.md

@@ -0,0 +1,37 @@
+le_go
+=====
+
+Golang client library for logentries.com
+
+It is compatible with http://golang.org/pkg/log/#Logger
+and also implements http://golang.org/pkg/io/#Writer
+
+[![GoDoc](https://godoc.org/github.com/bsphere/le_go?status.png)](https://godoc.org/github.com/bsphere/le_go)
+
+[![Build Status](https://travis-ci.org/bsphere/le_go.svg)](https://travis-ci.org/bsphere/le_go)
+
+Usage
+-----
+Add a new manual TCP token log at [logentries.com](https://logentries.com/quick-start/) and copy the [token](https://logentries.com/doc/input-token/).
+
+Installation: `go get github.com/bsphere/le_go`
+
+**Note:** The Logger is blocking, it can be easily run in a goroutine by calling `go le.Println(...)`
+
+```go
+package main
+
+import "github.com/bsphere/le_go"
+
+func main() {
+	le, err := le_go.Connect("XXXX-XXXX-XXXX-XXXX") // replace with token
+	if err != nil {
+		panic(err)
+	}
+
+	defer le.Close()
+
+	le.Println("another test message")
+}
+```
+

+ 216 - 0
vendor/src/github.com/bsphere/le_go/le.go

@@ -0,0 +1,216 @@
+// Package le_go provides a Golang client library for logging to
+// logentries.com over a TCP connection.
+//
+// it uses an access token for sending log events.
+package le_go
+
+import (
+	"crypto/tls"
+	"fmt"
+	"net"
+	"os"
+	"strings"
+	"sync"
+	"time"
+)
+
+// Logger represents a Logentries logger,
+// it holds the open TCP connection, access token, prefix and flags.
+//
+// all Logger operations are thread safe and blocking,
+// log operations can be invoked in a non-blocking way by calling them from
+// a goroutine.
+type Logger struct {
+	conn   net.Conn
+	flag   int
+	mu     sync.Mutex
+	prefix string
+	token  string
+	buf    []byte
+}
+
+const lineSep = "\n"
+
+// Connect creates a new Logger instance and opens a TCP connection to
+// logentries.com,
+// The token can be generated at logentries.com by adding a new log,
+// choosing manual configuration and token based TCP connection.
+func Connect(token string) (*Logger, error) {
+	logger := Logger{
+		token: token,
+	}
+
+	if err := logger.openConnection(); err != nil {
+		return nil, err
+	}
+
+	return &logger, nil
+}
+
+// Close closes the TCP connection to logentries.com
+func (logger *Logger) Close() error {
+	if logger.conn != nil {
+		return logger.conn.Close()
+	}
+
+	return nil
+}
+
+// Opens a TCP connection to logentries.com
+func (logger *Logger) openConnection() error {
+	conn, err := tls.Dial("tcp", "data.logentries.com:443", &tls.Config{})
+	if err != nil {
+		return err
+	}
+	logger.conn = conn
+	return nil
+}
+
+// It returns if the TCP connection to logentries.com is open
+func (logger *Logger) isOpenConnection() bool {
+	if logger.conn == nil {
+		return false
+	}
+
+	buf := make([]byte, 1)
+
+	logger.conn.SetReadDeadline(time.Now())
+
+	_, err := logger.conn.Read(buf)
+
+	switch err.(type) {
+	case net.Error:
+		if err.(net.Error).Timeout() == true {
+			logger.conn.SetReadDeadline(time.Time{})
+
+			return true
+		}
+	}
+
+	return false
+}
+
+// It ensures that the TCP connection to logentries.com is open.
+// If the connection is closed, a new one is opened.
+func (logger *Logger) ensureOpenConnection() error {
+	if !logger.isOpenConnection() {
+		if err := logger.openConnection(); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Fatal is same as Print() but calls to os.Exit(1)
+func (logger *Logger) Fatal(v ...interface{}) {
+	logger.Output(2, fmt.Sprint(v...))
+	os.Exit(1)
+}
+
+// Fatalf is same as Printf() but calls to os.Exit(1)
+func (logger *Logger) Fatalf(format string, v ...interface{}) {
+	logger.Output(2, fmt.Sprintf(format, v...))
+	os.Exit(1)
+}
+
+// Fatalln is same as Println() but calls to os.Exit(1)
+func (logger *Logger) Fatalln(v ...interface{}) {
+	logger.Output(2, fmt.Sprintln(v...))
+	os.Exit(1)
+}
+
+// Flags returns the logger flags
+func (logger *Logger) Flags() int {
+	return logger.flag
+}
+
+// Output does the actual writing to the TCP connection
+func (logger *Logger) Output(calldepth int, s string) error {
+	_, err := logger.Write([]byte(s))
+
+	return err
+}
+
+// Panic is same as Print() but calls to panic
+func (logger *Logger) Panic(v ...interface{}) {
+	s := fmt.Sprint(v...)
+	logger.Output(2, s)
+	panic(s)
+}
+
+// Panicf is same as Printf() but calls to panic
+func (logger *Logger) Panicf(format string, v ...interface{}) {
+	s := fmt.Sprintf(format, v...)
+	logger.Output(2, s)
+	panic(s)
+}
+
+// Panicln is same as Println() but calls to panic
+func (logger *Logger) Panicln(v ...interface{}) {
+	s := fmt.Sprintln(v...)
+	logger.Output(2, s)
+	panic(s)
+}
+
+// Prefix returns the logger prefix
+func (logger *Logger) Prefix() string {
+	return logger.prefix
+}
+
+// Print logs a message
+func (logger *Logger) Print(v ...interface{}) {
+	logger.Output(2, fmt.Sprint(v...))
+}
+
+// Printf logs a formatted message
+func (logger *Logger) Printf(format string, v ...interface{}) {
+	logger.Output(2, fmt.Sprintf(format, v...))
+}
+
+// Println logs a message with a linebreak
+func (logger *Logger) Println(v ...interface{}) {
+	logger.Output(2, fmt.Sprintln(v...))
+}
+
+// SetFlags sets the logger flags
+func (logger *Logger) SetFlags(flag int) {
+	logger.flag = flag
+}
+
+// SetPrefix sets the logger prefix
+func (logger *Logger) SetPrefix(prefix string) {
+	logger.prefix = prefix
+}
+
+// Write writes a bytes array to the Logentries TCP connection,
+// it adds the access token and prefix and also replaces
+// line breaks with the unicode \u2028 character
+func (logger *Logger) Write(p []byte) (n int, err error) {
+	if err := logger.ensureOpenConnection(); err != nil {
+		return 0, err
+	}
+
+	logger.mu.Lock()
+	defer logger.mu.Unlock()
+
+	logger.makeBuf(p)
+
+	return logger.conn.Write(logger.buf)
+}
+
+// makeBuf constructs the logger buffer
+// it is not safe to be used from within multiple concurrent goroutines
+func (logger *Logger) makeBuf(p []byte) {
+	count := strings.Count(string(p), lineSep)
+	p = []byte(strings.Replace(string(p), lineSep, "\u2028", count-1))
+
+	logger.buf = logger.buf[:0]
+	logger.buf = append(logger.buf, (logger.token + " ")...)
+	logger.buf = append(logger.buf, (logger.prefix + " ")...)
+	logger.buf = append(logger.buf, p...)
+
+	if !strings.HasSuffix(string(logger.buf), lineSep) {
+		logger.buf = append(logger.buf, (lineSep)...)
+	}
+}