Initial commit

This commit is contained in:
Ria Bhatia
2017-12-04 13:32:57 -06:00
committed by Erik St. Martin
commit 0075e5b0f3
9056 changed files with 2523100 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
package loggerutils
import (
"fmt"
"strconv"
"github.com/hyperhq/hypercli/daemon/logger"
)
const (
defaultFailOnStartupError = true // So that we do not break existing behaviour
)
// ParseFailOnStartupErrorFlag parses a log driver flag that determines if
// the driver should ignore possible connection errors during startup
func ParseFailOnStartupErrorFlag(ctx logger.Context) (bool, error) {
failOnStartupError := ctx.Config["fail-on-startup-error"]
if failOnStartupError == "" {
return defaultFailOnStartupError, nil
}
failOnStartupErrorFlag, err := strconv.ParseBool(failOnStartupError)
if err != nil {
return defaultFailOnStartupError, fmt.Errorf("invalid connect error flag %s: %s", failOnStartupError, err)
}
return failOnStartupErrorFlag, nil
}

View File

@@ -0,0 +1,51 @@
package loggerutils
import (
"testing"
"github.com/hyperhq/hypercli/daemon/logger"
)
func TestParseDefaultIgnoreFlag(t *testing.T) {
ctx := buildContext(map[string]string{})
flag, e := ParseFailOnStartupErrorFlag(ctx)
assertFlag(t, e, flag, true)
}
func TestParseIgnoreFlagWhenFalse(t *testing.T) {
ctx := buildContext(map[string]string{"fail-on-startup-error": "false"})
flag, e := ParseFailOnStartupErrorFlag(ctx)
assertFlag(t, e, flag, false)
}
func TestParseIgnoreFlagWhenTrue(t *testing.T) {
ctx := buildContext(map[string]string{"fail-on-startup-error": "true"})
flag, e := ParseFailOnStartupErrorFlag(ctx)
assertFlag(t, e, flag, true)
}
func TestParseIgnoreFlagWithError(t *testing.T) {
ctx := buildContext(map[string]string{"fail-on-startup-error": "maybe :)"})
flag, e := ParseFailOnStartupErrorFlag(ctx)
if e == nil {
t.Fatalf("Error should have happened")
}
assertFlag(t, nil, flag, true)
}
// Helpers
func buildConfig(cfg map[string]string) logger.Context {
return logger.Context{
Config: cfg,
}
}
func assertFlag(t *testing.T, e error, flag bool, expected bool) {
if e != nil {
t.Fatalf("Error parsing ignore connect error flag: %q", e)
}
if flag != expected {
t.Fatalf("Wrong flag: %t, should be %t", flag, expected)
}
}

View File

@@ -0,0 +1,46 @@
package loggerutils
import (
"bytes"
"fmt"
"text/template"
"github.com/Sirupsen/logrus"
"github.com/hyperhq/hypercli/daemon/logger"
)
// ParseLogTag generates a context aware tag for consistency across different
// log drivers based on the context of the running container.
func ParseLogTag(ctx logger.Context, defaultTemplate string) (string, error) {
tagTemplate := lookupTagTemplate(ctx, defaultTemplate)
tmpl, err := template.New("log-tag").Parse(tagTemplate)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, &ctx); err != nil {
return "", err
}
return buf.String(), nil
}
func lookupTagTemplate(ctx logger.Context, defaultTemplate string) string {
tagTemplate := ctx.Config["tag"]
deprecatedConfigs := []string{"syslog-tag", "gelf-tag", "fluentd-tag"}
for i := 0; tagTemplate == "" && i < len(deprecatedConfigs); i++ {
cfg := deprecatedConfigs[i]
if ctx.Config[cfg] != "" {
tagTemplate = ctx.Config[cfg]
logrus.Warn(fmt.Sprintf("Using log tag from deprecated log-opt '%s'. Please use: --log-opt tag=\"%s\"", cfg, tagTemplate))
}
}
if tagTemplate == "" {
tagTemplate = defaultTemplate
}
return tagTemplate
}

View File

@@ -0,0 +1,58 @@
package loggerutils
import (
"testing"
"github.com/hyperhq/hypercli/daemon/logger"
)
func TestParseLogTagDefaultTag(t *testing.T) {
ctx := buildContext(map[string]string{})
tag, e := ParseLogTag(ctx, "{{.ID}}")
assertTag(t, e, tag, ctx.ID())
}
func TestParseLogTag(t *testing.T) {
ctx := buildContext(map[string]string{"tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"})
tag, e := ParseLogTag(ctx, "{{.ID}}")
assertTag(t, e, tag, "test-image/test-container/container-ab")
}
func TestParseLogTagSyslogTag(t *testing.T) {
ctx := buildContext(map[string]string{"syslog-tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"})
tag, e := ParseLogTag(ctx, "{{.ID}}")
assertTag(t, e, tag, "test-image/test-container/container-ab")
}
func TestParseLogTagGelfTag(t *testing.T) {
ctx := buildContext(map[string]string{"gelf-tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"})
tag, e := ParseLogTag(ctx, "{{.ID}}")
assertTag(t, e, tag, "test-image/test-container/container-ab")
}
func TestParseLogTagFluentdTag(t *testing.T) {
ctx := buildContext(map[string]string{"fluentd-tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"})
tag, e := ParseLogTag(ctx, "{{.ID}}")
assertTag(t, e, tag, "test-image/test-container/container-ab")
}
// Helpers
func buildContext(cfg map[string]string) logger.Context {
return logger.Context{
ContainerID: "container-abcdefghijklmnopqrstuvwxyz01234567890",
ContainerName: "/test-container",
ContainerImageID: "image-abcdefghijklmnopqrstuvwxyz01234567890",
ContainerImageName: "test-image",
Config: cfg,
}
}
func assertTag(t *testing.T, e error, tag string, expected string) {
if e != nil {
t.Fatalf("Error generating tag: %q", e)
}
if tag != expected {
t.Fatalf("Wrong tag: %q, should be %q", tag, expected)
}
}

View File

@@ -0,0 +1,132 @@
package loggerutils
import (
"os"
"strconv"
"sync"
"github.com/hyperhq/hypercli/pkg/pubsub"
)
// RotateFileWriter is Logger implementation for default Docker logging.
type RotateFileWriter struct {
f *os.File // store for closing
mu sync.Mutex
capacity int64 //maximum size of each file
maxFiles int //maximum number of files
notifyRotate *pubsub.Publisher
}
//NewRotateFileWriter creates new RotateFileWriter
func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) {
log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
if err != nil {
return &RotateFileWriter{}, err
}
return &RotateFileWriter{
f: log,
capacity: capacity,
maxFiles: maxFiles,
notifyRotate: pubsub.NewPublisher(0, 1),
}, nil
}
//WriteLog write log message to File
func (w *RotateFileWriter) Write(message []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
if err := w.checkCapacityAndRotate(); err != nil {
return -1, err
}
return w.f.Write(message)
}
func (w *RotateFileWriter) checkCapacityAndRotate() error {
if w.capacity == -1 {
return nil
}
meta, err := w.f.Stat()
if err != nil {
return err
}
if meta.Size() >= w.capacity {
name := w.f.Name()
if err := w.f.Close(); err != nil {
return err
}
if err := rotate(name, w.maxFiles); err != nil {
return err
}
file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 06400)
if err != nil {
return err
}
w.f = file
w.notifyRotate.Publish(struct{}{})
}
return nil
}
func rotate(name string, maxFiles int) error {
if maxFiles < 2 {
return nil
}
for i := maxFiles - 1; i > 1; i-- {
toPath := name + "." + strconv.Itoa(i)
fromPath := name + "." + strconv.Itoa(i-1)
if err := backup(fromPath, toPath); err != nil && !os.IsNotExist(err) {
return err
}
}
if err := backup(name, name+".1"); err != nil {
return err
}
return nil
}
// backup renames a file from fromPath to toPath
func backup(fromPath, toPath string) error {
if _, err := os.Stat(fromPath); os.IsNotExist(err) {
return err
}
if _, err := os.Stat(toPath); !os.IsNotExist(err) {
err := os.Remove(toPath)
if err != nil {
return err
}
}
return os.Rename(fromPath, toPath)
}
// LogPath returns the location the given writer logs to.
func (w *RotateFileWriter) LogPath() string {
return w.f.Name()
}
// MaxFiles return maximum number of files
func (w *RotateFileWriter) MaxFiles() int {
return w.maxFiles
}
//NotifyRotate returns the new subscriber
func (w *RotateFileWriter) NotifyRotate() chan interface{} {
return w.notifyRotate.Subscribe()
}
//NotifyRotateEvict removes the specified subscriber from receiving any more messages.
func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) {
w.notifyRotate.Evict(sub)
}
// Close closes underlying file and signals all readers to stop.
func (w *RotateFileWriter) Close() error {
return w.f.Close()
}