diff --git a/dlog/doc.go b/dlog/doc.go new file mode 100644 index 0000000..28e95fb --- /dev/null +++ b/dlog/doc.go @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019. Genome Research Ltd. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @file doc.go + * @author Keith James + */ + +/* +Package dlog is a log (https://golang.org/pkg/log/) backend for +logshim (http://github.com/kjsanger/logshim). +*/ +package dlog diff --git a/dlog/log.go b/dlog/log.go new file mode 100644 index 0000000..f45ff2e --- /dev/null +++ b/dlog/log.go @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2019. Genome Research Ltd. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @file log.go + * @author Keith James + */ + +package dlog + +import ( + "fmt" + "io" + "log" + "strings" + "time" + + "github.com/kjsanger/logshim" +) + +type levelName string + +const ( + errorLevel levelName = "ERROR" + warnLevel levelName = "WARN" + // noticeLevel levelName = "NOTICE" + infoLevel levelName = "INFO" + debugLevel levelName = "DEBUG" +) + +func translateLevel(level logshim.Level) (levelName, error) { + var ( + lvn levelName + err error + ) + + switch level { + case logshim.ErrorLevel: + lvn = errorLevel + case logshim.WarnLevel: + lvn = warnLevel + case logshim.NoticeLevel: + fallthrough + case logshim.InfoLevel: + lvn = infoLevel + case logshim.DebugLevel: + lvn = debugLevel + default: + lvn = warnLevel + err = fmt.Errorf("invalid log level %d, defaulting to "+ + "WARN level", level) + } + + return lvn, err +} + +type StdLogger struct { + name string + Level logshim.Level + *log.Logger +} + +func New(writer io.Writer, level logshim.Level) *StdLogger { + lg := log.New(writer, "", log.LstdFlags|log.Lshortfile) + + _, err := translateLevel(level) + if err != nil { + log.Print(errorLevel, "log configuration error", err) + level = logshim.WarnLevel + } + + return &StdLogger{"StdLog", level, lg} +} + +func (log *StdLogger) Name() string { + return log.name +} + +func (log *StdLogger) Err(err error) logshim.Message { + effectiveLevel := logshim.InfoLevel + if err != nil { + effectiveLevel = logshim.ErrorLevel + } + + active := log.Level >= effectiveLevel + msg := &stdMessage{active, effectiveLevel, &strings.Builder{}} + msg.Err(err) + return msg +} + +func (log *StdLogger) Error() logshim.Message { + active := log.Level >= logshim.ErrorLevel + msg := &stdMessage{active, logshim.ErrorLevel, &strings.Builder{}} + return msg +} + +func (log *StdLogger) Warn() logshim.Message { + active := log.Level >= logshim.WarnLevel + msg := &stdMessage{active, logshim.WarnLevel, &strings.Builder{}} + return msg +} + +func (log *StdLogger) Notice() logshim.Message { + active := log.Level >= logshim.NoticeLevel + msg := &stdMessage{active, logshim.InfoLevel, &strings.Builder{}} + return msg +} + +func (log *StdLogger) Info() logshim.Message { + active := log.Level >= logshim.InfoLevel + msg := &stdMessage{active, logshim.InfoLevel, &strings.Builder{}} + return msg +} + +func (log *StdLogger) Debug() logshim.Message { + active := log.Level >= logshim.DebugLevel + msg := &stdMessage{active, logshim.DebugLevel, &strings.Builder{}} + return msg +} + +type stdMessage struct { + active bool + level logshim.Level + builder *strings.Builder +} + +func (msg *stdMessage) Err(err error) logshim.Message { + if msg.active { + msg.builder.WriteString(fmt.Sprintf(" error: %v", err)) + } + return msg +} + +func (msg *stdMessage) Bool(key string, val bool) logshim.Message { + if msg.active { + msg.builder.WriteString(fmt.Sprintf(" %s: %v", key, val)) + } + return msg +} + +func (msg *stdMessage) Int(key string, val int) logshim.Message { + if msg.active { + msg.builder.WriteString(fmt.Sprintf(" %s: %d", key, val)) + } + return msg +} + +func (msg *stdMessage) Int64(key string, val int64) logshim.Message { + if msg.active { + msg.builder.WriteString(fmt.Sprintf(" %s: %d", key, val)) + } + return msg +} + +func (msg *stdMessage) Uint64(key string, val uint64) logshim.Message { + if msg.active { + msg.builder.WriteString(fmt.Sprintf(" %s: %d", key, val)) + } + return msg +} + +func (msg *stdMessage) Str(key string, val string) logshim.Message { + if msg.active { + msg.builder.WriteString(fmt.Sprintf(" %s: %s", key, val)) + } + return msg +} + +func (msg *stdMessage) Time(key string, val time.Time) logshim.Message { + if msg.active { + msg.builder.WriteString(fmt.Sprintf(" %s: %v", key, val)) + } + return msg +} + +func (msg *stdMessage) Msg(val string) { + if msg.active { + lvn, err := translateLevel(msg.level) + if err != nil { + // This should never happen because the Logger constructor corrects + // invalid level values. + log.Print(errorLevel, "log configuration error", err) + } + + msg.builder.WriteString(" ") + msg.builder.WriteString(val) + log.Print(lvn, msg.builder.String()) + // Once this method is called, deactivate for all future calls + msg.active = false + } +} + +func (msg *stdMessage) Msgf(format string, a ...interface{}) { + msg.Msg(fmt.Sprintf(format, a...)) +} diff --git a/dlog/log_test.go b/dlog/log_test.go new file mode 100644 index 0000000..1ee3442 --- /dev/null +++ b/dlog/log_test.go @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019. Genome Research Ltd. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @file log_test.go + * @author Keith James + */ + +package dlog + +import ( + "os" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + + "github.com/kjsanger/logshim" +) + +func TestNew(t *testing.T) { + for _, level := range logshim.Levels() { + logger := New(os.Stderr, level) + assert.NotNil(t, logger, "StdLogger level %d was nil", level) + } +} + +func TestStdLogger_Err(t *testing.T) { + log := New(os.Stderr, logshim.ErrorLevel) + assert.NotNil(t, log.Err(errors.New("test error")), + "Err() should return a Message") +} + +func TestStdLogger_Error(t *testing.T) { + log := New(os.Stderr, logshim.ErrorLevel) + assert.NotNil(t, log.Error(), "Error() should return a Message") +} + +func TestStdLogger_Warn(t *testing.T) { + log := New(os.Stderr, logshim.ErrorLevel) + assert.NotNil(t, log.Warn(), "Warn() should return a Message") +} + +func TestStdLogger_Notice(t *testing.T) { + log := New(os.Stderr, logshim.ErrorLevel) + assert.NotNil(t, log.Notice(), "Notice() should return a Message") +} + +func TestStdLogger_Info(t *testing.T) { + log := New(os.Stderr, logshim.ErrorLevel) + assert.NotNil(t, log.Info(), "Info() should return a Message") +} + +func TestStdLogger_Debug(t *testing.T) { + log := New(os.Stderr, logshim.ErrorLevel) + assert.NotNil(t, log.Debug(), "Debug() should return a Message") +} diff --git a/go.mod b/go.mod index 64b860c..607e9eb 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ module github.com/kjsanger/logshim go 1.12 + +require ( + github.com/pkg/errors v0.8.1 + github.com/stretchr/testify v1.3.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9229611 --- /dev/null +++ b/go.sum @@ -0,0 +1,9 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=