// log/fmt.go
package log
import (
"sync"
"bytes"
"io"
"github.com/go-logfmt/logfmt"
"github.com/go-kit/log/level"
logkit "github.com/go-kit/log"
)
type logfmtEncoder struct {
*logfmt.Encoder
buf bytes.Buffer
}
func (l *logfmtEncoder) Reset() {
l.Encoder.Reset()
l.buf.Reset()
}
var logfmtEncoderPool = sync.Pool{
New: func() interface{} {
var enc logfmtEncoder
enc.Encoder = logfmt.NewEncoder(&enc.buf)
return &enc
},
}
type logfmtLogger struct {
w io.Writer
}
func NewLogfmtLogger(w io.Writer) logkit.Logger {
return &logfmtLogger{w}
}
func (l logfmtLogger) Log(keyvals ...interface{}) error {
enc := logfmtEncoderPool.Get().(*logfmtEncoder)
enc.Reset()
defer logfmtEncoderPool.Put(enc)
if err := enc.EncodeKeyvals(keyvals...); err != nil {
return err
}
if err := enc.EndRecord(); err != nil {
return err
}
n, err := l.w.Write(enc.buf.Bytes())
lev := "unknown"
for i := 1; i < len(keyvals); i += 2 {
if v, ok := keyvals[i].(level.Value); ok {
lev = v.String()
break
}
}
logMessages.WithLabelValues(lev).Inc()
logMessagesSize.WithLabelValues(lev).Add(float64(n))
return err
}
// log/log.go
package log
import (
"os"
logkit "github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
)
var (
logMessages = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "log_messages_total",
Help: "Total number of log messages.",
}, []string{"level"})
logMessagesSize = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "log_messages_size_total",
Help: "Total size of log messages.",
}, []string{"level"})
supportedLevels = []level.Value{
level.DebugValue(),
level.InfoValue(),
level.WarnValue(),
level.ErrorValue(),
}
)
func init() {
prometheus.MustRegister(logMessages)
prometheus.MustRegister(logMessagesSize)
}
type InstrumentedLogger struct {
logger logkit.Logger
}
func NewInstrumentedLogger(options level.Value, keyvals ...interface{}) (logkit.Logger, error) {
logger := NewLogfmtLogger(logkit.NewSyncWriter(os.Stderr))
logger = logkit.With(logger, "ts", logkit.DefaultTimestampUTC)
logger = logkit.With(logger, keyvals...)
logger = level.NewFilter(logger, level.Allow(options))
for _, level := range supportedLevels {
logMessages.WithLabelValues(level.String())
logMessagesSize.WithLabelValues(level.String())
}
return &InstrumentedLogger{
logger: logger,
}, nil
}
func (pl *InstrumentedLogger) Log(kv ...interface{}) error {
pl.logger.Log(kv...)
return nil
}
// main.go
package main
import (
"net/http"
"github.com/mircodz/log/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
logger, err := log.NewInstrumentedLogger(level.DebugValue())
if err != nil {
panic(err)
}
level.Debug(logger).Log("message", "Hello, World!")
level.Info(logger).Log("message", "Hello, World!")
level.Warn(logger).Log("message", "Hello, World!")
level.Error(logger).Log("message", "Hello, World!")
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":2112", nil)
}