mirror of
https://github.com/khorevaa/logos.git
synced 2025-03-11 14:59:51 +02:00
first commit
This commit is contained in:
commit
c714e3833d
25
.github/workflows/benchmark.yml
vendored
Normal file
25
.github/workflows/benchmark.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: benchmark
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
- name: Benchmark
|
||||
run: |
|
||||
curl -sL https://github.com/${GITHUB_REPOSITORY}/raw/master/README.md | \
|
||||
awk '{if($0 ~ "// go test -v"){a=1;b=1};if($0 ~ "```" && b=1){b=0};if (a&&b) {print}}' | \
|
||||
tee log_test.go
|
||||
go get github.com/${GITHUB_REPOSITORY}
|
||||
go get github.com/phuslu/log
|
||||
go get github.com/rs/zerolog
|
||||
go get go.uber.org/zap
|
||||
head -1 log_test.go | cut -b3- | sed -E 's#\r##' | bash -xe
|
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
.idea
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 v8platform
|
||||
|
||||
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.
|
173
README.md
Normal file
173
README.md
Normal file
@ -0,0 +1,173 @@
|
||||
# Logos: like log4j, but for golang.
|
||||
|
||||
[![go.dev][pkg-img]][pkg] [![goreport][report-img]][report] [![build][build-img]][build] [![coverage][cov-img]][cov] ![stability-stable][stability-img]
|
||||
|
||||
## Features
|
||||
|
||||
This project is a wrapper around the excellent logging framework zap.
|
||||
|
||||
* Dependency
|
||||
- `go.uber.org/zap` for logging
|
||||
- `github.com/elastic/go-ucfg` for config logging system
|
||||
* Simple and Clean Interface
|
||||
* One log manager for all logs
|
||||
* Hot config update from file or env
|
||||
* Appenders
|
||||
- `Console`, *write to console*
|
||||
- `File`, *any log file*
|
||||
- `gelfupd`, *greylog logger*
|
||||
- `RollingFile`, *rolling file writing & compress*
|
||||
* Encoders
|
||||
- `Console`, *colorful & formatting text for console*
|
||||
- `Gelf`, *gelf for greylog*
|
||||
- `Json`, *standard json encoder*
|
||||
* Useful utility function
|
||||
- `Setlevel(LogName string, level int)`, *hot update logger level*
|
||||
- `UpdateLogger(LogName string, logger *zap.Logger*)`, *hot update core logger*
|
||||
- `RedirectStdLog()`, *redirect standard log package*
|
||||
* High Performance
|
||||
- [Significantly faster][high-performance] than all other json loggers.
|
||||
|
||||
## Interfaces
|
||||
|
||||
### Logger
|
||||
```go
|
||||
// DefaultLogger is the global logger.
|
||||
```
|
||||
|
||||
### Json Writer
|
||||
|
||||
To log a machine-friendly, use `json`. [![playground][play-pretty-img]][play-pretty]
|
||||
|
||||
```go
|
||||
|
||||
```
|
||||
|
||||
### Pretty Console Writer
|
||||
|
||||
To log a human-friendly, colorized output, use `Console`. [![playground][play-pretty-img]][play-pretty]
|
||||
|
||||
```go
|
||||
|
||||
```
|
||||
![Pretty logging][pretty-img]
|
||||
> Note: pretty logging also works on windows console
|
||||
|
||||
### High Performance
|
||||
|
||||
A quick and simple benchmark with zap/zerolog, which runs on [github actions][benchmark]:
|
||||
|
||||
```go
|
||||
// go test -v -cpu=4 -run=none -bench=. -benchtime=10s -benchmem log_test.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/khorevaa/logos"
|
||||
"github.com/phuslu/log"
|
||||
"github.com/rs/zerolog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var fakeMessage = "Test logging, but use a somewhat realistic message length. "
|
||||
|
||||
func BenchmarkLogos(b *testing.B) {
|
||||
|
||||
const newConfig = `
|
||||
appenders:
|
||||
console:
|
||||
- name: CONSOLE
|
||||
target: discard
|
||||
encoder:
|
||||
console:
|
||||
loggers:
|
||||
root:
|
||||
level: info
|
||||
appender_refs:
|
||||
- CONSOLE
|
||||
`
|
||||
err := logos.InitWithConfigContent(newConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logger := logos.New("benchmark")
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Info(fakeMessage, zap.String("foo", "bar"), zap.Int("int", 42))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkZap(b *testing.B) {
|
||||
logger := zap.New(zapcore.NewCore(
|
||||
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
|
||||
zapcore.AddSync(ioutil.Discard),
|
||||
zapcore.InfoLevel,
|
||||
))
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Info(fakeMessage, zap.String("foo", "bar"), zap.Int("int", 42))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkZeroLog(b *testing.B) {
|
||||
logger := zerolog.New(ioutil.Discard).With().Timestamp().Logger()
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Info().Str("foo", "bar").Int("int", 42).Msg(fakeMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPhusLog(b *testing.B) {
|
||||
logger := log.Logger{
|
||||
TimeFormat: "", // uses rfc3339 by default
|
||||
Writer: log.IOWriter{ioutil.Discard},
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Info().Str("foo", "bar").Int("int", 42).Msg(fakeMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
```
|
||||
A Performance result as below, for daily benchmark results see [github actions][benchmark]
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
|
||||
[pkg-img]: http://img.shields.io/badge/godoc-reference-5272B4.svg
|
||||
[pkg]: https://godoc.org/github.com/khorevaa/logos
|
||||
[report-img]: https://goreportcard.com/badge/github.com/khorevaa/logos
|
||||
[report]: https://goreportcard.com/report/github.com/khorevaa/logos
|
||||
[build-img]: https://github.com/khorevaa/logos/workflows/build/badge.svg
|
||||
[build]: https://github.com/khorevaa/logos/actions
|
||||
[cov-img]: http://gocover.io/_badge/github.com/khorevaa/logos
|
||||
[cov]: https://gocover.io/github.com/khorevaa/logos
|
||||
[stability-img]: https://img.shields.io/badge/stability-stable-green.svg
|
||||
[high-performance]: https://github.com/khorevaa/logos#high-performance
|
||||
[play-simple-img]: https://img.shields.io/badge/playground-NGV25aBKmYH-29BEB0?style=flat&logo=go
|
||||
[play-simple]: https://play.golang.org/p/NGV25aBKmYH
|
||||
[play-customize-img]: https://img.shields.io/badge/playground-emTsJJKUGXZ-29BEB0?style=flat&logo=go
|
||||
[play-customize]: https://play.golang.org/p/emTsJJKUGXZ
|
||||
[play-file-img]: https://img.shields.io/badge/playground-nS--ILxFyhHM-29BEB0?style=flat&logo=go
|
||||
[play-file]: https://play.golang.org/p/nS-ILxFyhHM
|
||||
[play-pretty-img]: https://img.shields.io/badge/playground-SCcXG33esvI-29BEB0?style=flat&logo=go
|
||||
[play-pretty]: https://play.golang.org/p/SCcXG33esvI
|
||||
[pretty-img]: https://user-images.githubusercontent.com/195836/101993218-cda82380-3cf3-11eb-9aa2-b8b1c832a72e.png
|
||||
[play-formatting-img]: https://img.shields.io/badge/playground-UmJmLxYXwRO-29BEB0?style=flat&logo=go
|
||||
[play-formatting]: https://play.golang.org/p/UmJmLxYXwRO
|
||||
[play-context-img]: https://img.shields.io/badge/playground-oAVAo302faf-29BEB0?style=flat&logo=go
|
||||
[play-context]: https://play.golang.org/p/oAVAo302faf
|
||||
[play-marshal-img]: https://img.shields.io/badge/playground-NxMoqaiVxHM-29BEB0?style=flat&logo=go
|
||||
[play-marshal]: https://play.golang.org/p/NxMoqaiVxHM
|
||||
[play-interceptor]: https://play.golang.org/p/upmVP5cO62Y
|
||||
[play-interceptor-img]: https://img.shields.io/badge/playground-upmVP5cO62Y-29BEB0?style=flat&logo=go
|
||||
[benchmark]: https://github.com/khorevaa/logos/actions?query=workflow%3Abenchmark
|
||||
[zerolog]: https://github.com/rs/zerolog
|
||||
[glog]: https://github.com/golang/glog
|
||||
[quicktemplate]: https://github.com/valyala/quicktemplate
|
||||
[gjson]: https://github.com/tidwall/gjson
|
||||
[zap]: https://github.com/uber-go/zap
|
||||
[lumberjack]: https://github.com/natefinch/lumberjack
|
91
appender/appender.go
Normal file
91
appender/appender.go
Normal file
@ -0,0 +1,91 @@
|
||||
package appender
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/khorevaa/logos/appender/console"
|
||||
"github.com/khorevaa/logos/appender/file"
|
||||
"github.com/khorevaa/logos/appender/gelfudp"
|
||||
"github.com/khorevaa/logos/appender/rollingfile"
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var (
|
||||
writers = map[string]WriterFactory{}
|
||||
encoders = map[string]EncoderFactory{}
|
||||
)
|
||||
|
||||
type WriterFactory func(config *common.Config) (zapcore.WriteSyncer, error)
|
||||
type EncoderFactory func(*common.Config) (zapcore.Encoder, error)
|
||||
|
||||
type Appender struct {
|
||||
Writer zapcore.WriteSyncer
|
||||
Encoder zapcore.Encoder
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterWriterType("console", console.New)
|
||||
RegisterWriterType("file", file.New)
|
||||
RegisterWriterType("rolling_file", rollingfile.New)
|
||||
RegisterWriterType("gelf_udp", gelfudp.New)
|
||||
}
|
||||
|
||||
func CreateAppender(writerType string, config *common.Config) (*Appender, error) {
|
||||
w, err := NewWriter(writerType, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoderConfig, err := config.Child("encoder", -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ec := EncoderConfig{}
|
||||
if err := encoderConfig.Unpack(&ec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e, err := CreateEncoder(ec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Appender{w, e}, nil
|
||||
}
|
||||
|
||||
func RegisterWriterType(name string, f WriterFactory) {
|
||||
if writers[name] != nil {
|
||||
panic(fmt.Errorf("writer type '%v' exists already", name))
|
||||
}
|
||||
writers[name] = f
|
||||
}
|
||||
|
||||
func NewWriter(name string, config *common.Config) (zapcore.WriteSyncer, error) {
|
||||
factory := writers[name]
|
||||
if factory == nil {
|
||||
return nil, fmt.Errorf("writer type %v undefined", name)
|
||||
}
|
||||
return factory(config)
|
||||
}
|
||||
|
||||
type EncoderConfig struct {
|
||||
Namespace common.ConfigNamespace `logos-config:",inline"`
|
||||
}
|
||||
|
||||
func RegisterEncoderType(name string, gen EncoderFactory) {
|
||||
if _, exists := encoders[name]; exists {
|
||||
panic(fmt.Sprintf("encoder %q already registered", name))
|
||||
}
|
||||
encoders[name] = gen
|
||||
}
|
||||
|
||||
func CreateEncoder(cfg EncoderConfig) (zapcore.Encoder, error) {
|
||||
// default to json encoder
|
||||
encoder := "json"
|
||||
if name := cfg.Namespace.Name(); name != "" {
|
||||
encoder = name
|
||||
}
|
||||
|
||||
factory := encoders[encoder]
|
||||
if factory == nil {
|
||||
return nil, fmt.Errorf("'%v' encoder is not available", encoder)
|
||||
}
|
||||
return factory(cfg.Namespace.Config())
|
||||
}
|
67
appender/console/console.go
Normal file
67
appender/console/console.go
Normal file
@ -0,0 +1,67 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"github.com/mattn/go-colorable"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type Console struct {
|
||||
zapcore.WriteSyncer
|
||||
colorable bool
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Target `logos-config:"target" logos-validate:"required,logos.oneof=stderr stdout discard"`
|
||||
NoColor bool `logos-config:"no_color"`
|
||||
}
|
||||
|
||||
type Target = string
|
||||
|
||||
const (
|
||||
Discard Target = "discard"
|
||||
Stdout Target = "stdout"
|
||||
Stderr Target = "stderr"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultConfig = Config{
|
||||
Target: Stdout,
|
||||
}
|
||||
)
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return defaultConfig
|
||||
}
|
||||
|
||||
func New(v *common.Config) (zapcore.WriteSyncer, error) {
|
||||
cfg := DefaultConfig()
|
||||
if err := v.Unpack(&cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch cfg.Target {
|
||||
case Stdout:
|
||||
return NewConsole(cfg, os.Stdout), nil
|
||||
case Stderr:
|
||||
return NewConsole(cfg, os.Stderr), nil
|
||||
case Discard:
|
||||
return &Console{zapcore.AddSync(ioutil.Discard), false}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown target %q", cfg.Target)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func NewConsole(config Config, file *os.File) zapcore.WriteSyncer {
|
||||
|
||||
if config.NoColor {
|
||||
return &Console{zapcore.AddSync(colorable.NewNonColorable(file)), false}
|
||||
}
|
||||
|
||||
return &Console{zapcore.AddSync(colorable.NewColorable(file)), true}
|
||||
|
||||
}
|
35
appender/file/file.go
Normal file
35
appender/file/file.go
Normal file
@ -0,0 +1,35 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"os"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
*os.File
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
FileName string `logos-config:"file_name" logos-validate:"required"`
|
||||
}
|
||||
|
||||
var (
|
||||
defaultConfig = Config{}
|
||||
)
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return defaultConfig
|
||||
}
|
||||
|
||||
func New(v *common.Config) (zapcore.WriteSyncer, error) {
|
||||
cfg := DefaultConfig()
|
||||
if err := v.Unpack(&cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.OpenFile(cfg.FileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &File{f}, nil
|
||||
}
|
12
appender/file/file_test.go
Normal file
12
appender/file/file_test.go
Normal file
@ -0,0 +1,12 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDefaultConfig(t *testing.T) {
|
||||
c := DefaultConfig()
|
||||
assert.Empty(t, c.FileName)
|
||||
}
|
193
appender/gelfudp/gelfudp.go
Normal file
193
appender/gelfudp/gelfudp.go
Normal file
@ -0,0 +1,193 @@
|
||||
package gelfudp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Host string `logos-config:"host"`
|
||||
Port int `logos-config:"port"`
|
||||
CompressionType string `logos-config:"compression_type" logos-validate:"logos.oneof=none gzip zlib"`
|
||||
CompressionLevel int `logos-config:"compression_level"`
|
||||
}
|
||||
|
||||
var defaultConfig = Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: 12201,
|
||||
CompressionType: "gzip",
|
||||
CompressionLevel: gzip.DefaultCompression,
|
||||
}
|
||||
|
||||
const (
|
||||
MaxDatagramSize = 1420
|
||||
HeadSize = 12
|
||||
MaxChunkSize = MaxDatagramSize - HeadSize
|
||||
MaxChunks = 128
|
||||
MaxMessageSize = MaxChunkSize * MaxChunks
|
||||
|
||||
CompressionNone = 0
|
||||
CompressionGzip = 1
|
||||
CompressionZlib = 2
|
||||
)
|
||||
|
||||
var Magic = []byte{0x1e, 0x0f}
|
||||
var ErrTooLargeMessageSize = errors.New("too large message size")
|
||||
|
||||
type UDPSender struct {
|
||||
raddr *net.UDPAddr
|
||||
conn *net.UDPConn
|
||||
id IdGenerator
|
||||
}
|
||||
|
||||
func NewUDPSender(address string) (*UDPSender, error) {
|
||||
ip, err := GuessIP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id := NewDefaultIdGenerator(ip)
|
||||
raddr, err := net.ResolveUDPAddr("udp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UDPSender{
|
||||
raddr: raddr,
|
||||
conn: conn,
|
||||
id: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *UDPSender) Send(message []byte) error {
|
||||
if len(message) > MaxMessageSize {
|
||||
return ErrTooLargeMessageSize
|
||||
}
|
||||
|
||||
if len(message) <= MaxDatagramSize {
|
||||
_, err := s.conn.WriteToUDP(message, s.raddr)
|
||||
return err
|
||||
}
|
||||
|
||||
chunks := len(message) / MaxChunkSize
|
||||
if chunks*MaxChunkSize < len(message) {
|
||||
chunks = chunks + 1
|
||||
}
|
||||
|
||||
messageID := s.id.NextId()
|
||||
chunk := make([]byte, MaxDatagramSize)
|
||||
for i := 0; i < chunks; i++ {
|
||||
copy(chunk[0:2], Magic)
|
||||
binary.BigEndian.PutUint64(chunk[2:10], messageID)
|
||||
chunk[10] = byte(i)
|
||||
chunk[11] = byte(chunks)
|
||||
begin, end := i*MaxChunkSize, (i+1)*MaxChunkSize
|
||||
if end > len(message) {
|
||||
end = len(message)
|
||||
}
|
||||
copy(chunk[12:12+end-begin], message[begin:end])
|
||||
_, err := s.conn.WriteToUDP(chunk[0:12+end-begin], s.raddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCompressor(compressionType string, compressionLevel int) (*Compressor, error) {
|
||||
switch compressionType {
|
||||
case "none":
|
||||
return &Compressor{CompressionNone, compressionLevel}, nil
|
||||
case "gzip":
|
||||
if _, err := gzip.NewWriterLevel(ioutil.Discard, compressionLevel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Compressor{CompressionGzip, compressionLevel}, nil
|
||||
case "zlib":
|
||||
if _, err := zlib.NewWriterLevel(ioutil.Discard, compressionLevel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Compressor{CompressionZlib, compressionLevel}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("no compression type %q", compressionType)
|
||||
}
|
||||
}
|
||||
|
||||
type Compressor struct {
|
||||
compressionType int
|
||||
compressionLevel int
|
||||
}
|
||||
|
||||
func (c *Compressor) Compress(buf []byte) (int, []byte, error) {
|
||||
var (
|
||||
cw io.WriteCloser
|
||||
cBuf bytes.Buffer
|
||||
err error
|
||||
)
|
||||
switch c.compressionType {
|
||||
case CompressionNone:
|
||||
return len(buf), buf, nil
|
||||
case CompressionGzip:
|
||||
cw, err = gzip.NewWriterLevel(&cBuf, c.compressionLevel)
|
||||
case CompressionZlib:
|
||||
cw, err = zlib.NewWriterLevel(&cBuf, c.compressionLevel)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
n, err := cw.Write(buf)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if err := cw.Close(); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return n, cBuf.Bytes(), nil
|
||||
}
|
||||
|
||||
type Writer struct {
|
||||
sender *UDPSender
|
||||
compressor *Compressor
|
||||
}
|
||||
|
||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||
n, b, err := w.compressor.Compress(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := w.sender.Send(b); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func New(rawConfig *common.Config) (zapcore.WriteSyncer, error) {
|
||||
config := defaultConfig
|
||||
if err := rawConfig.Unpack(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := NewCompressor(config.CompressionType, config.CompressionLevel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s, err := NewUDPSender(fmt.Sprintf("%s:%d", config.Host, config.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return zapcore.AddSync(&Writer{s, c}), nil
|
||||
}
|
51
appender/gelfudp/id.go
Normal file
51
appender/gelfudp/id.go
Normal file
@ -0,0 +1,51 @@
|
||||
package gelfudp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IdGenerator interface {
|
||||
NextId() uint64
|
||||
}
|
||||
|
||||
type DefaultIdGenerator struct {
|
||||
ip uint64
|
||||
}
|
||||
|
||||
func NewDefaultIdGenerator(ip uint64) *DefaultIdGenerator {
|
||||
return &DefaultIdGenerator{ip}
|
||||
}
|
||||
|
||||
func (g *DefaultIdGenerator) NextId() uint64 {
|
||||
timestamp := uint64(time.Now().Nanosecond())
|
||||
timestamp = timestamp << 32
|
||||
timestamp = timestamp >> 32
|
||||
return (g.ip << 32) | timestamp
|
||||
}
|
||||
|
||||
func GuessIP() (uint64, error) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ips, err := net.LookupIP(hostname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, ip := range ips {
|
||||
if ip.To4() != nil {
|
||||
parts := strings.Split(ip.String(), ".")
|
||||
a, _ := strconv.ParseUint(parts[0], 10, 64)
|
||||
b, _ := strconv.ParseUint(parts[1], 10, 64)
|
||||
c, _ := strconv.ParseUint(parts[2], 10, 64)
|
||||
d, _ := strconv.ParseUint(parts[3], 10, 64)
|
||||
return (a << 24) | (b << 16) | (c << 8) | d, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New("cannot resolve ip by hostname")
|
||||
}
|
64
appender/rollingfile/rollingfile.go
Normal file
64
appender/rollingfile/rollingfile.go
Normal file
@ -0,0 +1,64 @@
|
||||
package rollingfile
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
type RollingFile struct {
|
||||
zapcore.WriteSyncer
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
// FileName is the file to write logs to. Backup log files will be retained
|
||||
// in the same directory. It uses <processname>-lumberjack.log in
|
||||
// os.TempDir() if empty.
|
||||
FileName string `logos-config:"file_name" logos-validate:"required"`
|
||||
|
||||
// MaxSize is the maximum size in megabytes of the log file before it gets
|
||||
// rotated. It defaults to 100 megabytes.
|
||||
MaxSize int `logos-config:"max_size" logos-validate:"min=1"`
|
||||
|
||||
// MaxAge is the maximum number of days to retain old log files based on the
|
||||
// timestamp encoded in their filename. Note that a day is defined as 24
|
||||
// hours and may not exactly correspond to calendar days due to daylight
|
||||
// savings, leap seconds, etc. The default is not to remove old log files
|
||||
// based on age.
|
||||
MaxAge int `logos-config:"max_age"`
|
||||
|
||||
// MaxBackups is the maximum number of old log files to retain. The default
|
||||
// is to retain all old log files (though MaxAge may still cause them to get
|
||||
// deleted.)
|
||||
MaxBackups int `logos-config:"max_backups"`
|
||||
|
||||
// LocalTime determines if the time used for formatting the timestamps in
|
||||
// backup files is the computer's local time. The default is to use UTC
|
||||
// time.
|
||||
LocalTime bool `logos-config:"local_time"`
|
||||
|
||||
// Compress determines if the rotated log files should be compressed
|
||||
// using gzip. The default is not to perform compression.
|
||||
Compress bool `logos-config:"compress"`
|
||||
}
|
||||
|
||||
func New(v *common.Config) (zapcore.WriteSyncer, error) {
|
||||
cfg := Config{
|
||||
MaxSize: 500,
|
||||
}
|
||||
if err := v.Unpack(&cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cfg.MaxAge == 0 {
|
||||
cfg.MaxAge = 7
|
||||
}
|
||||
w := &lumberjack.Logger{
|
||||
Filename: cfg.FileName,
|
||||
MaxSize: cfg.MaxSize,
|
||||
MaxAge: cfg.MaxAge,
|
||||
MaxBackups: cfg.MaxBackups,
|
||||
LocalTime: cfg.LocalTime,
|
||||
Compress: cfg.Compress,
|
||||
}
|
||||
return &RollingFile{zapcore.AddSync(w)}, nil
|
||||
}
|
32
appender/rollingfile/rollingfile_test.go
Normal file
32
appender/rollingfile/rollingfile_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package rollingfile
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewRollingFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config string
|
||||
hasErr bool
|
||||
}{
|
||||
{"case1", `
|
||||
file_name: /tmp/app.log
|
||||
max_size: -1
|
||||
encoder:
|
||||
json:`, true},
|
||||
{"case2", `
|
||||
file_name: /tmp/app.log
|
||||
encoder:
|
||||
json:`, false},
|
||||
}
|
||||
|
||||
for _, c := range tests {
|
||||
cfg, err := common.NewConfigFrom(c.config)
|
||||
assert.Nil(t, err, c.name)
|
||||
_, err = New(cfg)
|
||||
assert.Equal(t, c.hasErr, err != nil, c.name)
|
||||
}
|
||||
}
|
37
benchmarks/apex_test.go
Normal file
37
benchmarks/apex_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package benchmarks
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/apex/log/handlers/json"
|
||||
)
|
||||
|
||||
func newDisabledApexLog() *log.Logger {
|
||||
return &log.Logger{
|
||||
Handler: json.New(ioutil.Discard),
|
||||
Level: log.ErrorLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func newApexLog() *log.Logger {
|
||||
return &log.Logger{
|
||||
Handler: json.New(ioutil.Discard),
|
||||
Level: log.DebugLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func fakeApexFields() log.Fields {
|
||||
return log.Fields{
|
||||
"int": _tenInts[0],
|
||||
"ints": _tenInts,
|
||||
"string": _tenStrings[0],
|
||||
"strings": _tenStrings,
|
||||
"time": _tenTimes[0],
|
||||
"times": _tenTimes,
|
||||
"user1": _oneUser,
|
||||
"user2": _oneUser,
|
||||
"users": _tenUsers,
|
||||
"error": errExample,
|
||||
}
|
||||
}
|
12
benchmarks/go.mod
Normal file
12
benchmarks/go.mod
Normal file
@ -0,0 +1,12 @@
|
||||
module benchmarks
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/apex/log v1.9.0
|
||||
github.com/phuslu/log v1.0.60
|
||||
github.com/rs/zerolog v1.20.0
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
go.uber.org/multierr v1.6.0
|
||||
go.uber.org/zap v1.16.0
|
||||
)
|
136
benchmarks/go.sum
Normal file
136
benchmarks/go.sum
Normal file
@ -0,0 +1,136 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0=
|
||||
github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=
|
||||
github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
|
||||
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
|
||||
github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
|
||||
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elastic/go-ucfg v0.8.3 h1:leywnFjzr2QneZZWhE6uWd+QN/UpP0sdJRHYyuFvkeo=
|
||||
github.com/elastic/go-ucfg v0.8.3/go.mod h1:iaiY0NBIYeasNgycLyTvhJftQlQEUO2hpF+FX0JKxzo=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
|
||||
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/phuslu/log v1.0.60 h1:9BsTv2UGYS003TMejH0/ycWdQGPrOqwrUSmnZgAgqhU=
|
||||
github.com/phuslu/log v1.0.60/go.mod h1:kzJN3LRifrepxThMjufQwS7S35yFAB+jAV1qgA7eBW4=
|
||||
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/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
||||
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
||||
github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc=
|
||||
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
|
||||
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
||||
github.com/khorevaa/logos v0.0.0-20210128201039-04350835b235 h1:bnt2KYugM3aTSE6VH4kRYFdFE0z18ch8kchQ4vtP0yU=
|
||||
github.com/khorevaa/logos v0.0.0-20210128201039-04350835b235/go.mod h1:nhDpfkExY+70/vYt47VjnDulF6Tt5V17uNy3+Lsu7a8=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
159
benchmarks/logos_test.go
Normal file
159
benchmarks/logos_test.go
Normal file
@ -0,0 +1,159 @@
|
||||
package benchmarks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/khorevaa/logos"
|
||||
"time"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var (
|
||||
errExample = errors.New("fail")
|
||||
|
||||
_messages = fakeMessages(1000)
|
||||
_tenInts = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
|
||||
_tenStrings = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
|
||||
_tenTimes = []time.Time{
|
||||
time.Unix(0, 0),
|
||||
time.Unix(1, 0),
|
||||
time.Unix(2, 0),
|
||||
time.Unix(3, 0),
|
||||
time.Unix(4, 0),
|
||||
time.Unix(5, 0),
|
||||
time.Unix(6, 0),
|
||||
time.Unix(7, 0),
|
||||
time.Unix(8, 0),
|
||||
time.Unix(9, 0),
|
||||
}
|
||||
_oneUser = &user{
|
||||
Name: "Jane Doe",
|
||||
Email: "jane@test.com",
|
||||
CreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC),
|
||||
}
|
||||
_tenUsers = users{
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
const newConfig = `
|
||||
appenders:
|
||||
console:
|
||||
- name: CONSOLE
|
||||
target: discard
|
||||
encoder:
|
||||
console:
|
||||
loggers:
|
||||
root:
|
||||
level: info
|
||||
appender_refs:
|
||||
- CONSOLE
|
||||
`
|
||||
err := logos.InitWithConfigContent(newConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeMessages(n int) []string {
|
||||
messages := make([]string, n)
|
||||
for i := range messages {
|
||||
messages[i] = fmt.Sprintf("Test logging, but use a somewhat realistic message length. (#%v)", i)
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
func getMessage(iter int) string {
|
||||
return _messages[iter%1000]
|
||||
}
|
||||
|
||||
type users []*user
|
||||
|
||||
func (uu users) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
var err error
|
||||
for i := range uu {
|
||||
err = multierr.Append(err, arr.AppendObject(uu[i]))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type user struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
func (u *user) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||
enc.AddString("name", u.Name)
|
||||
enc.AddString("email", u.Email)
|
||||
enc.AddInt64("createdAt", u.CreatedAt.UnixNano())
|
||||
return nil
|
||||
}
|
||||
|
||||
func newZapLogger(lvl zapcore.Level) logos.Logger {
|
||||
log := logos.New("test")
|
||||
log.SetLLevel(lvl)
|
||||
return log
|
||||
|
||||
}
|
||||
|
||||
func fakeFields() []interface{} {
|
||||
return []interface{}{
|
||||
zap.Int("int", _tenInts[0]),
|
||||
zap.Ints("ints", _tenInts),
|
||||
zap.String("string", _tenStrings[0]),
|
||||
zap.Strings("strings", _tenStrings),
|
||||
zap.Time("time", _tenTimes[0]),
|
||||
zap.Times("times", _tenTimes),
|
||||
zap.Object("user1", _oneUser),
|
||||
zap.Object("user2", _oneUser),
|
||||
zap.Array("users", _tenUsers),
|
||||
zap.Error(errExample),
|
||||
}
|
||||
}
|
||||
|
||||
func fakeSugarFields() []interface{} {
|
||||
return []interface{}{
|
||||
"int", _tenInts[0],
|
||||
"ints", _tenInts,
|
||||
"string", _tenStrings[0],
|
||||
"strings", _tenStrings,
|
||||
"time", _tenTimes[0],
|
||||
"times", _tenTimes,
|
||||
"user1", _oneUser,
|
||||
"user2", _oneUser,
|
||||
"users", _tenUsers,
|
||||
"error", errExample,
|
||||
}
|
||||
}
|
||||
|
||||
func fakeFmtArgs() []interface{} {
|
||||
// Need to keep this a function instead of a package-global var so that we
|
||||
// pay the cast-to-interface{} penalty on each call.
|
||||
return []interface{}{
|
||||
_tenInts[0],
|
||||
_tenInts,
|
||||
_tenStrings[0],
|
||||
_tenStrings,
|
||||
_tenTimes[0],
|
||||
_tenTimes,
|
||||
_oneUser,
|
||||
_oneUser,
|
||||
_tenUsers,
|
||||
errExample,
|
||||
}
|
||||
}
|
37
benchmarks/logus_test.go
Normal file
37
benchmarks/logus_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package benchmarks
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func newDisabledLogrus() *logrus.Logger {
|
||||
logger := newLogrus()
|
||||
logger.Level = logrus.ErrorLevel
|
||||
return logger
|
||||
}
|
||||
|
||||
func newLogrus() *logrus.Logger {
|
||||
return &logrus.Logger{
|
||||
Out: ioutil.Discard,
|
||||
Formatter: new(logrus.JSONFormatter),
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.DebugLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func fakeLogrusFields() logrus.Fields {
|
||||
return logrus.Fields{
|
||||
"int": _tenInts[0],
|
||||
"ints": _tenInts,
|
||||
"string": _tenStrings[0],
|
||||
"strings": _tenStrings,
|
||||
"time": _tenTimes[0],
|
||||
"times": _tenTimes,
|
||||
"user1": _oneUser,
|
||||
"user2": _oneUser,
|
||||
"users": _tenUsers,
|
||||
"error": errExample,
|
||||
}
|
||||
}
|
67
benchmarks/main/main_test.go
Normal file
67
benchmarks/main/main_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos"
|
||||
"github.com/phuslu/log"
|
||||
"github.com/rs/zerolog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var fakeMessage = "Test logging, but use a somewhat realistic message length. "
|
||||
|
||||
func BenchmarkLogos(b *testing.B) {
|
||||
|
||||
const newConfig = `
|
||||
appenders:
|
||||
console:
|
||||
- name: CONSOLE
|
||||
target: discard
|
||||
encoder:
|
||||
console:
|
||||
loggers:
|
||||
root:
|
||||
level: info
|
||||
appender_refs:
|
||||
- CONSOLE
|
||||
`
|
||||
err := logos.InitWithConfigContent(newConfig)
|
||||
if err != nil {
|
||||
//panic(err)
|
||||
}
|
||||
|
||||
logger := logos.New("benchmark")
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Info(fakeMessage, zap.String("foo", "bar"), zap.Int("int", 42))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkZap(b *testing.B) {
|
||||
logger := zap.New(zapcore.NewCore(
|
||||
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
|
||||
zapcore.AddSync(ioutil.Discard),
|
||||
zapcore.InfoLevel,
|
||||
))
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Info(fakeMessage, zap.String("foo", "bar"), zap.Int("int", 42))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkZeroLog(b *testing.B) {
|
||||
logger := zerolog.New(ioutil.Discard).With().Timestamp().Logger()
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Info().Str("foo", "bar").Int("int", 42).Msg(fakeMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPhusLog(b *testing.B) {
|
||||
logger := log.Logger{
|
||||
TimeFormat: "", // uses rfc3339 by default
|
||||
Writer: log.IOWriter{ioutil.Discard},
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
logger.Info().Str("foo", "bar").Int("int", 42).Msg(fakeMessage)
|
||||
}
|
||||
}
|
182
benchmarks/scenario_brench_test.go
Normal file
182
benchmarks/scenario_brench_test.go
Normal file
@ -0,0 +1,182 @@
|
||||
package benchmarks
|
||||
|
||||
import (
|
||||
//"io/ioutil"
|
||||
//"log"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func BenchmarkDisabledWithoutFields(b *testing.B) {
|
||||
b.Logf("Logging at a disabled level without any structured context.")
|
||||
b.Run("Logos", func(b *testing.B) {
|
||||
logger := newZapLogger(zap.ErrorLevel)
|
||||
i := 0
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
i++
|
||||
logger.Info(getMessage(i))
|
||||
}
|
||||
})
|
||||
})
|
||||
b.Run("Zap.Check", func(b *testing.B) {
|
||||
logger := newSampledLogger(zap.ErrorLevel)
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
if m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil {
|
||||
m.Write()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
b.Run("Zap.Sugar", func(b *testing.B) {
|
||||
logger := newSampledLogger(zap.ErrorLevel).Sugar()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logger.Info(getMessage(0))
|
||||
}
|
||||
})
|
||||
})
|
||||
//b.Run("Zap.SugarFormatting", func(b *testing.B) {
|
||||
// logger := newZapLogger(zap.ErrorLevel).Sugar()
|
||||
// b.ResetTimer()
|
||||
// b.RunParallel(func(pb *testing.PB) {
|
||||
// for pb.Next() {
|
||||
// logger.Infof("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...)
|
||||
// }
|
||||
// })
|
||||
//})
|
||||
b.Run("apex/log", func(b *testing.B) {
|
||||
logger := newDisabledApexLog()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logger.Info(getMessage(0))
|
||||
}
|
||||
})
|
||||
})
|
||||
b.Run("sirupsen/logrus", func(b *testing.B) {
|
||||
logger := newDisabledLogrus()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logger.Info(getMessage(0))
|
||||
}
|
||||
})
|
||||
})
|
||||
//b.Run("rs/zerolog", func(b *testing.B) {
|
||||
// logger := newDisabledZerolog()
|
||||
// b.ResetTimer()
|
||||
// b.RunParallel(func(pb *testing.PB) {
|
||||
// for pb.Next() {
|
||||
// logger.Info().Msg(getMessage(0))
|
||||
// }
|
||||
// })
|
||||
//})
|
||||
}
|
||||
|
||||
func BenchmarkAddingFields(b *testing.B) {
|
||||
b.Logf("Logging with additional context at each log site.")
|
||||
b.Run("Zap", func(b *testing.B) {
|
||||
logger := newZapLogger(zap.DebugLevel)
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logger.Infow(getMessage(0), fakeFields()...)
|
||||
}
|
||||
})
|
||||
})
|
||||
b.Run("Zap.Check", func(b *testing.B) {
|
||||
logger := newSampledLogger(zap.DebugLevel)
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
if ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil {
|
||||
//ce.Write(fakeFields()...)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
b.Run("Zap.CheckSampled", func(b *testing.B) {
|
||||
logger := newSampledLogger(zap.DebugLevel)
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
i++
|
||||
if ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil {
|
||||
//ce.Write(fakeFields()...)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
b.Run("Zap.Sugar", func(b *testing.B) {
|
||||
logger := newSampledLogger(zap.DebugLevel).Sugar()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logger.Infow(getMessage(0), fakeSugarFields()...)
|
||||
}
|
||||
})
|
||||
})
|
||||
b.Run("apex/log", func(b *testing.B) {
|
||||
logger := newApexLog()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logger.WithFields(fakeApexFields()).Info(getMessage(0))
|
||||
}
|
||||
})
|
||||
})
|
||||
//b.Run("go-kit/kit/log", func(b *testing.B) {
|
||||
// logger := newKitLog()
|
||||
// b.ResetTimer()
|
||||
// b.RunParallel(func(pb *testing.PB) {
|
||||
// for pb.Next() {
|
||||
// logger.Log(fakeSugarFields()...)
|
||||
// }
|
||||
// })
|
||||
//})
|
||||
//b.Run("inconshreveable/log15", func(b *testing.B) {
|
||||
// logger := newLog15()
|
||||
// b.ResetTimer()
|
||||
// b.RunParallel(func(pb *testing.PB) {
|
||||
// for pb.Next() {
|
||||
// logger.Info(getMessage(0), fakeSugarFields()...)
|
||||
// }
|
||||
// })
|
||||
//})
|
||||
b.Run("sirupsen/logrus", func(b *testing.B) {
|
||||
logger := newLogrus()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logger.WithFields(fakeLogrusFields()).Info(getMessage(0))
|
||||
}
|
||||
})
|
||||
})
|
||||
//b.Run("rs/zerolog", func(b *testing.B) {
|
||||
// logger := newZerolog()
|
||||
// b.ResetTimer()
|
||||
// b.RunParallel(func(pb *testing.PB) {
|
||||
// for pb.Next() {
|
||||
// fakeZerologFields(logger.Info()).Msg(getMessage(0))
|
||||
// }
|
||||
// })
|
||||
//})
|
||||
//b.Run("rs/zerolog.Check", func(b *testing.B) {
|
||||
// logger := newZerolog()
|
||||
// b.ResetTimer()
|
||||
// b.RunParallel(func(pb *testing.PB) {
|
||||
// for pb.Next() {
|
||||
// if e := logger.Info(); e.Enabled() {
|
||||
// fakeZerologFields(e).Msg(getMessage(0))
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
//})
|
||||
}
|
60
config/config.go
Normal file
60
config/config.go
Normal file
@ -0,0 +1,60 @@
|
||||
package config
|
||||
|
||||
import "github.com/khorevaa/logos/internal/common"
|
||||
|
||||
const DefaultConfig = `
|
||||
appenders:
|
||||
console:
|
||||
- name: CONSOLE
|
||||
target: stdout
|
||||
encoder:
|
||||
console:
|
||||
time_encoder: ISO8601
|
||||
loggerConfigs:
|
||||
root:
|
||||
level: info
|
||||
appender_refs:
|
||||
- CONSOLE
|
||||
logger:
|
||||
- name: stdlog
|
||||
level: info
|
||||
add_caller: true
|
||||
|
||||
scan: false
|
||||
scan_period: 1m
|
||||
`
|
||||
|
||||
type Config struct {
|
||||
Appenders map[string][]*common.Config `logos-config:"appenders"`
|
||||
Loggers Loggers `logos-config:"loggerConfigs"`
|
||||
}
|
||||
|
||||
type ScanConfig struct {
|
||||
Scan bool `logos-config:"scan"`
|
||||
ScanPeriod string `logos-config:"scan_period"`
|
||||
}
|
||||
|
||||
type Loggers struct {
|
||||
Root RootLogger `logos-config:"root"`
|
||||
Logger []LoggerConfig `logos-config:"logger"`
|
||||
}
|
||||
|
||||
type RootLogger struct {
|
||||
Level string `logos-config:"level"`
|
||||
AppenderRefs []string `logos-config:"appender_refs"`
|
||||
AppenderConfig []AppenderConfig `logos-config:"appenders"`
|
||||
}
|
||||
|
||||
type LoggerConfig struct {
|
||||
Name string `logos-config:"name" logos-validate:"required"`
|
||||
Level string `logos-config:"level"`
|
||||
AddCaller bool `logos-config:"add_caller"`
|
||||
TraceLevel string `logos-config:"trace_level"`
|
||||
AppenderRefs []string `logos-config:"appender_refs"`
|
||||
AppenderConfig []AppenderConfig `logos-config:"appenders"`
|
||||
}
|
||||
|
||||
type AppenderConfig struct {
|
||||
Name string `logos-config:"name" logos-validate:"required"`
|
||||
Level string `logos-config:"level"`
|
||||
}
|
71
config/config_test.go
Normal file
71
config/config_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos/appender/console"
|
||||
common2 "github.com/khorevaa/logos/encoder/common"
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
type ConfigNamespace struct {
|
||||
name string
|
||||
config console.Config
|
||||
}
|
||||
|
||||
func TestConfigFrom(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
config interface{}
|
||||
text []string
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
config: struct {
|
||||
Appenders map[string][]interface{} `logos-config:"appenders"`
|
||||
Loggers Loggers `logos-config:"loggerConfigs"`
|
||||
}{
|
||||
Appenders: map[string][]interface{}{
|
||||
"console": {struct {
|
||||
Name string `logos-config:"name"`
|
||||
Target string `logos-config:"target"`
|
||||
Encoder interface{} `logos-config:"encoder"`
|
||||
}{
|
||||
Name: "CONSOLE",
|
||||
Target: "stderr",
|
||||
Encoder: struct {
|
||||
Json common2.JsonEncoderConfig `logos-config:"json"`
|
||||
}{
|
||||
Json: common2.JsonEncoderConfig{
|
||||
TimeEncoder: "ISO8601",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Loggers: Loggers{
|
||||
Root: RootLogger{
|
||||
Level: "error",
|
||||
AppenderRefs: []string{"CONSOLE"},
|
||||
//AppenderConfig: nil,
|
||||
},
|
||||
}},
|
||||
|
||||
text: []string{"hello world", "hello"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
cfg, _ := common.NewConfigFrom(tt.config)
|
||||
|
||||
defConfig, err := common.NewConfigFrom(DefaultConfig)
|
||||
|
||||
cfg, err = common.MergeConfigs(defConfig, cfg)
|
||||
t.Error(err)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
33
encoder/common/common.go
Normal file
33
encoder/common/common.go
Normal file
@ -0,0 +1,33 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// Config is used to pass encoding parameters to New.
|
||||
type JsonEncoderConfig struct {
|
||||
TimeKey string `logos-config:"time_key"`
|
||||
LevelKey string `logos-config:"level_key"`
|
||||
NameKey string `logos-config:"name_key"`
|
||||
CallerKey string `logos-config:"caller_key"`
|
||||
MessageKey string `logos-config:"message_key"`
|
||||
StacktraceKey string `logos-config:"stacktrace_key"`
|
||||
LineEnding string `logos-config:"line_ending"`
|
||||
TimeEncoder string `logos-config:"time_encoder" logos-validate:"logos.oneof=epoch epoch_millis epoch_nanos ISO8601"`
|
||||
}
|
||||
|
||||
func GetTimeEncoder(name string) (zapcore.TimeEncoder, error) {
|
||||
switch name {
|
||||
case "epoch":
|
||||
return zapcore.EpochTimeEncoder, nil
|
||||
case "epoch_millis":
|
||||
return zapcore.EpochMillisTimeEncoder, nil
|
||||
case "epoch_nanos":
|
||||
return zapcore.EpochNanosTimeEncoder, nil
|
||||
case "ISO8601":
|
||||
return zapcore.ISO8601TimeEncoder, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("no such TimeEncoder %q", name)
|
||||
}
|
||||
}
|
182
encoder/console/color.go
Normal file
182
encoder/console/color.go
Normal file
@ -0,0 +1,182 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// No color
|
||||
NoColor uint16 = 1 << 15
|
||||
)
|
||||
|
||||
const (
|
||||
// Foreground colors for ColorScheme.
|
||||
_ uint16 = iota | NoColor
|
||||
Black
|
||||
Red
|
||||
Green
|
||||
Yellow
|
||||
Blue
|
||||
Magenta
|
||||
Cyan
|
||||
White
|
||||
bitsForeground = 0
|
||||
maskForeground = 0xf
|
||||
ansiForegroundOffset = 30 - 1
|
||||
)
|
||||
|
||||
const (
|
||||
// Background colors for ColorScheme.
|
||||
_ uint16 = iota<<bitsBackground | NoColor
|
||||
BackgroundBlack
|
||||
BackgroundRed
|
||||
BackgroundGreen
|
||||
BackgroundYellow
|
||||
BackgroundBlue
|
||||
BackgroundMagenta
|
||||
BackgroundCyan
|
||||
BackgroundWhite
|
||||
bitsBackground = 4
|
||||
maskBackground = 0xf << bitsBackground
|
||||
ansiBackgroundOffset = 40 - 1
|
||||
)
|
||||
|
||||
const (
|
||||
// Bold flag for ColorScheme.
|
||||
Bold uint16 = 1<<bitsBold | NoColor
|
||||
bitsBold = 8
|
||||
maskBold = 1 << bitsBold
|
||||
ansiBold = 1
|
||||
)
|
||||
|
||||
// To use with SetColorScheme.
|
||||
type ColorScheme struct {
|
||||
Bool uint16
|
||||
Integer uint16
|
||||
Float uint16
|
||||
String uint16
|
||||
StringQuotation uint16
|
||||
EscapedChar uint16
|
||||
FieldName uint16
|
||||
PointerAddress uint16
|
||||
Nil uint16
|
||||
Time uint16
|
||||
StructName uint16
|
||||
ObjectLength uint16
|
||||
|
||||
LogNaming uint16
|
||||
Timestamp uint16
|
||||
InfoLevel uint16
|
||||
WarnLevel uint16
|
||||
ErrorLevel uint16
|
||||
FatalLevel uint16
|
||||
PanicLevel uint16
|
||||
DPanicLevel uint16
|
||||
DebugLevel uint16
|
||||
}
|
||||
|
||||
var (
|
||||
defaultScheme = ColorScheme{
|
||||
Bool: Cyan | Bold,
|
||||
Integer: Blue | Bold,
|
||||
Float: Magenta | Bold,
|
||||
String: Red,
|
||||
StringQuotation: Red | Bold,
|
||||
EscapedChar: Magenta | Bold,
|
||||
FieldName: Yellow,
|
||||
PointerAddress: Blue | Bold,
|
||||
Nil: Cyan | Bold,
|
||||
Time: Blue | Bold,
|
||||
StructName: Green,
|
||||
ObjectLength: Blue,
|
||||
|
||||
LogNaming: Black | Bold | BackgroundWhite,
|
||||
Timestamp: Blue | Bold,
|
||||
InfoLevel: Green,
|
||||
WarnLevel: Yellow | Bold,
|
||||
ErrorLevel: Red,
|
||||
FatalLevel: Red | Bold,
|
||||
PanicLevel: Red | Bold,
|
||||
DPanicLevel: Black | Bold | BackgroundRed,
|
||||
DebugLevel: Blue,
|
||||
}
|
||||
|
||||
colorMap = map[string]uint16{
|
||||
"black": Black,
|
||||
"red": Red,
|
||||
"green": Green,
|
||||
"yellow": Yellow,
|
||||
"blue": Blue,
|
||||
"magenta": Magenta,
|
||||
"cyan": Cyan,
|
||||
"white": White,
|
||||
}
|
||||
|
||||
colorBgMap = map[string]uint16{
|
||||
"black": BackgroundBlack,
|
||||
"red": BackgroundRed,
|
||||
"green": BackgroundGreen,
|
||||
"yellow": BackgroundYellow,
|
||||
"blue": BackgroundBlue,
|
||||
"magenta": BackgroundMagenta,
|
||||
"cyan": BackgroundCyan,
|
||||
"white": BackgroundWhite,
|
||||
}
|
||||
)
|
||||
|
||||
func getColor(color string) uint16 {
|
||||
|
||||
val, ok := colorMap[strings.ToLower(color)]
|
||||
if ok {
|
||||
return val
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func getBgColor(color string) uint16 {
|
||||
|
||||
val, ok := colorBgMap[strings.ToLower(color)]
|
||||
if ok {
|
||||
return val
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (cs *ColorScheme) fixColors() {
|
||||
typ := reflect.Indirect(reflect.ValueOf(cs))
|
||||
defaultType := reflect.ValueOf(defaultScheme)
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
if field.Uint() == 0 {
|
||||
field.SetUint(defaultType.Field(i).Uint())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func colorizeText(text string, color uint16) string {
|
||||
foreground := color & maskForeground >> bitsForeground
|
||||
background := color & maskBackground >> bitsBackground
|
||||
bold := color & maskBold
|
||||
|
||||
if foreground == 0 && background == 0 && bold == 0 {
|
||||
return text
|
||||
}
|
||||
|
||||
modBold := ""
|
||||
modForeground := ""
|
||||
modBackground := ""
|
||||
|
||||
if bold > 0 {
|
||||
modBold = "\033[1m"
|
||||
}
|
||||
if foreground > 0 {
|
||||
modForeground = fmt.Sprintf("\033[%dm", foreground+ansiForegroundOffset)
|
||||
}
|
||||
if background > 0 {
|
||||
modBackground = fmt.Sprintf("\033[%dm", background+ansiBackgroundOffset)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s%s%s\033[0m", modForeground, modBackground, modBold, text)
|
||||
}
|
359
encoder/console/colorEncoder.go
Normal file
359
encoder/console/colorEncoder.go
Normal file
@ -0,0 +1,359 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var poolColoredEncoder = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &coloredEncoder{
|
||||
buf: bufferpool.Get(),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func getColoredEncoder(lvlColor uint16, scheme ColorScheme) *coloredEncoder {
|
||||
enc := poolColoredEncoder.Get().(*coloredEncoder)
|
||||
enc.buf = bufferpool.Get()
|
||||
enc.scheme = scheme
|
||||
enc.entLevelColor = lvlColor
|
||||
return enc
|
||||
}
|
||||
|
||||
func getLevelColor(level zapcore.Level, scheme ColorScheme) uint16 {
|
||||
switch level {
|
||||
case zapcore.DebugLevel:
|
||||
return scheme.DebugLevel
|
||||
case zapcore.InfoLevel:
|
||||
return scheme.InfoLevel
|
||||
case zapcore.ErrorLevel:
|
||||
return scheme.ErrorLevel
|
||||
case zapcore.PanicLevel:
|
||||
return scheme.PanicLevel
|
||||
case zapcore.WarnLevel:
|
||||
return scheme.WarnLevel
|
||||
case zapcore.DPanicLevel:
|
||||
return scheme.DPanicLevel
|
||||
case zapcore.FatalLevel:
|
||||
return scheme.FatalLevel
|
||||
default:
|
||||
return scheme.String
|
||||
}
|
||||
}
|
||||
|
||||
func putColoredEncoder(enc *coloredEncoder) {
|
||||
enc.scheme = defaultScheme
|
||||
enc.buf.Free()
|
||||
enc.entLevelColor = NoColor
|
||||
enc.EncodeDuration = nil
|
||||
enc.EncodeTime = nil
|
||||
poolColoredEncoder.Put(enc)
|
||||
}
|
||||
|
||||
type coloredEncoder struct {
|
||||
buf *buffer.Buffer
|
||||
disableColor bool
|
||||
scheme ColorScheme
|
||||
|
||||
entLevelColor uint16
|
||||
|
||||
EncodeDuration zapcore.DurationEncoder
|
||||
EncodeTime zapcore.TimeEncoder
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) addKey(key string) {
|
||||
e.buf.AppendByte(' ')
|
||||
e.appendColoredString(key, e.scheme.FieldName)
|
||||
e.buf.AppendByte('=')
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) appendColoredString(val string, color uint16) {
|
||||
|
||||
if e.disableColor {
|
||||
e.buf.AppendString(val)
|
||||
return
|
||||
}
|
||||
|
||||
e.buf.AppendString(colorizeText(val, color))
|
||||
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendBool(val bool) {
|
||||
e.addElementSeparator()
|
||||
e.appendColoredString(strconv.FormatBool(val), e.scheme.Bool)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendByteString(bstr []byte) {
|
||||
e.addElementSeparator()
|
||||
e.buf.AppendString(string(bstr))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendComplex128(val complex128) {
|
||||
r, i := float64(real(val)), float64(imag(val))
|
||||
|
||||
str := fmt.Sprintf("%s%s",
|
||||
strconv.FormatFloat(r, 'f', -1, 64),
|
||||
strconv.FormatFloat(i, 'f', -1, 64))
|
||||
e.addElementSeparator()
|
||||
e.appendColoredString(str, e.scheme.Float)
|
||||
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendComplex64(val complex64) {
|
||||
e.AppendComplex128(complex128(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendFloat64(val float64) {
|
||||
e.addElementSeparator()
|
||||
e.appendColoredString(strconv.FormatFloat(val, 'f', -1, 64), e.scheme.Float)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendFloat32(val float32) {
|
||||
e.AppendFloat64(float64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendInt(val int) {
|
||||
e.AppendInt64(int64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendInt64(val int64) {
|
||||
e.addElementSeparator()
|
||||
e.appendColoredString(strconv.FormatInt(val, 10), e.scheme.Integer)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendInt32(val int32) {
|
||||
e.AppendInt64(int64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendInt16(val int16) {
|
||||
e.AppendInt64(int64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendInt8(val int8) {
|
||||
e.AppendInt64(int64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendString(str string) {
|
||||
e.addElementSeparator()
|
||||
e.appendColoredString(str, e.scheme.String)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendUint(val uint) {
|
||||
e.AppendUint64(uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendUint64(val uint64) {
|
||||
e.addElementSeparator()
|
||||
e.appendColoredString(strconv.FormatUint(val, 64), e.scheme.PointerAddress)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendUint32(val uint32) {
|
||||
e.AppendUint64(uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendUint16(val uint16) {
|
||||
e.AppendUint64(uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendUint8(val uint8) {
|
||||
e.AppendUint64(uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendUintptr(val uintptr) {
|
||||
e.AppendUint64(uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
|
||||
e.addKey(key)
|
||||
return e.AppendArray(marshaler)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddObject(key string, obj zapcore.ObjectMarshaler) error {
|
||||
e.addKey(key)
|
||||
return e.AppendObject(obj)
|
||||
}
|
||||
|
||||
//func (enc *coloredEncoder) AppendArray(arr ArrayMarshaler) error {
|
||||
// enc.addElementSeparator()
|
||||
// enc.buf.AppendByte('[')
|
||||
// err := arr.MarshalLogArray(enc)
|
||||
// enc.buf.AppendByte(']')
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//func (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error {
|
||||
// enc.addElementSeparator()
|
||||
// enc.buf.AppendByte('{')
|
||||
// err := obj.MarshalLogObject(enc)
|
||||
// enc.buf.AppendByte('}')
|
||||
// return err
|
||||
//}
|
||||
|
||||
func (e *coloredEncoder) AddBinary(key string, val []byte) {
|
||||
e.AddString(key, base64.StdEncoding.EncodeToString(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddByteString(key string, val []byte) {
|
||||
e.AddString(key, base64.StdEncoding.EncodeToString(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddBool(key string, val bool) {
|
||||
e.addKey(key)
|
||||
e.AppendBool(val)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddComplex128(key string, val complex128) {
|
||||
e.addKey(key)
|
||||
e.AppendComplex128(val)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddComplex64(key string, val complex64) {
|
||||
e.addKey(key)
|
||||
e.AppendComplex128(complex128(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddDuration(key string, val time.Duration) {
|
||||
e.addKey(key)
|
||||
e.AppendDuration(val)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddFloat64(key string, val float64) {
|
||||
e.addKey(key)
|
||||
e.AppendFloat64(val)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddFloat32(key string, val float32) {
|
||||
e.AddFloat64(key, float64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddInt(key string, val int) {
|
||||
e.AddInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddInt64(key string, val int64) {
|
||||
e.addKey(key)
|
||||
e.AppendInt64(val)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddInt32(key string, val int32) {
|
||||
e.AddInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddInt16(key string, val int16) {
|
||||
e.AddInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddInt8(key string, val int8) {
|
||||
e.AddInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddString(key string, val string) {
|
||||
e.addKey(key)
|
||||
e.AppendString(val)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddTime(key string, val time.Time) {
|
||||
e.addKey(key)
|
||||
e.AppendTime(val)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddUint(key string, val uint) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddUint64(key string, val uint64) {
|
||||
e.addKey(key)
|
||||
e.AppendUint64(val)
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddUint32(key string, val uint32) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddUint16(key string, val uint16) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddUint8(key string, val uint8) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddUintptr(key string, val uintptr) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AddReflected(key string, val interface{}) error {
|
||||
e.addKey(key)
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
v = fmt.Sprintf("%v", val)
|
||||
}
|
||||
e.AppendString(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) OpenNamespace(_ string) {
|
||||
// no-op -
|
||||
// namespaces do not really visually apply to console logs
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendDuration(val time.Duration) {
|
||||
cur := e.buf.Len()
|
||||
e.EncodeDuration(val, e)
|
||||
if cur == e.buf.Len() {
|
||||
e.AppendInt64(int64(val))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendTime(val time.Time) {
|
||||
cur := e.buf.Len()
|
||||
e.EncodeTime(val, e)
|
||||
if cur == e.buf.Len() {
|
||||
e.AppendInt64(val.UnixNano())
|
||||
}
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendArray(arr zapcore.ArrayMarshaler) error {
|
||||
e.addElementSeparator()
|
||||
e.buf.AppendByte('[')
|
||||
err := arr.MarshalLogArray(e)
|
||||
e.buf.AppendByte(']')
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendObject(obj zapcore.ObjectMarshaler) error {
|
||||
e.addElementSeparator()
|
||||
e.buf.AppendByte('{')
|
||||
err := obj.MarshalLogObject(e)
|
||||
e.buf.AppendByte('}')
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) AppendReflected(val interface{}) error {
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
v = fmt.Sprintf("%v", val)
|
||||
}
|
||||
e.addElementSeparator()
|
||||
e.AppendString(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *coloredEncoder) addElementSeparator() {
|
||||
last := e.buf.Len() - 1
|
||||
if last < 0 {
|
||||
return
|
||||
}
|
||||
switch e.buf.Bytes()[last] {
|
||||
case '{', '[', ':', ',', ' ', '=':
|
||||
return
|
||||
default:
|
||||
e.buf.AppendByte(',')
|
||||
}
|
||||
}
|
112
encoder/console/config.go
Normal file
112
encoder/console/config.go
Normal file
@ -0,0 +1,112 @@
|
||||
package console
|
||||
|
||||
import "strings"
|
||||
|
||||
// Schema is the color schema for the default log parts/levels
|
||||
type ColorSchemaConfig struct {
|
||||
Timestamp string `logos-config:"timestamp"`
|
||||
Naming string `logos-config:"naming"`
|
||||
InfoLevel string `logos-config:"info_level"`
|
||||
WarnLevel string `logos-config:"warn_level"`
|
||||
ErrorLevel string `logos-config:"error_level"`
|
||||
FatalLevel string `logos-config:"fatal_level"`
|
||||
PanicLevel string `logos-config:"panic_level"`
|
||||
DPanicLevel string `logos-config:"dpanic_level"`
|
||||
DebugLevel string `logos-config:"debug_level"`
|
||||
}
|
||||
|
||||
func (c ColorSchemaConfig) Parse() ColorScheme {
|
||||
|
||||
scheme := ColorScheme{}
|
||||
|
||||
scheme.Timestamp = parseFieldColor(c.Timestamp)
|
||||
scheme.LogNaming = parseFieldColor(c.Naming)
|
||||
scheme.InfoLevel = parseFieldColor(c.InfoLevel)
|
||||
scheme.WarnLevel = parseFieldColor(c.WarnLevel)
|
||||
scheme.ErrorLevel = parseFieldColor(c.ErrorLevel)
|
||||
scheme.FatalLevel = parseFieldColor(c.FatalLevel)
|
||||
scheme.PanicLevel = parseFieldColor(c.PanicLevel)
|
||||
scheme.DPanicLevel = parseFieldColor(c.DPanicLevel)
|
||||
scheme.DebugLevel = parseFieldColor(c.DebugLevel)
|
||||
|
||||
scheme.fixColors()
|
||||
return scheme
|
||||
}
|
||||
|
||||
func parseFieldColor(colorString string) uint16 {
|
||||
|
||||
colors := strings.Split(colorString, ",")
|
||||
|
||||
if len(colors) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
color := colors[0]
|
||||
bold := uint16(0)
|
||||
bgColor := ""
|
||||
|
||||
if arr := strings.Split(color, "+"); len(arr) > 1 {
|
||||
color = strings.TrimSpace(arr[0])
|
||||
|
||||
if strings.TrimSpace(arr[1]) == "b" {
|
||||
bold = Bold
|
||||
}
|
||||
|
||||
}
|
||||
if len(colors) > 1 {
|
||||
bgColor = strings.TrimSpace(colors[1])
|
||||
}
|
||||
|
||||
return getColor(color) | bold | getBgColor(bgColor)
|
||||
|
||||
}
|
||||
|
||||
// Config is used to pass encoding parameters to New.
|
||||
type Config struct {
|
||||
|
||||
// color schema for messages
|
||||
ColorSchema *ColorSchemaConfig `logos-config:"color_scheme"`
|
||||
// no colors
|
||||
DisableColors bool `logos-config:"disable_colors"`
|
||||
// no check for TTY terminal
|
||||
ForceColors bool `logos-config:"force_colors"`
|
||||
// false -> name passed, true -> github.com/khorevaa/logos
|
||||
DisableNaming bool `logos-config:"disable_naming"`
|
||||
// no timestamp
|
||||
DisableTimestamp bool `logos-config:"disable_timestamp"`
|
||||
// console separator default space
|
||||
ConsoleSeparator string `logos-config:"console_separator"`
|
||||
// false -> time passed, true -> timestamp
|
||||
UseTimePassedAsTimestamp bool `logos-config:"pass_timestamp"`
|
||||
// false -> info, true -> INFO
|
||||
UseUppercaseLevel bool `logos-config:"uppercase_level"`
|
||||
|
||||
TimestampFormat string `logos-config:"timestamp_format"`
|
||||
|
||||
LineEnding string `logos-config:"line_ending"`
|
||||
}
|
||||
|
||||
// EncoderConfig is used to pass encoding parameters to New.
|
||||
type EncoderConfig struct {
|
||||
|
||||
// no colors
|
||||
DisableColors bool
|
||||
// no check for TTY terminal
|
||||
ForceColors bool
|
||||
// false -> time passed, true -> timestamp
|
||||
UseTimePassedAsTimestamp bool
|
||||
// false -> info, true -> INFO
|
||||
UseUppercaseLevel bool
|
||||
// false -> name passed, true -> github.com/khorevaa/logos
|
||||
DisableNaming bool
|
||||
// no timestamp
|
||||
DisableTimestamp bool
|
||||
// console separator default space
|
||||
ConsoleSeparator string
|
||||
// line end for log
|
||||
LineEnding string
|
||||
// time format string
|
||||
TimestampFormat string
|
||||
// color schema for messages
|
||||
Schema ColorScheme
|
||||
}
|
209
encoder/console/console.go
Normal file
209
encoder/console/console.go
Normal file
@ -0,0 +1,209 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/khorevaa/logos/appender"
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"time"
|
||||
)
|
||||
|
||||
var defaultTimestampFormat = "2006-01-02T15:04:05.000Z0700"
|
||||
var baseTimestamp = time.Now()
|
||||
|
||||
var defaultConfig = Config{
|
||||
ConsoleSeparator: "\t",
|
||||
TimestampFormat: defaultTimestampFormat,
|
||||
LineEnding: "\n",
|
||||
}
|
||||
|
||||
func init() {
|
||||
appender.RegisterEncoderType("console", func(cfg *common.Config) (zapcore.Encoder, error) {
|
||||
config := defaultConfig
|
||||
if cfg != nil {
|
||||
if err := cfg.Unpack(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
encoderConfig := EncoderConfig{
|
||||
DisableColors: config.DisableColors,
|
||||
ForceColors: config.ForceColors,
|
||||
DisableNaming: config.DisableNaming,
|
||||
DisableTimestamp: config.DisableTimestamp,
|
||||
ConsoleSeparator: config.ConsoleSeparator,
|
||||
LineEnding: config.LineEnding,
|
||||
TimestampFormat: config.TimestampFormat,
|
||||
UseTimePassedAsTimestamp: config.UseTimePassedAsTimestamp,
|
||||
UseUppercaseLevel: config.UseUppercaseLevel,
|
||||
}
|
||||
|
||||
encoderConfig.Schema = defaultScheme
|
||||
|
||||
if config.ColorSchema != nil {
|
||||
encoderConfig.Schema = config.ColorSchema.Parse()
|
||||
}
|
||||
en := NewEncoder(encoderConfig)
|
||||
return en, nil
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// NewEncoder initializes a a bol.com tailored Encoder
|
||||
func NewEncoder(cfg EncoderConfig) *Encoder {
|
||||
return &Encoder{
|
||||
buf: bufferpool.Get(),
|
||||
EncoderConfig: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Encoder is a bol.com tailored zap encoder for
|
||||
// writing human readable logs to the console
|
||||
type Encoder struct {
|
||||
buf *buffer.Buffer
|
||||
EncoderConfig
|
||||
}
|
||||
|
||||
// Clone implements the Clone method of the zapcore Encoder interface
|
||||
func (e *Encoder) Clone() zapcore.Encoder {
|
||||
clone := e.clone()
|
||||
_, _ = clone.buf.Write(e.buf.Bytes())
|
||||
return clone
|
||||
}
|
||||
|
||||
func (e *Encoder) clone() *Encoder {
|
||||
clone := get()
|
||||
clone.EncoderConfig = e.EncoderConfig
|
||||
clone.buf = bufferpool.Get()
|
||||
return clone
|
||||
}
|
||||
|
||||
// EncodeEntry implements the EncodeEntry method of the zapcore Encoder interface
|
||||
func (e Encoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
||||
|
||||
line := bufferpool.Get()
|
||||
|
||||
lvlColor := getLevelColor(ent.Level, e.Schema)
|
||||
|
||||
e.appendTimeInfo(line, ent)
|
||||
|
||||
line.AppendString(e.colorizeText(ent.Level.CapitalString(), lvlColor))
|
||||
e.addSeparatorIfNecessary(line)
|
||||
|
||||
//pp.Println(e.DisableNaming, ent.LoggerName)
|
||||
if !e.DisableNaming && len(ent.LoggerName) > 0 {
|
||||
|
||||
line.AppendString(e.colorizeText(ent.LoggerName, e.Schema.LogNaming))
|
||||
e.addSeparatorIfNecessary(line)
|
||||
}
|
||||
|
||||
if ent.Caller.Defined {
|
||||
|
||||
line.AppendString(e.colorizeText(ent.Caller.TrimmedPath(), e.Schema.Nil))
|
||||
e.addSeparatorIfNecessary(line)
|
||||
|
||||
}
|
||||
|
||||
line.AppendString(e.colorizeText(ent.Message, lvlColor))
|
||||
//e.addSeparatorIfNecessary(line)
|
||||
// Add any structured context.
|
||||
e.writeContext(lvlColor, line, fields)
|
||||
|
||||
// If there's no stacktrace key, honor that; this allows users to force
|
||||
// single-line output.
|
||||
if ent.Stack != "" {
|
||||
line.AppendByte('\n')
|
||||
line.AppendString(ent.Stack)
|
||||
}
|
||||
|
||||
line.AppendString(zapcore.DefaultLineEnding)
|
||||
|
||||
return line, nil
|
||||
|
||||
}
|
||||
|
||||
// appendTimeInfo appends the time related info
|
||||
// appends nothing on DisableTimestamp
|
||||
// appends [seconds] on UseTimePassedAsTimestamp
|
||||
// appends formatted TimestampFormat else
|
||||
func (e *Encoder) appendTimeInfo(buf *buffer.Buffer, entry zapcore.Entry) {
|
||||
if !e.DisableTimestamp {
|
||||
var timeInfo string
|
||||
if e.UseTimePassedAsTimestamp {
|
||||
timeInfo = fmt.Sprintf("[%04d]", int(entry.Time.Sub(baseTimestamp)/time.Second))
|
||||
} else {
|
||||
timestampFormat := e.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
timestampFormat = defaultTimestampFormat
|
||||
}
|
||||
timeInfo = entry.Time.Format(timestampFormat)
|
||||
}
|
||||
|
||||
buf.AppendString(e.colorizeText(timeInfo, e.Schema.Timestamp))
|
||||
e.addSeparatorIfNecessary(buf)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (e Encoder) writeContext(defColor uint16, out *buffer.Buffer, extra []zapcore.Field) {
|
||||
|
||||
if len(extra) == 0 {
|
||||
return
|
||||
}
|
||||
//e.buf.AppendByte('{')
|
||||
enc := getColoredEncoder(defColor, e.Schema)
|
||||
defer putColoredEncoder(enc)
|
||||
|
||||
addFields(enc, extra)
|
||||
if enc.buf.Len() > 0 {
|
||||
out.Write(enc.buf.Bytes())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (e Encoder) addSeparatorIfNecessary(line *buffer.Buffer) {
|
||||
if line.Len() > 0 {
|
||||
line.AppendString(e.ConsoleSeparator)
|
||||
}
|
||||
}
|
||||
|
||||
func (e Encoder) colorizeText(text string, color uint16) string {
|
||||
|
||||
if e.DisableColors {
|
||||
return text
|
||||
}
|
||||
|
||||
return colorizeText(text, color)
|
||||
|
||||
}
|
||||
|
||||
func (e *Encoder) addKey(key string) {
|
||||
|
||||
if !e.DisableColors {
|
||||
key = colorizeText(key, e.Schema.FieldName)
|
||||
}
|
||||
|
||||
e.buf.Write([]byte(e.ConsoleSeparator))
|
||||
e.buf.AppendString(key)
|
||||
e.buf.AppendByte('=')
|
||||
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeTime(val time.Time) string {
|
||||
|
||||
timestampFormat := e.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
timestampFormat = defaultTimestampFormat
|
||||
}
|
||||
timeInfo := val.Format(timestampFormat)
|
||||
|
||||
return timeInfo
|
||||
|
||||
}
|
||||
|
||||
func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
|
||||
for i := range fields {
|
||||
fields[i].AddTo(enc)
|
||||
}
|
||||
}
|
250
encoder/console/methods.go
Normal file
250
encoder/console/methods.go
Normal file
@ -0,0 +1,250 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// This file contains the methods that implement
|
||||
// the zapcore ConsoleEncoder interfaces
|
||||
|
||||
func (e *Encoder) AppendBool(val bool) {
|
||||
e.buf.AppendBool(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendByteString(bstr []byte) {
|
||||
e.buf.AppendString(string(bstr))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendComplex128(val complex128) {
|
||||
r, i := float64(real(val)), float64(imag(val))
|
||||
e.buf.AppendFloat(r, 64)
|
||||
e.buf.AppendFloat(i, 64)
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendComplex64(val complex64) {
|
||||
e.AppendComplex128(complex128(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendFloat64(val float64) {
|
||||
e.buf.AppendFloat(val, 64)
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendFloat32(val float32) {
|
||||
e.buf.AppendFloat(float64(val), 32)
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendInt(val int) {
|
||||
e.buf.AppendInt(int64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendInt64(val int64) {
|
||||
e.buf.AppendInt(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendInt32(val int32) {
|
||||
e.buf.AppendInt(int64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendInt16(val int16) {
|
||||
e.buf.AppendInt(int64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendInt8(val int8) {
|
||||
e.buf.AppendInt(int64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendString(str string) {
|
||||
e.buf.AppendString(str)
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendUint(val uint) {
|
||||
e.buf.AppendUint(uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendUint64(val uint64) {
|
||||
e.buf.AppendUint(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendUint32(val uint32) {
|
||||
e.buf.AppendUint(uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendUint16(val uint16) {
|
||||
e.buf.AppendUint(uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendUint8(val uint8) {
|
||||
e.buf.AppendUint(uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendUintptr(val uintptr) {
|
||||
e.AppendUint64(uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
|
||||
e.addKey(key)
|
||||
return marshaler.MarshalLogArray(e)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddObject(key string, obj zapcore.ObjectMarshaler) error {
|
||||
e.addKey(key)
|
||||
return obj.MarshalLogObject(e)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddBinary(key string, val []byte) {
|
||||
e.AddString(key, base64.StdEncoding.EncodeToString(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddByteString(key string, val []byte) {
|
||||
e.AddString(key, base64.StdEncoding.EncodeToString(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddBool(key string, val bool) {
|
||||
e.addKey(key)
|
||||
e.AppendBool(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddComplex128(key string, val complex128) {
|
||||
e.addKey(key)
|
||||
e.AppendComplex128(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddComplex64(key string, val complex64) {
|
||||
e.addKey(key)
|
||||
e.AppendComplex128(complex128(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddDuration(key string, val time.Duration) {
|
||||
e.addKey(key)
|
||||
e.AppendDuration(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddFloat64(key string, val float64) {
|
||||
e.addKey(key)
|
||||
e.AppendFloat64(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddFloat32(key string, val float32) {
|
||||
e.AddFloat64(key, float64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddInt(key string, val int) {
|
||||
e.AddInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddInt64(key string, val int64) {
|
||||
e.addKey(key)
|
||||
e.AppendInt64(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddInt32(key string, val int32) {
|
||||
e.AddInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddInt16(key string, val int16) {
|
||||
e.AddInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddInt8(key string, val int8) {
|
||||
e.AddInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddString(key string, val string) {
|
||||
e.addKey(key)
|
||||
e.AppendString(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddTime(key string, val time.Time) {
|
||||
e.addKey(key)
|
||||
e.AppendTime(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddUint(key string, val uint) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddUint64(key string, val uint64) {
|
||||
e.addKey(key)
|
||||
e.AppendUint64(val)
|
||||
}
|
||||
|
||||
func (e *Encoder) AddUint32(key string, val uint32) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddUint16(key string, val uint16) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddUint8(key string, val uint8) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddUintptr(key string, val uintptr) {
|
||||
e.AddUint64(key, uint64(val))
|
||||
}
|
||||
|
||||
func (e *Encoder) AddReflected(key string, val interface{}) error {
|
||||
e.addKey(key)
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
v = fmt.Sprintf("%v", val)
|
||||
}
|
||||
e.AppendString(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) OpenNamespace(_ string) {
|
||||
// no-op -
|
||||
// namespaces do not really visually apply to console logs
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendDuration(val time.Duration) {
|
||||
//cur := e.buf.Len()
|
||||
|
||||
//e.EncoderConfig.TimestampFormat.EncodeDuration(val, e)
|
||||
//if cur == e.buf.Len() {
|
||||
e.AppendInt64(int64(val))
|
||||
//}
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendTime(val time.Time) {
|
||||
|
||||
cur := e.buf.Len()
|
||||
encodeTime := e.encodeTime(val)
|
||||
|
||||
if len(encodeTime) > 0 {
|
||||
e.buf.AppendString(encodeTime)
|
||||
}
|
||||
|
||||
if cur == e.buf.Len() {
|
||||
e.AppendInt64(val.UnixNano())
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendArray(arr zapcore.ArrayMarshaler) error {
|
||||
e.buf.AppendByte('[')
|
||||
err := arr.MarshalLogArray(e)
|
||||
e.buf.AppendByte(']')
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendObject(obj zapcore.ObjectMarshaler) error {
|
||||
e.buf.AppendByte('{')
|
||||
err := obj.MarshalLogObject(e)
|
||||
e.buf.AppendByte('}')
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Encoder) AppendReflected(val interface{}) error {
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
v = fmt.Sprintf("%v", val)
|
||||
}
|
||||
e.AppendString(v)
|
||||
return nil
|
||||
}
|
23
encoder/console/pool.go
Normal file
23
encoder/console/pool.go
Normal file
@ -0,0 +1,23 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"go.uber.org/zap/buffer"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var pool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Encoder{}
|
||||
},
|
||||
}
|
||||
|
||||
func get() *Encoder {
|
||||
return pool.Get().(*Encoder)
|
||||
}
|
||||
|
||||
func put(enc *Encoder) {
|
||||
enc.buf = nil
|
||||
pool.Put(enc)
|
||||
}
|
||||
|
||||
var bufferpool = buffer.NewPool()
|
100
encoder/gelf/gelf.go
Normal file
100
encoder/gelf/gelf.go
Normal file
@ -0,0 +1,100 @@
|
||||
package gelf
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos/appender"
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Encoder struct {
|
||||
Fields []zapcore.Field
|
||||
zapcore.Encoder
|
||||
}
|
||||
|
||||
type KeyValuePair struct {
|
||||
Key string `logos-config:"key"`
|
||||
Value string `logos-config:"value"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
KeyValuePairs []KeyValuePair `logos-config:"key_value_pairs"`
|
||||
}
|
||||
|
||||
func (e *Encoder) EncodeEntry(enc zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
||||
newFields := make([]zap.Field, len(e.Fields)+len(fields))
|
||||
i := 0
|
||||
for ; i < len(e.Fields); i++ {
|
||||
newFields[i] = e.Fields[i]
|
||||
}
|
||||
for ; i < len(e.Fields)+len(fields); i++ {
|
||||
j := i - len(e.Fields)
|
||||
f := fields[j]
|
||||
f.Key = "_" + f.Key
|
||||
newFields[i] = f
|
||||
}
|
||||
return e.Encoder.EncodeEntry(enc, newFields)
|
||||
}
|
||||
|
||||
func LevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
|
||||
level := uint8(7)
|
||||
switch l {
|
||||
case zapcore.DebugLevel:
|
||||
level = 7
|
||||
case zapcore.InfoLevel:
|
||||
level = 6
|
||||
case zapcore.WarnLevel:
|
||||
level = 4
|
||||
case zapcore.ErrorLevel:
|
||||
level = 3
|
||||
case zapcore.DPanicLevel:
|
||||
level = 2
|
||||
case zapcore.PanicLevel:
|
||||
level = 1
|
||||
case zapcore.FatalLevel:
|
||||
level = 0
|
||||
}
|
||||
enc.AppendUint8(level)
|
||||
}
|
||||
|
||||
func init() {
|
||||
appender.RegisterEncoderType("gelf", func(config *common.Config) (zapcore.Encoder, error) {
|
||||
cfg := Config{}
|
||||
if err := config.Unpack(&cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encoderConfig := zapcore.EncoderConfig{
|
||||
TimeKey: "timestamp",
|
||||
LevelKey: "level",
|
||||
NameKey: "_logger",
|
||||
CallerKey: "_caller",
|
||||
MessageKey: "short_message",
|
||||
StacktraceKey: "full_message",
|
||||
LineEnding: "\n",
|
||||
EncodeLevel: LevelEncoder,
|
||||
EncodeTime: zapcore.EpochTimeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
}
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields := []zapcore.Field{
|
||||
zap.String("version", "1.1"),
|
||||
zap.String("host", hostname),
|
||||
}
|
||||
|
||||
for _, kv := range cfg.KeyValuePairs {
|
||||
fields = append(fields, zap.String("_"+kv.Key, kv.Value))
|
||||
}
|
||||
|
||||
return &Encoder{
|
||||
Fields: fields,
|
||||
Encoder: zapcore.NewJSONEncoder(encoderConfig),
|
||||
}, nil
|
||||
})
|
||||
}
|
133
encoder/gelf/gelf_test.go
Normal file
133
encoder/gelf/gelf_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
package gelf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type dummyEncoder struct {
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddBinary(key string, value []byte) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddBool(key string, value bool) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddByteString(key string, value []byte) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddComplex128(key string, value complex128) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddComplex64(key string, value complex64) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddDuration(key string, value time.Duration) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddFloat32(key string, value float32) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddFloat64(key string, value float64) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddInt(key string, value int) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddInt16(key string, value int16) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddInt32(key string, value int32) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddInt64(key string, value int64) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddInt8(key string, value int8) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddReflected(key string, value interface{}) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddString(key, value string) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddTime(key string, value time.Time) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddUint(key string, value uint) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddUint16(key string, value uint16) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddUint32(key string, value uint32) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddUint64(key string, value uint64) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddUint8(key string, value uint8) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) AddUintptr(key string, value uintptr) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) Clone() zapcore.Encoder {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (*dummyEncoder) EncodeEntry(_ zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
||||
b := &buffer.Buffer{}
|
||||
b.Write([]byte(fmt.Sprintf("%+v", fields)))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (*dummyEncoder) OpenNamespace(key string) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func TestEncoder_EncodeEntry(t *testing.T) {
|
||||
e := Encoder{
|
||||
Fields: []zapcore.Field{zap.String("version", "1.1")},
|
||||
Encoder: &dummyEncoder{},
|
||||
}
|
||||
b, _ := e.EncodeEntry(zapcore.Entry{}, []zapcore.Field{zap.String("seq_id", "123")})
|
||||
assert.Equal(t, `[{Key:version Type:15 Integer:0 String:1.1 Interface:<nil>} {Key:_seq_id Type:15 Integer:0 String:123 Interface:<nil>}]`, b.String())
|
||||
}
|
52
encoder/json/json.go
Normal file
52
encoder/json/json.go
Normal file
@ -0,0 +1,52 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos/appender"
|
||||
ec "github.com/khorevaa/logos/encoder/common"
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var defaultConfig = ec.JsonEncoderConfig{
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: "\n",
|
||||
TimeEncoder: "ISO8601",
|
||||
}
|
||||
|
||||
func init() {
|
||||
appender.RegisterEncoderType("json", func(cfg *common.Config) (zapcore.Encoder, error) {
|
||||
config := defaultConfig
|
||||
if cfg != nil {
|
||||
if err := cfg.Unpack(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
encoderConfig := zapcore.EncoderConfig{
|
||||
TimeKey: config.TimeKey,
|
||||
LevelKey: config.LevelKey,
|
||||
NameKey: config.NameKey,
|
||||
CallerKey: config.CallerKey,
|
||||
MessageKey: config.MessageKey,
|
||||
StacktraceKey: config.StacktraceKey,
|
||||
LineEnding: config.LineEnding,
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
EncodeTime: zapcore.EpochTimeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
}
|
||||
|
||||
te, err := ec.GetTimeEncoder(config.TimeEncoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoderConfig.EncodeTime = te
|
||||
|
||||
return zapcore.NewJSONEncoder(encoderConfig), nil
|
||||
})
|
||||
}
|
15
example_config.yaml
Normal file
15
example_config.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
appenders:
|
||||
console:
|
||||
- name: CONSOLE
|
||||
target: stdout
|
||||
encoder:
|
||||
console:
|
||||
loggers:
|
||||
root:
|
||||
level: info
|
||||
appender_refs:
|
||||
- CONSOLE
|
||||
logger:
|
||||
- name: stdlog
|
||||
level: info
|
||||
add_caller: true
|
16
example_test.go
Normal file
16
example_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package logos_test
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
|
||||
log := logos.New("github.com/v8platform/test")
|
||||
log.Info("Error")
|
||||
log.Sync()
|
||||
|
||||
// Output:
|
||||
// 2021-01-28T22:10:37.059+0300 INFO github.com/v8platform/test Error {"url": "url", "attempt": 3, "backoff": 1}
|
||||
|
||||
}
|
539
field.go
Normal file
539
field.go
Normal file
@ -0,0 +1,539 @@
|
||||
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package logos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Field is an alias for Field. Aliasing this type dramatically
|
||||
// improves the navigability of this package's API documentation.
|
||||
type Field = zapcore.Field
|
||||
|
||||
var (
|
||||
_minTimeInt64 = time.Unix(0, math.MinInt64)
|
||||
_maxTimeInt64 = time.Unix(0, math.MaxInt64)
|
||||
)
|
||||
|
||||
// Skip constructs a no-op field, which is often useful when handling invalid
|
||||
// inputs in other Field constructors.
|
||||
func Skip() Field {
|
||||
return Field{Type: zapcore.SkipType}
|
||||
}
|
||||
|
||||
// nilField returns a field which will marshal explicitly as nil. See motivation
|
||||
// in https://github.com/uber-go/zap/issues/753 . If we ever make breaking
|
||||
// changes and add zapcore.NilType and zapcore.ObjectEncoder.AddNil, the
|
||||
// implementation here should be changed to reflect that.
|
||||
func nilField(key string) Field { return Reflect(key, nil) }
|
||||
|
||||
// Binary constructs a field that carries an opaque binary blob.
|
||||
//
|
||||
// Binary data is serialized in an encoding-appropriate format. For example,
|
||||
// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text,
|
||||
// use ByteString.
|
||||
func Binary(key string, val []byte) Field {
|
||||
return Field{Key: key, Type: zapcore.BinaryType, Interface: val}
|
||||
}
|
||||
|
||||
// Bool constructs a field that carries a bool.
|
||||
func Bool(key string, val bool) Field {
|
||||
var ival int64
|
||||
if val {
|
||||
ival = 1
|
||||
}
|
||||
return Field{Key: key, Type: zapcore.BoolType, Integer: ival}
|
||||
}
|
||||
|
||||
// Boolp constructs a field that carries a *bool. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Boolp(key string, val *bool) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Bool(key, *val)
|
||||
}
|
||||
|
||||
// ByteString constructs a field that carries UTF-8 encoded text as a []byte.
|
||||
// To log opaque binary blobs (which aren't necessarily valid UTF-8), use
|
||||
// Binary.
|
||||
func ByteString(key string, val []byte) Field {
|
||||
return Field{Key: key, Type: zapcore.ByteStringType, Interface: val}
|
||||
}
|
||||
|
||||
// Complex128 constructs a field that carries a complex number. Unlike most
|
||||
// numeric fields, this costs an allocation (to convert the complex128 to
|
||||
// interface{}).
|
||||
func Complex128(key string, val complex128) Field {
|
||||
return Field{Key: key, Type: zapcore.Complex128Type, Interface: val}
|
||||
}
|
||||
|
||||
// Complex128p constructs a field that carries a *complex128. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Complex128p(key string, val *complex128) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Complex128(key, *val)
|
||||
}
|
||||
|
||||
// Complex64 constructs a field that carries a complex number. Unlike most
|
||||
// numeric fields, this costs an allocation (to convert the complex64 to
|
||||
// interface{}).
|
||||
func Complex64(key string, val complex64) Field {
|
||||
return Field{Key: key, Type: zapcore.Complex64Type, Interface: val}
|
||||
}
|
||||
|
||||
// Complex64p constructs a field that carries a *complex64. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Complex64p(key string, val *complex64) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Complex64(key, *val)
|
||||
}
|
||||
|
||||
// Float64 constructs a field that carries a float64. The way the
|
||||
// floating-point value is represented is encoder-dependent, so marshaling is
|
||||
// necessarily lazy.
|
||||
func Float64(key string, val float64) Field {
|
||||
return Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))}
|
||||
}
|
||||
|
||||
// Float64p constructs a field that carries a *float64. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Float64p(key string, val *float64) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Float64(key, *val)
|
||||
}
|
||||
|
||||
// Float32 constructs a field that carries a float32. The way the
|
||||
// floating-point value is represented is encoder-dependent, so marshaling is
|
||||
// necessarily lazy.
|
||||
func Float32(key string, val float32) Field {
|
||||
return Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))}
|
||||
}
|
||||
|
||||
// Float32p constructs a field that carries a *float32. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Float32p(key string, val *float32) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Float32(key, *val)
|
||||
}
|
||||
|
||||
// Int constructs a field with the given key and value.
|
||||
func Int(key string, val int) Field {
|
||||
return Int64(key, int64(val))
|
||||
}
|
||||
|
||||
// Intp constructs a field that carries a *int. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Intp(key string, val *int) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int(key, *val)
|
||||
}
|
||||
|
||||
// Int64 constructs a field with the given key and value.
|
||||
func Int64(key string, val int64) Field {
|
||||
return Field{Key: key, Type: zapcore.Int64Type, Integer: val}
|
||||
}
|
||||
|
||||
// Int64p constructs a field that carries a *int64. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Int64p(key string, val *int64) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int64(key, *val)
|
||||
}
|
||||
|
||||
// Int32 constructs a field with the given key and value.
|
||||
func Int32(key string, val int32) Field {
|
||||
return Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Int32p constructs a field that carries a *int32. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Int32p(key string, val *int32) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int32(key, *val)
|
||||
}
|
||||
|
||||
// Int16 constructs a field with the given key and value.
|
||||
func Int16(key string, val int16) Field {
|
||||
return Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Int16p constructs a field that carries a *int16. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Int16p(key string, val *int16) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int16(key, *val)
|
||||
}
|
||||
|
||||
// Int8 constructs a field with the given key and value.
|
||||
func Int8(key string, val int8) Field {
|
||||
return Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Int8p constructs a field that carries a *int8. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Int8p(key string, val *int8) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int8(key, *val)
|
||||
}
|
||||
|
||||
// String constructs a field with the given key and value.
|
||||
func String(key string, val string) Field {
|
||||
return Field{Key: key, Type: zapcore.StringType, String: val}
|
||||
}
|
||||
|
||||
// Stringp constructs a field that carries a *string. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Stringp(key string, val *string) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return String(key, *val)
|
||||
}
|
||||
|
||||
// Uint constructs a field with the given key and value.
|
||||
func Uint(key string, val uint) Field {
|
||||
return Uint64(key, uint64(val))
|
||||
}
|
||||
|
||||
// Uintp constructs a field that carries a *uint. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uintp(key string, val *uint) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint(key, *val)
|
||||
}
|
||||
|
||||
// Uint64 constructs a field with the given key and value.
|
||||
func Uint64(key string, val uint64) Field {
|
||||
return Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint64p constructs a field that carries a *uint64. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uint64p(key string, val *uint64) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint64(key, *val)
|
||||
}
|
||||
|
||||
// Uint32 constructs a field with the given key and value.
|
||||
func Uint32(key string, val uint32) Field {
|
||||
return Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint32p constructs a field that carries a *uint32. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uint32p(key string, val *uint32) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint32(key, *val)
|
||||
}
|
||||
|
||||
// Uint16 constructs a field with the given key and value.
|
||||
func Uint16(key string, val uint16) Field {
|
||||
return Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint16p constructs a field that carries a *uint16. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uint16p(key string, val *uint16) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint16(key, *val)
|
||||
}
|
||||
|
||||
// Uint8 constructs a field with the given key and value.
|
||||
func Uint8(key string, val uint8) Field {
|
||||
return Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint8p constructs a field that carries a *uint8. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uint8p(key string, val *uint8) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint8(key, *val)
|
||||
}
|
||||
|
||||
// Uintptr constructs a field with the given key and value.
|
||||
func Uintptr(key string, val uintptr) Field {
|
||||
return Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uintptrp constructs a field that carries a *uintptr. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uintptrp(key string, val *uintptr) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uintptr(key, *val)
|
||||
}
|
||||
|
||||
// Reflect constructs a field with the given key and an arbitrary object. It uses
|
||||
// an encoding-appropriate, reflection-based function to lazily serialize nearly
|
||||
// any object into the logging context, but it's relatively slow and
|
||||
// allocation-heavy. Outside tests, Any is always a better choice.
|
||||
//
|
||||
// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect
|
||||
// includes the error message in the final log output.
|
||||
func Reflect(key string, val interface{}) Field {
|
||||
return Field{Key: key, Type: zapcore.ReflectType, Interface: val}
|
||||
}
|
||||
|
||||
// Namespace creates a named, isolated scope within the logger's context. All
|
||||
// subsequent fields will be added to the new namespace.
|
||||
//
|
||||
// This helps prevent key collisions when injecting loggers into sub-components
|
||||
// or third-party libraries.
|
||||
func Namespace(key string) Field {
|
||||
return Field{Key: key, Type: zapcore.NamespaceType}
|
||||
}
|
||||
|
||||
// Stringer constructs a field with the given key and the output of the value's
|
||||
// String method. The Stringer's String method is called lazily.
|
||||
func Stringer(key string, val fmt.Stringer) Field {
|
||||
return Field{Key: key, Type: zapcore.StringerType, Interface: val}
|
||||
}
|
||||
|
||||
// Time constructs a Field with the given key and value. The encoder
|
||||
// controls how the time is serialized.
|
||||
func Time(key string, val time.Time) Field {
|
||||
if val.Before(_minTimeInt64) || val.After(_maxTimeInt64) {
|
||||
return Field{Key: key, Type: zapcore.TimeFullType, Interface: val}
|
||||
}
|
||||
return Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()}
|
||||
}
|
||||
|
||||
// Timep constructs a field that carries a *time.Time. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Timep(key string, val *time.Time) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Time(key, *val)
|
||||
}
|
||||
|
||||
// Stack constructs a field that stores a stacktrace of the current goroutine
|
||||
// under provided key. Keep in mind that taking a stacktrace is eager and
|
||||
// expensive (relatively speaking); this function both makes an allocation and
|
||||
// takes about two microseconds.
|
||||
func Stack(key string) Field {
|
||||
return zap.StackSkip(key, 1) // skip Stack
|
||||
}
|
||||
|
||||
// StackSkip constructs a field similarly to Stack, but also skips the given
|
||||
// number of frames from the top of the stacktrace.
|
||||
func StackSkip(key string, skip int) Field {
|
||||
// Returning the stacktrace as a string costs an allocation, but saves us
|
||||
// from expanding the zapcore.Field union struct to include a byte slice. Since
|
||||
// taking a stacktrace is already so expensive (~10us), the extra allocation
|
||||
// is okay.
|
||||
return zap.StackSkip(key, skip) // skip StackSkip
|
||||
}
|
||||
|
||||
// Duration constructs a field with the given key and value. The encoder
|
||||
// controls how the duration is serialized.
|
||||
func Duration(key string, val time.Duration) Field {
|
||||
return Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Durationp constructs a field that carries a *time.Duration. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Durationp(key string, val *time.Duration) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Duration(key, *val)
|
||||
}
|
||||
|
||||
// Object constructs a field with the given key and ObjectMarshaler. It
|
||||
// provides a flexible, but still type-safe and efficient, way to add map- or
|
||||
// struct-like user-defined types to the logging context. The struct's
|
||||
// MarshalLogObject method is called lazily.
|
||||
func Object(key string, val zapcore.ObjectMarshaler) Field {
|
||||
return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val}
|
||||
}
|
||||
|
||||
// Any takes a key and an arbitrary value and chooses the best way to represent
|
||||
// them as a field, falling back to a reflection-based approach only if
|
||||
// necessary.
|
||||
//
|
||||
// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between
|
||||
// them. To minimize surprises, []byte values are treated as binary blobs, byte
|
||||
// values are treated as uint8, and runes are always treated as integers.
|
||||
func Any(key string, value interface{}) Field {
|
||||
switch val := value.(type) {
|
||||
case zapcore.ObjectMarshaler:
|
||||
return Object(key, val)
|
||||
case zapcore.ArrayMarshaler:
|
||||
return zap.Array(key, val)
|
||||
case bool:
|
||||
return Bool(key, val)
|
||||
case *bool:
|
||||
return Boolp(key, val)
|
||||
case []bool:
|
||||
return zap.Bools(key, val)
|
||||
case complex128:
|
||||
return Complex128(key, val)
|
||||
case *complex128:
|
||||
return Complex128p(key, val)
|
||||
case []complex128:
|
||||
return zap.Complex128s(key, val)
|
||||
case complex64:
|
||||
return Complex64(key, val)
|
||||
case *complex64:
|
||||
return Complex64p(key, val)
|
||||
case []complex64:
|
||||
return zap.Complex64s(key, val)
|
||||
case float64:
|
||||
return Float64(key, val)
|
||||
case *float64:
|
||||
return Float64p(key, val)
|
||||
case []float64:
|
||||
return zap.Float64s(key, val)
|
||||
case float32:
|
||||
return Float32(key, val)
|
||||
case *float32:
|
||||
return Float32p(key, val)
|
||||
case []float32:
|
||||
return zap.Float32s(key, val)
|
||||
case int:
|
||||
return Int(key, val)
|
||||
case *int:
|
||||
return Intp(key, val)
|
||||
case []int:
|
||||
return zap.Ints(key, val)
|
||||
case int64:
|
||||
return Int64(key, val)
|
||||
case *int64:
|
||||
return Int64p(key, val)
|
||||
case []int64:
|
||||
return zap.Int64s(key, val)
|
||||
case int32:
|
||||
return Int32(key, val)
|
||||
case *int32:
|
||||
return Int32p(key, val)
|
||||
case []int32:
|
||||
return zap.Int32s(key, val)
|
||||
case int16:
|
||||
return Int16(key, val)
|
||||
case *int16:
|
||||
return Int16p(key, val)
|
||||
case []int16:
|
||||
return zap.Int16s(key, val)
|
||||
case int8:
|
||||
return Int8(key, val)
|
||||
case *int8:
|
||||
return Int8p(key, val)
|
||||
case []int8:
|
||||
return zap.Int8s(key, val)
|
||||
case string:
|
||||
return String(key, val)
|
||||
case *string:
|
||||
return Stringp(key, val)
|
||||
case []string:
|
||||
return zap.Strings(key, val)
|
||||
case uint:
|
||||
return Uint(key, val)
|
||||
case *uint:
|
||||
return Uintp(key, val)
|
||||
case []uint:
|
||||
return zap.Uints(key, val)
|
||||
case uint64:
|
||||
return Uint64(key, val)
|
||||
case *uint64:
|
||||
return Uint64p(key, val)
|
||||
case []uint64:
|
||||
return zap.Uint64s(key, val)
|
||||
case uint32:
|
||||
return Uint32(key, val)
|
||||
case *uint32:
|
||||
return Uint32p(key, val)
|
||||
case []uint32:
|
||||
return zap.Uint32s(key, val)
|
||||
case uint16:
|
||||
return Uint16(key, val)
|
||||
case *uint16:
|
||||
return Uint16p(key, val)
|
||||
case []uint16:
|
||||
return zap.Uint16s(key, val)
|
||||
case uint8:
|
||||
return Uint8(key, val)
|
||||
case *uint8:
|
||||
return Uint8p(key, val)
|
||||
case []byte:
|
||||
return Binary(key, val)
|
||||
case uintptr:
|
||||
return Uintptr(key, val)
|
||||
case *uintptr:
|
||||
return Uintptrp(key, val)
|
||||
case []uintptr:
|
||||
return zap.Uintptrs(key, val)
|
||||
case time.Time:
|
||||
return Time(key, val)
|
||||
case *time.Time:
|
||||
return Timep(key, val)
|
||||
case []time.Time:
|
||||
return zap.Times(key, val)
|
||||
case time.Duration:
|
||||
return Duration(key, val)
|
||||
case *time.Duration:
|
||||
return Durationp(key, val)
|
||||
case []time.Duration:
|
||||
return zap.Durations(key, val)
|
||||
case error:
|
||||
return zap.NamedError(key, val)
|
||||
case []error:
|
||||
return zap.Errors(key, val)
|
||||
case fmt.Stringer:
|
||||
return Stringer(key, val)
|
||||
default:
|
||||
return Reflect(key, val)
|
||||
}
|
||||
}
|
12
go.mod
Normal file
12
go.mod
Normal file
@ -0,0 +1,12 @@
|
||||
module github.com/khorevaa/logos
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/elastic/go-ucfg v0.8.3
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
github.com/stretchr/testify v1.6.1
|
||||
go.uber.org/zap v1.16.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
|
||||
)
|
69
go.sum
Normal file
69
go.sum
Normal file
@ -0,0 +1,69 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elastic/go-ucfg v0.8.3 h1:leywnFjzr2QneZZWhE6uWd+QN/UpP0sdJRHYyuFvkeo=
|
||||
github.com/elastic/go-ucfg v0.8.3/go.mod h1:iaiY0NBIYeasNgycLyTvhJftQlQEUO2hpF+FX0JKxzo=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
260
internal/common/config.go
Normal file
260
internal/common/config.go
Normal file
@ -0,0 +1,260 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/elastic/go-ucfg"
|
||||
"github.com/elastic/go-ucfg/yaml"
|
||||
)
|
||||
|
||||
// Config object to store hierarchical configurations into.
|
||||
// See https://godoc.org/github.com/elastic/go-ucfg#Config
|
||||
type Config ucfg.Config
|
||||
|
||||
// ConfigNamespace storing at most one configuration section by name and sub-section.
|
||||
type ConfigNamespace struct {
|
||||
name string
|
||||
config *Config
|
||||
}
|
||||
|
||||
var configOpts = []ucfg.Option{
|
||||
ucfg.PathSep("."),
|
||||
ucfg.ResolveEnv,
|
||||
ucfg.VarExp,
|
||||
ucfg.StructTag("logos-config"),
|
||||
ucfg.ValidatorTag("logos-validate"),
|
||||
ucfg.ReplaceValues,
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
return fromConfig(ucfg.New())
|
||||
}
|
||||
|
||||
// NewConfigFrom creates a new Config object from the given input.
|
||||
// From can be any kind of structured data (struct, map, array, slice).
|
||||
//
|
||||
// If from is a string, the contents is treated like raw YAML input. The string
|
||||
// will be parsed and a structure config object is build from the parsed
|
||||
// result.
|
||||
func NewConfigFrom(from interface{}) (*Config, error) {
|
||||
if str, ok := from.(string); ok {
|
||||
c, err := yaml.NewConfig([]byte(str), configOpts...)
|
||||
return fromConfig(c), err
|
||||
}
|
||||
|
||||
c, err := ucfg.NewFrom(from, configOpts...)
|
||||
return fromConfig(c), err
|
||||
}
|
||||
|
||||
// MustNewConfigFrom creates a new Config object from the given input.
|
||||
// From can be any kind of structured data (struct, map, array, slice).
|
||||
//
|
||||
// If from is a string, the contents is treated like raw YAML input. The string
|
||||
// will be parsed and a structure config object is build from the parsed
|
||||
// result.
|
||||
//
|
||||
// MustNewConfigFrom panics if an error occurs.
|
||||
func MustNewConfigFrom(from interface{}) *Config {
|
||||
cfg, err := NewConfigFrom(from)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func MergeConfigs(cfgs ...*Config) (*Config, error) {
|
||||
config := NewConfig()
|
||||
for _, c := range cfgs {
|
||||
if err := config.Merge(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func NewConfigWithYAML(in []byte, source string) (*Config, error) {
|
||||
opts := append(
|
||||
[]ucfg.Option{
|
||||
ucfg.MetaData(ucfg.Meta{Source: source}),
|
||||
},
|
||||
configOpts...,
|
||||
)
|
||||
c, err := yaml.NewConfig(in, opts...)
|
||||
return fromConfig(c), err
|
||||
}
|
||||
|
||||
// OverwriteConfigOpts allow to change the globally set config option
|
||||
func OverwriteConfigOpts(options []ucfg.Option) {
|
||||
configOpts = options
|
||||
}
|
||||
|
||||
func LoadFile(path string) (*Config, [md5.Size]byte, error) {
|
||||
bs, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, [md5.Size]byte{}, err
|
||||
}
|
||||
hash := md5.Sum(bs)
|
||||
c, err := yaml.NewConfig(bs, configOpts...)
|
||||
if err != nil {
|
||||
return nil, hash, err
|
||||
}
|
||||
cfg := fromConfig(c)
|
||||
return cfg, hash, err
|
||||
}
|
||||
|
||||
func (c *Config) Merge(from interface{}) error {
|
||||
return c.access().Merge(from, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) Unpack(to interface{}) error {
|
||||
return c.access().Unpack(to, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) Path() string {
|
||||
return c.access().Path(".")
|
||||
}
|
||||
|
||||
func (c *Config) PathOf(field string) string {
|
||||
return c.access().PathOf(field, ".")
|
||||
}
|
||||
|
||||
func (c *Config) HasField(name string) bool {
|
||||
return c.access().HasField(name)
|
||||
}
|
||||
|
||||
func (c *Config) CountField(name string) (int, error) {
|
||||
return c.access().CountField(name)
|
||||
}
|
||||
|
||||
func (c *Config) Name() (string, error) {
|
||||
return c.access().String("name", -1)
|
||||
}
|
||||
|
||||
func (c *Config) MustName(fallback ...string) string {
|
||||
name, err := c.Name()
|
||||
if err != nil {
|
||||
if len(fallback) > 0 {
|
||||
return fallback[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (c *Config) Bool(name string, idx int) (bool, error) {
|
||||
return c.access().Bool(name, idx, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) String(name string, idx int) (string, error) {
|
||||
return c.access().String(name, idx, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) Int(name string, idx int) (int64, error) {
|
||||
return c.access().Int(name, idx, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) Float(name string, idx int) (float64, error) {
|
||||
return c.access().Float(name, idx, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) Child(name string, idx int) (*Config, error) {
|
||||
sub, err := c.access().Child(name, idx, configOpts...)
|
||||
return fromConfig(sub), err
|
||||
}
|
||||
|
||||
func (c *Config) SetBool(name string, idx int, value bool) error {
|
||||
return c.access().SetBool(name, idx, value, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) SetInt(name string, idx int, value int64) error {
|
||||
return c.access().SetInt(name, idx, value, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) SetFloat(name string, idx int, value float64) error {
|
||||
return c.access().SetFloat(name, idx, value, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) SetString(name string, idx int, value string) error {
|
||||
return c.access().SetString(name, idx, value, configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) SetChild(name string, idx int, value *Config) error {
|
||||
return c.access().SetChild(name, idx, value.access(), configOpts...)
|
||||
}
|
||||
|
||||
func (c *Config) IsDict() bool {
|
||||
return c.access().IsDict()
|
||||
}
|
||||
|
||||
func (c *Config) IsArray() bool {
|
||||
return c.access().IsArray()
|
||||
}
|
||||
|
||||
func fromConfig(in *ucfg.Config) *Config {
|
||||
return (*Config)(in)
|
||||
}
|
||||
|
||||
func (c *Config) access() *ucfg.Config {
|
||||
return (*ucfg.Config)(c)
|
||||
}
|
||||
|
||||
func (c *Config) GetFields() []string {
|
||||
return c.access().GetFields()
|
||||
}
|
||||
|
||||
// Unpack unpacks a configuration with at most one sub object. An sub object is
|
||||
// ignored if it is disabled by setting `enabled: false`. If the configuration
|
||||
// passed contains multiple active sub objects, Unpack will return an error.
|
||||
func (ns *ConfigNamespace) Unpack(cfg *Config) error {
|
||||
fields := cfg.GetFields()
|
||||
if len(fields) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
found bool
|
||||
)
|
||||
|
||||
for _, name := range fields {
|
||||
var sub *Config
|
||||
|
||||
sub, err = cfg.Child(name, -1)
|
||||
if err != nil {
|
||||
// element is no configuration object -> continue so a namespace
|
||||
// Config unpacked as a namespace can have other configuration
|
||||
// values as well
|
||||
continue
|
||||
}
|
||||
|
||||
if ns.name != "" {
|
||||
return errors.New("more than one namespace configured")
|
||||
}
|
||||
|
||||
ns.name = name
|
||||
ns.config = sub
|
||||
found = true
|
||||
}
|
||||
|
||||
if !found {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name returns the configuration sections it's name if a section has been set.
|
||||
func (ns *ConfigNamespace) Name() string {
|
||||
return ns.name
|
||||
}
|
||||
|
||||
// Config return the sub-configuration section if a section has been set.
|
||||
func (ns *ConfigNamespace) Config() *Config {
|
||||
return ns.config
|
||||
}
|
||||
|
||||
// IsSet returns true if a sub-configuration section has been set.
|
||||
func (ns *ConfigNamespace) IsSet() bool {
|
||||
return ns.config != nil
|
||||
}
|
52
internal/common/floatset.go
Normal file
52
internal/common/floatset.go
Normal file
@ -0,0 +1,52 @@
|
||||
package common
|
||||
|
||||
import "strconv"
|
||||
|
||||
type FloatSet map[float64]struct{}
|
||||
|
||||
func MakeFloatSetFromStrings(strings ...string) (FloatSet, error) {
|
||||
if len(strings) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
set := FloatSet{}
|
||||
for _, str := range strings {
|
||||
f, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set[f] = struct{}{}
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func MakeFloatSet(floats ...float64) FloatSet {
|
||||
if len(floats) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
set := FloatSet{}
|
||||
for _, f := range floats {
|
||||
set[f] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func (set FloatSet) Add(f float64) {
|
||||
set[f] = struct{}{}
|
||||
}
|
||||
|
||||
func (set FloatSet) Del(f float64) {
|
||||
delete(set, f)
|
||||
}
|
||||
|
||||
func (set FloatSet) Count() int {
|
||||
return len(set)
|
||||
}
|
||||
|
||||
func (set FloatSet) Has(f float64) (exists bool) {
|
||||
if set != nil {
|
||||
_, exists = set[f]
|
||||
}
|
||||
return
|
||||
}
|
52
internal/common/intset.go
Normal file
52
internal/common/intset.go
Normal file
@ -0,0 +1,52 @@
|
||||
package common
|
||||
|
||||
import "strconv"
|
||||
|
||||
type IntSet map[int]struct{}
|
||||
|
||||
func MakeIntSetFromStrings(strings ...string) (IntSet, error) {
|
||||
if len(strings) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
set := IntSet{}
|
||||
for _, str := range strings {
|
||||
i, err := strconv.ParseInt(str, 0, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set[int(i)] = struct{}{}
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func MakeIntSet(ints ...int) IntSet {
|
||||
if len(ints) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
set := IntSet{}
|
||||
for _, i := range ints {
|
||||
set[i] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func (set IntSet) Add(i int) {
|
||||
set[i] = struct{}{}
|
||||
}
|
||||
|
||||
func (set IntSet) Del(i int) {
|
||||
delete(set, i)
|
||||
}
|
||||
|
||||
func (set IntSet) Count() int {
|
||||
return len(set)
|
||||
}
|
||||
|
||||
func (set IntSet) Has(s int) (exists bool) {
|
||||
if set != nil {
|
||||
_, exists = set[s]
|
||||
}
|
||||
return
|
||||
}
|
45
internal/common/stringset.go
Normal file
45
internal/common/stringset.go
Normal file
@ -0,0 +1,45 @@
|
||||
package common
|
||||
|
||||
type StringSet map[string]struct{}
|
||||
|
||||
func MakeStringSet(strings ...string) StringSet {
|
||||
if len(strings) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
set := StringSet{}
|
||||
for _, str := range strings {
|
||||
set[str] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func (set StringSet) Add(s string) {
|
||||
set[s] = struct{}{}
|
||||
}
|
||||
|
||||
func (set StringSet) Del(s string) {
|
||||
delete(set, s)
|
||||
}
|
||||
|
||||
func (set StringSet) Count() int {
|
||||
return len(set)
|
||||
}
|
||||
|
||||
func (set StringSet) Has(s string) (exists bool) {
|
||||
if set != nil {
|
||||
_, exists = set[s]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (set StringSet) ToSlice() []string {
|
||||
if set == nil {
|
||||
return nil
|
||||
}
|
||||
ss := make([]string, 0)
|
||||
for s := range set {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
return ss
|
||||
}
|
52
internal/common/uintset.go
Normal file
52
internal/common/uintset.go
Normal file
@ -0,0 +1,52 @@
|
||||
package common
|
||||
|
||||
import "strconv"
|
||||
|
||||
type UintSet map[uint]struct{}
|
||||
|
||||
func MakeUintSetFromStrings(strings ...string) (UintSet, error) {
|
||||
if len(strings) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
set := UintSet{}
|
||||
for _, str := range strings {
|
||||
i, err := strconv.ParseUint(str, 0, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set[uint(i)] = struct{}{}
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func MakeUintSet(uints ...uint) UintSet {
|
||||
if len(uints) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
set := UintSet{}
|
||||
for _, i := range uints {
|
||||
set[i] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func (set UintSet) Add(i uint) {
|
||||
set[i] = struct{}{}
|
||||
}
|
||||
|
||||
func (set UintSet) Del(i uint) {
|
||||
delete(set, i)
|
||||
}
|
||||
|
||||
func (set UintSet) Count() int {
|
||||
return len(set)
|
||||
}
|
||||
|
||||
func (set UintSet) Has(s uint) (exists bool) {
|
||||
if set != nil {
|
||||
_, exists = set[s]
|
||||
}
|
||||
return
|
||||
}
|
50
internal/common/validation.go
Normal file
50
internal/common/validation.go
Normal file
@ -0,0 +1,50 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/elastic/go-ucfg"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := ucfg.RegisterValidator("logos.oneof", func(v interface{}, params string) error {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
val := reflect.ValueOf(v)
|
||||
switch val.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
intSet, err := MakeIntSetFromStrings(strings.Split(params, " ")...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if intSet.Has(int(val.Int())) {
|
||||
return nil
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
uintSet, err := MakeUintSetFromStrings(strings.Split(params, " ")...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if uintSet.Has(uint(val.Uint())) {
|
||||
return nil
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
floatSet, err := MakeFloatSetFromStrings(strings.Split(params, " ")...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if floatSet.Has(val.Float()) {
|
||||
return nil
|
||||
}
|
||||
case reflect.String:
|
||||
if MakeStringSet(strings.Split(params, " ")...).Has(val.String()) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("requires value one of %q", params)
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
29
level.go
Normal file
29
level.go
Normal file
@ -0,0 +1,29 @@
|
||||
package logos
|
||||
|
||||
import (
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// OffLevel
|
||||
OffLevel = zapcore.Level(-2)
|
||||
// DebugLevel logs are typically voluminous, and are usually disabled in
|
||||
// production.
|
||||
DebugLevel = zapcore.DebugLevel
|
||||
// InfoLevel is the default logging priority.
|
||||
InfoLevel = zapcore.InfoLevel
|
||||
// WarnLevel logs are more important than Info, but don't need individual
|
||||
// human review.
|
||||
WarnLevel = zapcore.WarnLevel
|
||||
// ErrorLevel logs are high-priority. If an application is running smoothly,
|
||||
// it shouldn't generate any error-level logs.
|
||||
ErrorLevel = zapcore.ErrorLevel
|
||||
// DPanicLevel logs are particularly important errors. In development the
|
||||
// logger panics after writing the message.
|
||||
DPanicLevel = zapcore.DPanicLevel
|
||||
// PanicLevel logs a message, then panics.
|
||||
PanicLevel = zapcore.PanicLevel
|
||||
// FatalLevel logs a message, then calls os.Exit(1).
|
||||
FatalLevel = zapcore.FatalLevel
|
||||
)
|
214
logger.go
Normal file
214
logger.go
Normal file
@ -0,0 +1,214 @@
|
||||
package logos
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ Logger = (*warpLogger)(nil)
|
||||
var _ SugaredLogger = (*warpLogger)(nil)
|
||||
|
||||
func newLogger(name string, logger *zap.Logger) *warpLogger {
|
||||
|
||||
l := &warpLogger{
|
||||
Name: name,
|
||||
defLogger: logger,
|
||||
mu: sync.RWMutex{},
|
||||
}
|
||||
|
||||
l.SetUsedAt(time.Now())
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
type warpLogger struct {
|
||||
Name string
|
||||
|
||||
defLogger *zap.Logger
|
||||
sugaredLogger *zap.SugaredLogger
|
||||
mu sync.RWMutex
|
||||
|
||||
_usedAt uint32 // atomic
|
||||
_locked uint32
|
||||
_lockWait sync.WaitGroup
|
||||
}
|
||||
|
||||
func (l *warpLogger) Sugar() SugaredLogger {
|
||||
|
||||
l.initSugaredLogger()
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *warpLogger) Debug(msg string, fields ...Field) {
|
||||
l.checkLock()
|
||||
l.defLogger.Debug(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Info(msg string, fields ...Field) {
|
||||
|
||||
l.checkLock()
|
||||
l.defLogger.Info(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Warn(msg string, fields ...Field) {
|
||||
l.checkLock()
|
||||
l.defLogger.Warn(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Error(msg string, fields ...Field) {
|
||||
l.checkLock()
|
||||
l.defLogger.Error(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Fatal(msg string, fields ...Field) {
|
||||
l.checkLock()
|
||||
l.defLogger.Fatal(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Panic(msg string, fields ...Field) {
|
||||
l.checkLock()
|
||||
l.defLogger.Panic(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) DPanic(msg string, fields ...Field) {
|
||||
l.checkLock()
|
||||
l.defLogger.DPanic(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Debugf(format string, args ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Debugf(format, args...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Infof(format string, args ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Infof(format, args...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Warnf(format string, args ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Errorf(format string, args ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Fatalf(format string, args ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Panicf(format string, args ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Panicf(format, args...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) DPanicf(format string, args ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.DPanicf(format, args...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Debugw(msg string, keysAndValues ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Debugw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Infow(msg string, keysAndValues ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Infow(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Warnw(msg string, keysAndValues ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Warnw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Errorw(msg string, keysAndValues ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Errorw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Fatalw(msg string, keysAndValues ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Fatalw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Panicw(msg string, keysAndValues ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.Panicw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) DPanicw(msg string, keysAndValues ...interface{}) {
|
||||
l.checkLock()
|
||||
l.sugaredLogger.DPanicw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *warpLogger) Sync() error {
|
||||
return l.defLogger.Sync()
|
||||
}
|
||||
|
||||
func (l *warpLogger) Desugar() Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *warpLogger) UsedAt() time.Time {
|
||||
unix := atomic.LoadUint32(&l._usedAt)
|
||||
return time.Unix(int64(unix), 0)
|
||||
}
|
||||
|
||||
func (l *warpLogger) SetUsedAt(tm time.Time) {
|
||||
atomic.StoreUint32(&l._usedAt, uint32(tm.Unix()))
|
||||
}
|
||||
|
||||
func (l *warpLogger) initSugaredLogger() {
|
||||
|
||||
if l.sugaredLogger == nil {
|
||||
l.sugaredLogger = l.defLogger.Sugar()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (l *warpLogger) updateLogger(logger *zap.Logger) {
|
||||
|
||||
l.lock()
|
||||
defer l.unlock()
|
||||
|
||||
_ = l.Sync()
|
||||
|
||||
l.defLogger = logger
|
||||
|
||||
if l.sugaredLogger != nil {
|
||||
l.sugaredLogger = l.defLogger.Sugar()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *warpLogger) lock() {
|
||||
|
||||
l.mu.Lock()
|
||||
atomic.StoreUint32(&l._locked, 1)
|
||||
l._lockWait.Add(1)
|
||||
}
|
||||
|
||||
func (l *warpLogger) unlock() {
|
||||
|
||||
atomic.StoreUint32(&l._locked, 0)
|
||||
l._lockWait.Done()
|
||||
l.mu.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (l *warpLogger) checkLock() {
|
||||
|
||||
if l.locked() {
|
||||
l._lockWait.Wait()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (l *warpLogger) locked() bool {
|
||||
return atomic.LoadUint32(&l._locked) == 1
|
||||
}
|
79
loggerConfig.go
Normal file
79
loggerConfig.go
Normal file
@ -0,0 +1,79 @@
|
||||
package logos
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos/appender"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type loggerConfig struct {
|
||||
Name string
|
||||
|
||||
// Global core config
|
||||
Level zapcore.Level
|
||||
AddCaller bool
|
||||
AddStacktrace zapcore.LevelEnabler
|
||||
|
||||
Parent *loggerConfig
|
||||
coreConfigs map[string]zapcore.Level
|
||||
}
|
||||
|
||||
func (l *loggerConfig) CreateLogger(appenders map[string]*appender.Appender) *warpLogger {
|
||||
|
||||
if l.Level == OffLevel {
|
||||
return newLogger(l.Name, newZapLogger(l.Name, zapcore.NewNopCore()))
|
||||
}
|
||||
|
||||
zc := newZapCore(l.coreConfigs, appenders)
|
||||
zl := newZapLogger(l.Name, zc, zap.WithCaller(l.AddCaller), zap.AddStacktrace(l.AddStacktrace), zap.AddCallerSkip(1))
|
||||
return newLogger(l.Name, zl)
|
||||
|
||||
}
|
||||
|
||||
func (l *loggerConfig) UpdateLogger(logger *warpLogger, appenders map[string]*appender.Appender) {
|
||||
|
||||
if l.Level == OffLevel {
|
||||
logger.updateLogger(zap.NewNop())
|
||||
}
|
||||
zc := newZapCore(l.coreConfigs, appenders)
|
||||
|
||||
newLogger := zap.New(zc, zap.WithCaller(l.AddCaller), zap.AddStacktrace(l.AddStacktrace), zap.AddCallerSkip(1))
|
||||
|
||||
if len(l.Name) > 0 {
|
||||
newLogger = newLogger.Named(l.Name)
|
||||
}
|
||||
|
||||
logger.updateLogger(newLogger)
|
||||
|
||||
}
|
||||
|
||||
func (l *loggerConfig) copy(name string) *loggerConfig {
|
||||
|
||||
log := &loggerConfig{
|
||||
Name: name,
|
||||
Level: l.Level,
|
||||
Parent: l.Parent,
|
||||
coreConfigs: make(map[string]zapcore.Level),
|
||||
}
|
||||
|
||||
copyMapConfig(log.coreConfigs, l.coreConfigs)
|
||||
|
||||
return log
|
||||
|
||||
}
|
||||
|
||||
func copyMapConfig(dst map[string]zapcore.Level, src map[string]zapcore.Level) {
|
||||
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if dst == nil {
|
||||
dst = make(map[string]zapcore.Level, len(src))
|
||||
}
|
||||
|
||||
for name, level := range src {
|
||||
dst[name] = level
|
||||
}
|
||||
|
||||
}
|
164
logos.go
Normal file
164
logos.go
Normal file
@ -0,0 +1,164 @@
|
||||
package logos
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/khorevaa/logos/config"
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
_ "github.com/khorevaa/logos/appender"
|
||||
_ "github.com/khorevaa/logos/encoder/common"
|
||||
_ "github.com/khorevaa/logos/encoder/console"
|
||||
_ "github.com/khorevaa/logos/encoder/gelf"
|
||||
_ "github.com/khorevaa/logos/encoder/json"
|
||||
)
|
||||
|
||||
var (
|
||||
manager *logManager
|
||||
configFile string
|
||||
initLocker sync.Mutex
|
||||
explicitInited = false
|
||||
debug bool
|
||||
)
|
||||
|
||||
func resolveConfigFileFromEnv() (string, error) {
|
||||
f := os.Getenv("LOGOS_CONFIG_FILE")
|
||||
if f == "" {
|
||||
return "", errors.New("environment variable 'LOGOS_CONFIG_FILE' is not set")
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func resolveConfigFileFromWorkDir() (string, error) {
|
||||
matches1, _ := filepath.Glob("logos.yaml")
|
||||
matches2, _ := filepath.Glob("logos.yml")
|
||||
matches := append(matches1, matches2...)
|
||||
switch len(matches) {
|
||||
case 0:
|
||||
return "", errors.New("no config file found in work dir")
|
||||
case 1:
|
||||
return matches[0], nil
|
||||
default:
|
||||
panic(fmt.Errorf("multiple config files found %v", matches))
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
initLocker.Lock()
|
||||
defer initLocker.Unlock()
|
||||
|
||||
debug, _ = strconv.ParseBool(os.Getenv("LOGOS_DEBUG"))
|
||||
|
||||
if configFile == "" {
|
||||
cf, err := resolveConfigFileFromEnv()
|
||||
if err == nil {
|
||||
configFile = cf
|
||||
}
|
||||
}
|
||||
|
||||
if configFile == "" {
|
||||
cf, err := resolveConfigFileFromWorkDir()
|
||||
if err == nil {
|
||||
configFile = cf
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
var rawConfig *common.Config
|
||||
|
||||
if configFile != "" {
|
||||
// load ConfigFile
|
||||
configFile, err = filepath.Abs(configFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if debug {
|
||||
fmt.Println("logos using config file: ", configFile)
|
||||
bs, err := ioutil.ReadFile(configFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(bs))
|
||||
}
|
||||
|
||||
rawConfig, _, err = common.LoadFile(configFile)
|
||||
} else {
|
||||
if debug {
|
||||
fmt.Print("logos using default config:\n" + config.DefaultConfig)
|
||||
}
|
||||
rawConfig, err = common.NewConfigFrom(config.DefaultConfig)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
manager, err = newLogManager(rawConfig)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
manager.RedirectStdLog()
|
||||
|
||||
go func() {
|
||||
quit := make(chan os.Signal)
|
||||
signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)
|
||||
<-quit
|
||||
Sync()
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
func InitWithConfigContent(content string) error {
|
||||
initLocker.Lock()
|
||||
defer initLocker.Unlock()
|
||||
|
||||
if explicitInited {
|
||||
return errors.New("logos is explicit inited")
|
||||
}
|
||||
|
||||
if debug {
|
||||
fmt.Println("logos InitWithConfigContent:\n" + content)
|
||||
}
|
||||
|
||||
rawConfig, err := common.NewConfigFrom(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = manager.Update(rawConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
explicitInited = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func New(name string) Logger {
|
||||
return manager.NewLogger(name)
|
||||
}
|
||||
func SetLevel(name string, level zapcore.Level) {
|
||||
manager.SetLevel(name, level)
|
||||
}
|
||||
|
||||
func UpdateLogger(name string, logger *zap.Logger) {
|
||||
manager.UpdateLogger(name, logger)
|
||||
}
|
||||
|
||||
func Sync() {
|
||||
_ = manager.Sync()
|
||||
}
|
100
logos_test.go
Normal file
100
logos_test.go
Normal file
@ -0,0 +1,100 @@
|
||||
package logos
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/zap"
|
||||
stdlog "log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
logName string
|
||||
text []string
|
||||
}{
|
||||
{
|
||||
"simple",
|
||||
"github.com/khorevaa/logos",
|
||||
[]string{"hello world", "hello"},
|
||||
},
|
||||
{
|
||||
"simple",
|
||||
"github.com/v8platform",
|
||||
[]string{"hello world", "hello"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
log := New(tt.logName)
|
||||
|
||||
SetLevel(tt.logName, zap.DebugLevel)
|
||||
|
||||
for _, text := range tt.text {
|
||||
stdlog.Println(text)
|
||||
log.Info(text)
|
||||
log.Debug(text)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitWithConfigContent(t *testing.T) {
|
||||
const newConfig = `
|
||||
appenders:
|
||||
console:
|
||||
- name: CONSOLE
|
||||
target: stdout
|
||||
#no_color: true
|
||||
encoder:
|
||||
console:
|
||||
color_scheme:
|
||||
#debug_level: "Black,yellow"
|
||||
|
||||
rolling_file:
|
||||
- name: ROLL_FILE
|
||||
file_name: ./logs/log.log
|
||||
max_size: 100
|
||||
encoder:
|
||||
json:
|
||||
loggerConfigs:
|
||||
root:
|
||||
level: info
|
||||
appender_refs:
|
||||
- CONSOLE
|
||||
logger:
|
||||
- name: github.com
|
||||
level: debug
|
||||
add_caller: true
|
||||
trace_level: error
|
||||
appender_refs:
|
||||
- ROLL_FILE
|
||||
appenders:
|
||||
- name: CONSOLE
|
||||
level: debug
|
||||
|
||||
`
|
||||
l := New("github.com/logger")
|
||||
l.Info("hello")
|
||||
l.Debug("world")
|
||||
l2 := New("github.com/logger/v1")
|
||||
l2.Info("hello world test/logger/v1", zap.String("key", "val"))
|
||||
l2.Debug("hello world test/logger/v1")
|
||||
|
||||
err := InitWithConfigContent(newConfig)
|
||||
|
||||
//l.SetLLevel(OffLevel)
|
||||
assert.Nil(t, err)
|
||||
|
||||
//err = InitWithConfigContent(newConfig)
|
||||
//assert.NotNil(t, err)
|
||||
|
||||
l.Debug("hello world")
|
||||
l2.Debug("hello world test/logger/v1")
|
||||
l2.Error("hello world test/logger/v1", zap.Any("interface", []interface{}{1, "2", true}))
|
||||
l2.DPanic("hello world test/logger/v1", zap.Any("ints", []int{1, 2, 3321}))
|
||||
l2.Warn("hello world test/logger/v1", zap.Int("key", 123), zap.Bool("bool", false), zap.Any("bools", []bool{false, true, true}))
|
||||
|
||||
}
|
382
manager.go
Normal file
382
manager.go
Normal file
@ -0,0 +1,382 @@
|
||||
package logos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/khorevaa/logos/appender"
|
||||
config2 "github.com/khorevaa/logos/config"
|
||||
"github.com/khorevaa/logos/internal/common"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
rootLoggerName = "root"
|
||||
packageSepBySlash = '/'
|
||||
)
|
||||
|
||||
type logManager struct {
|
||||
getLoggerLocker sync.RWMutex
|
||||
loggerConfigs sync.Map
|
||||
coreLoggers sync.Map //
|
||||
|
||||
appenders map[string]*appender.Appender
|
||||
|
||||
rootLevel zapcore.Level
|
||||
rootLogger *warpLogger
|
||||
rootLoggerConfig *loggerConfig
|
||||
}
|
||||
|
||||
func newLogManager(rawConfig *common.Config) (*logManager, error) {
|
||||
|
||||
config := config2.Config{}
|
||||
err := rawConfig.Unpack(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := logManager{
|
||||
loggerConfigs: sync.Map{},
|
||||
coreLoggers: sync.Map{},
|
||||
appenders: map[string]*appender.Appender{},
|
||||
}
|
||||
|
||||
for appenderType, appenderConfigs := range config.Appenders {
|
||||
for _, appenderConfig := range appenderConfigs {
|
||||
createAppender, err := appender.CreateAppender(appenderType, appenderConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, err := appenderConfig.Name()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.appenders[name] = createAppender
|
||||
}
|
||||
}
|
||||
|
||||
err = m.newRootLoggerFromCfg(config.Loggers.Root)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// loggerConfigs
|
||||
for _, lc := range config.Loggers.Logger {
|
||||
l, err := m.newLoggerFromCfg(lc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.loggerConfigs.Store(lc.Name, l)
|
||||
|
||||
//if _, loaded := m.loggerConfigs.LoadOrStore(lc.Name, l); loaded {
|
||||
// return nil, fmt.Errorf("duplicated logger %q", lc.Name)
|
||||
//}
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
|
||||
}
|
||||
|
||||
func (m *logManager) NewLogger(name string) Logger {
|
||||
return m.getLogger(name)
|
||||
}
|
||||
|
||||
func (m *logManager) SetLevel(name string, level zapcore.Level) {
|
||||
|
||||
// TODO Чтото сделать
|
||||
//return m.getLogger(name)
|
||||
}
|
||||
|
||||
func (m *logManager) getLogger(name string, lock ...bool) *warpLogger {
|
||||
|
||||
if len(name) == 0 {
|
||||
return m.rootLogger
|
||||
}
|
||||
|
||||
if len(lock) > 0 && lock[0] || len(lock) == 0 {
|
||||
m.getLoggerLocker.Lock()
|
||||
defer m.getLoggerLocker.Unlock()
|
||||
}
|
||||
|
||||
if core, ok := m.coreLoggers.Load(name); ok {
|
||||
return core.(*warpLogger)
|
||||
}
|
||||
|
||||
if cfg, ok := m.loggerConfigs.Load(name); ok {
|
||||
|
||||
logConfig := cfg.(*loggerConfig)
|
||||
|
||||
core := logConfig.CreateLogger(m.appenders)
|
||||
m.coreLoggers.Store(logConfig.Name, core)
|
||||
|
||||
return core
|
||||
}
|
||||
|
||||
logConfig := m.newCoreLoggerConfig(name)
|
||||
m.loggerConfigs.Store(name, logConfig)
|
||||
|
||||
core := logConfig.CreateLogger(m.appenders)
|
||||
m.coreLoggers.Store(logConfig.Name, core)
|
||||
|
||||
return core
|
||||
|
||||
}
|
||||
|
||||
func (m *logManager) getParent(name string) *loggerConfig {
|
||||
|
||||
parent := m.getRootLoggerConfig()
|
||||
for i, c := range name {
|
||||
// Search for package separator character
|
||||
if c == packageSepBySlash {
|
||||
parentName := name[0:i]
|
||||
if parentName != "" {
|
||||
parent = m.loadCoreLoggerConfig(parentName, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent
|
||||
}
|
||||
|
||||
func (m *logManager) getRootLoggerConfig() *loggerConfig {
|
||||
|
||||
if m.rootLoggerConfig != nil {
|
||||
return m.rootLoggerConfig
|
||||
}
|
||||
|
||||
name := rootLoggerName
|
||||
|
||||
if log, ok := m.loggerConfigs.Load(name); ok {
|
||||
return log.(*loggerConfig)
|
||||
}
|
||||
|
||||
log := &loggerConfig{
|
||||
Name: name,
|
||||
Level: m.rootLevel,
|
||||
coreConfigs: make(map[string]zapcore.Level),
|
||||
AddStacktrace: StackTraceLevelEnabler,
|
||||
}
|
||||
log.coreConfigs["CONSOLE"] = InfoLevel
|
||||
|
||||
m.rootLoggerConfig = log
|
||||
m.loggerConfigs.Store(name, log)
|
||||
m.rootLogger = log.CreateLogger(m.appenders)
|
||||
|
||||
return m.rootLoggerConfig
|
||||
}
|
||||
|
||||
func (m *logManager) newLoggerFromCfg(loggerCfg config2.LoggerConfig) (*loggerConfig, error) {
|
||||
|
||||
name := loggerCfg.Name
|
||||
levelName := loggerCfg.Level
|
||||
appenderConfigs := loggerCfg.AppenderConfig
|
||||
appenders := loggerCfg.AppenderRefs
|
||||
|
||||
level, err := createLevel(levelName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := m.newCoreLoggerConfig(name)
|
||||
log.Level = level
|
||||
|
||||
if len(appenders) > 0 {
|
||||
log.coreConfigs = make(map[string]zapcore.Level, len(appenders))
|
||||
for _, appenderName := range appenders {
|
||||
log.coreConfigs[appenderName] = level
|
||||
}
|
||||
}
|
||||
|
||||
for _, appenderConfig := range appenderConfigs {
|
||||
|
||||
if len(appenderConfig.Level) > 0 {
|
||||
appenderLevel, err := createLevel(appenderConfig.Level)
|
||||
if err != nil {
|
||||
debugf("creating appender level <%s> error: %s", appenderConfig.Level, err)
|
||||
continue
|
||||
}
|
||||
log.coreConfigs[appenderConfig.Name] = appenderLevel
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log.AddCaller = loggerCfg.AddCaller
|
||||
log.AddStacktrace = StackTraceLevelEnabler
|
||||
|
||||
if tLevel, err := createLevel(loggerCfg.TraceLevel); len(loggerCfg.TraceLevel) > 0 && err == nil {
|
||||
log.AddStacktrace = zap.NewAtomicLevelAt(tLevel)
|
||||
}
|
||||
|
||||
return log, nil
|
||||
}
|
||||
|
||||
func debugf(format string, args ...interface{}) {
|
||||
if debug {
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *logManager) loadCoreLoggerConfig(name string, parent *loggerConfig) *loggerConfig {
|
||||
|
||||
if log, ok := m.loggerConfigs.Load(name); ok {
|
||||
return log.(*loggerConfig)
|
||||
}
|
||||
|
||||
if parent == nil {
|
||||
parent = m.rootLoggerConfig
|
||||
}
|
||||
|
||||
log := &loggerConfig{
|
||||
Name: name,
|
||||
Parent: parent,
|
||||
AddStacktrace: parent.AddStacktrace,
|
||||
AddCaller: parent.AddCaller,
|
||||
coreConfigs: make(map[string]zapcore.Level),
|
||||
}
|
||||
|
||||
copyMapConfig(log.coreConfigs, parent.coreConfigs)
|
||||
m.loggerConfigs.Store(name, log)
|
||||
return log
|
||||
|
||||
}
|
||||
|
||||
func (m *logManager) newCoreLoggerConfig(name string) *loggerConfig {
|
||||
|
||||
parent := m.getParent(name)
|
||||
loggerConfig := m.loadCoreLoggerConfig(name, parent)
|
||||
|
||||
return loggerConfig
|
||||
}
|
||||
|
||||
func (m *logManager) RedirectStdLog() {
|
||||
|
||||
stdlog := m.getLogger("stdlog", false)
|
||||
zap.RedirectStdLog(stdlog.defLogger)
|
||||
}
|
||||
|
||||
func (m *logManager) Update(rawConfig *common.Config) error {
|
||||
|
||||
nc, err := newLogManager(rawConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.getLoggerLocker.Lock()
|
||||
defer m.getLoggerLocker.Unlock()
|
||||
|
||||
err = m.Sync()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.appenders = nc.appenders
|
||||
m.rootLevel = nc.rootLevel
|
||||
m.rootLoggerConfig = nc.rootLoggerConfig
|
||||
m.rootLoggerConfig.UpdateLogger(m.rootLogger, m.appenders)
|
||||
|
||||
m.loggerConfigs.Range(func(key, value interface{}) bool {
|
||||
name := key.(string)
|
||||
newLog := nc.newCoreLoggerConfig(name)
|
||||
ref := value.(*loggerConfig)
|
||||
*ref = *newLog
|
||||
return true
|
||||
})
|
||||
|
||||
nc.loggerConfigs.Range(func(key, value interface{}) bool {
|
||||
if _, found := m.loggerConfigs.Load(key); found {
|
||||
return true
|
||||
}
|
||||
m.loggerConfigs.Store(key, value)
|
||||
return true
|
||||
})
|
||||
|
||||
m.coreLoggers.Range(func(key, value interface{}) bool {
|
||||
|
||||
newCore := m.newCoreLoggerConfig(key.(string))
|
||||
newCore.UpdateLogger(value.(*warpLogger), m.appenders)
|
||||
return true
|
||||
})
|
||||
|
||||
m.RedirectStdLog()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *logManager) Sync() error {
|
||||
m.coreLoggers.Range(func(_, value interface{}) bool {
|
||||
_ = value.(*warpLogger).Sync()
|
||||
return true
|
||||
})
|
||||
for _, a := range m.appenders {
|
||||
_ = a.Writer.Sync()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *logManager) newRootLoggerFromCfg(root config2.RootLogger) error {
|
||||
|
||||
levelName := root.Level
|
||||
appenderConfigs := root.AppenderConfig
|
||||
appenders := root.AppenderRefs
|
||||
|
||||
level, err := createLevel(levelName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.rootLevel = level
|
||||
|
||||
rootLoggerConfig := &loggerConfig{
|
||||
Name: rootLoggerName,
|
||||
Level: m.rootLevel,
|
||||
coreConfigs: make(map[string]zapcore.Level),
|
||||
AddStacktrace: StackTraceLevelEnabler,
|
||||
}
|
||||
|
||||
for _, appenderName := range appenders {
|
||||
rootLoggerConfig.coreConfigs[appenderName] = level
|
||||
}
|
||||
|
||||
for _, appenderConfig := range appenderConfigs {
|
||||
|
||||
if len(appenderConfig.Level) > 0 {
|
||||
|
||||
appenderLevel, err := createLevel(appenderConfig.Level)
|
||||
if err != nil {
|
||||
debugf("creating appender level <%s> error: %s", appenderConfig.Level, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rootLoggerConfig.coreConfigs[appenderConfig.Name] = appenderLevel
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
m.rootLoggerConfig = rootLoggerConfig
|
||||
m.loggerConfigs.Store(rootLoggerName, m.rootLoggerConfig)
|
||||
m.rootLogger = m.rootLoggerConfig.CreateLogger(m.appenders)
|
||||
|
||||
m.coreLoggers.Store(rootLoggerName, m.rootLogger)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *logManager) UpdateLogger(name string, logger *zap.Logger) {
|
||||
core := m.getLogger(name, false)
|
||||
core.updateLogger(logger)
|
||||
}
|
||||
|
||||
func createLevel(level string) (zapcore.Level, error) {
|
||||
switch level {
|
||||
case "off", "OFF", "false":
|
||||
return OffLevel, nil
|
||||
default:
|
||||
var l zapcore.Level
|
||||
err := l.UnmarshalText([]byte(level))
|
||||
return l, err
|
||||
}
|
||||
|
||||
}
|
123
types.go
Normal file
123
types.go
Normal file
@ -0,0 +1,123 @@
|
||||
package logos
|
||||
|
||||
type Logger interface {
|
||||
// Debug uses fmt.Sprint to construct and log a message.
|
||||
Debug(msg string, fields ...Field)
|
||||
|
||||
// Info uses fmt.Sprint to construct and log a message.
|
||||
Info(msg string, fields ...Field)
|
||||
|
||||
// Warn uses fmt.Sprint to construct and log a message.
|
||||
Warn(msg string, fields ...Field)
|
||||
|
||||
// Error uses fmt.Sprint to construct and log a message.
|
||||
Error(msg string, fields ...Field)
|
||||
|
||||
// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit(1).
|
||||
Fatal(msg string, fields ...Field)
|
||||
|
||||
// Panic uses fmt.Sprint to construct and log a message, then panics.
|
||||
Panic(msg string, fields ...Field)
|
||||
|
||||
// DPanic uses fmt.Sprint to construct and log a message. In development, the
|
||||
// logger then panics.
|
||||
DPanic(msg string, fields ...Field)
|
||||
|
||||
Sync() error
|
||||
|
||||
Sugar() SugaredLogger
|
||||
}
|
||||
|
||||
type SugaredLogger interface {
|
||||
// Debug uses fmt.Sprint to construct and log a message.
|
||||
Debug(msg string, fields ...Field)
|
||||
|
||||
// Info uses fmt.Sprint to construct and log a message.
|
||||
Info(msg string, fields ...Field)
|
||||
|
||||
// Warn uses fmt.Sprint to construct and log a message.
|
||||
Warn(msg string, fields ...Field)
|
||||
|
||||
// Error uses fmt.Sprint to construct and log a message.
|
||||
Error(msg string, fields ...Field)
|
||||
|
||||
// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit(1).
|
||||
Fatal(msg string, fields ...Field)
|
||||
|
||||
// Panic uses fmt.Sprint to construct and log a message, then panics.
|
||||
Panic(msg string, fields ...Field)
|
||||
|
||||
// DPanic uses fmt.Sprint to construct and log a message. In development, the
|
||||
// logger then panics.
|
||||
DPanic(msg string, fields ...Field)
|
||||
|
||||
// Debugf uses fmt.Sprintf to construct and log a message.
|
||||
Debugf(format string, args ...interface{})
|
||||
|
||||
// Infof uses fmt.Sprintf to log a templated message.
|
||||
Infof(format string, args ...interface{})
|
||||
|
||||
// Warnf uses fmt.Sprintf to log a templated message.
|
||||
Warnf(format string, args ...interface{})
|
||||
|
||||
// Errorf uses fmt.Sprintf to log a templated message.
|
||||
Errorf(format string, args ...interface{})
|
||||
|
||||
// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit(1).
|
||||
Fatalf(format string, args ...interface{})
|
||||
|
||||
// Panicf uses fmt.Sprintf to log a templated message, then panics.
|
||||
Panicf(format string, args ...interface{})
|
||||
|
||||
// DPanicf uses fmt.Sprintf to log a templated message. In development, the
|
||||
// logger then panics.
|
||||
DPanicf(format string, args ...interface{})
|
||||
|
||||
// Debugw logs a message with some additional context. The additional context
|
||||
// is added in the form of key-value pairs. The optimal way to write the value
|
||||
// to the log message will be inferred by the value's type. To explicitly
|
||||
// specify a type you can pass a Field such as logp.Stringer.
|
||||
Debugw(msg string, keysAndValues ...interface{})
|
||||
|
||||
// Infow logs a message with some additional context. The additional context
|
||||
// is added in the form of key-value pairs. The optimal way to write the value
|
||||
// to the log message will be inferred by the value's type. To explicitly
|
||||
// specify a type you can pass a Field such as logp.Stringer.
|
||||
Infow(msg string, keysAndValues ...interface{})
|
||||
|
||||
// Warnw logs a message with some additional context. The additional context
|
||||
// is added in the form of key-value pairs. The optimal way to write the value
|
||||
// to the log message will be inferred by the value's type. To explicitly
|
||||
// specify a type you can pass a Field such as logp.Stringer.
|
||||
Warnw(msg string, keysAndValues ...interface{})
|
||||
|
||||
// Errorw logs a message with some additional context. The additional context
|
||||
// is added in the form of key-value pairs. The optimal way to write the value
|
||||
// to the log message will be inferred by the value's type. To explicitly
|
||||
// specify a type you can pass a Field such as logp.Stringer.
|
||||
Errorw(msg string, keysAndValues ...interface{})
|
||||
|
||||
// Fatalw logs a message with some additional context, then calls os.Exit(1).
|
||||
// The additional context is added in the form of key-value pairs. The optimal
|
||||
// way to write the value to the log message will be inferred by the value's
|
||||
// type. To explicitly specify a type you can pass a Field such as
|
||||
// logp.Stringer.
|
||||
Fatalw(msg string, keysAndValues ...interface{})
|
||||
|
||||
// Panicw logs a message with some additional context, then panics. The
|
||||
// additional context is added in the form of key-value pairs. The optimal way
|
||||
// to write the value to the log message will be inferred by the value's type.
|
||||
// To explicitly specify a type you can pass a Field such as logp.Stringer.
|
||||
Panicw(msg string, keysAndValues ...interface{})
|
||||
|
||||
// DPanicw logs a message with some additional context. The logger panics only
|
||||
// in Development mode. The additional context is added in the form of
|
||||
// key-value pairs. The optimal way to write the value to the log message will
|
||||
// be inferred by the value's type. To explicitly specify a type you can pass a
|
||||
// Field such as logp.Stringer.
|
||||
DPanicw(msg string, keysAndValues ...interface{})
|
||||
|
||||
Sync() error
|
||||
|
||||
Desugar() Logger
|
||||
}
|
36
warp_zap.go
Normal file
36
warp_zap.go
Normal file
@ -0,0 +1,36 @@
|
||||
package logos
|
||||
|
||||
import (
|
||||
"github.com/khorevaa/logos/appender"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var StackTraceLevelEnabler = zap.NewAtomicLevelAt(zapcore.ErrorLevel)
|
||||
|
||||
func newZapCore(config map[string]zapcore.Level, appenders map[string]*appender.Appender) zapcore.Core {
|
||||
|
||||
zcs := make([]zapcore.Core, 0)
|
||||
|
||||
for name, level := range config {
|
||||
|
||||
if level == OffLevel {
|
||||
continue
|
||||
}
|
||||
|
||||
if a, ok := appenders[name]; ok {
|
||||
zcs = append(zcs, zapcore.NewCore(a.Encoder, a.Writer, level))
|
||||
}
|
||||
|
||||
}
|
||||
if len(zcs) == 0 {
|
||||
return zapcore.NewNopCore()
|
||||
}
|
||||
|
||||
return zapcore.NewTee(zcs...)
|
||||
}
|
||||
|
||||
func newZapLogger(name string, core zapcore.Core, option ...zap.Option) *zap.Logger {
|
||||
|
||||
return zap.New(core, option...).Named(name)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user