log/修改包名 jsonlog->logjson; 修改 logjson.config 的初始化方式;
This commit is contained in:
98
log/logsdk/logjson/option.go
Normal file
98
log/logsdk/logjson/option.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package logjson
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.blauwelle.com/go/crate/synchelper"
|
||||
)
|
||||
|
||||
// Option 配置日志处理对象
|
||||
type Option interface {
|
||||
apply(cfg *config)
|
||||
}
|
||||
|
||||
// WithBufferPool 配置缓冲池
|
||||
func WithBufferPool(pool synchelper.BytesBufferPool) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.pool = pool
|
||||
})
|
||||
}
|
||||
|
||||
// WithOutput 配置输出
|
||||
func WithOutput(w io.Writer) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.output = w
|
||||
})
|
||||
}
|
||||
|
||||
// WithTimeFormat 配置时间格式
|
||||
func WithTimeFormat(format string) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.timestampFormat = format
|
||||
})
|
||||
}
|
||||
|
||||
// WithDisableTime 配置仅用时间输出
|
||||
func WithDisableTime(disable bool) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.disableTime = disable
|
||||
})
|
||||
}
|
||||
|
||||
// WithDisableHTMLEscape 配置禁止 HTML 转义
|
||||
func WithDisableHTMLEscape(disable bool) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.disableHTMLEscape = disable
|
||||
})
|
||||
}
|
||||
|
||||
// WithPrettyPrint 配置 JSON 多行缩进输出
|
||||
func WithPrettyPrint(pretty bool) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.prettyPrint = pretty
|
||||
})
|
||||
}
|
||||
|
||||
func newConfig(opts ...Option) *config {
|
||||
cfg := defaultConfig()
|
||||
for _, opt := range opts {
|
||||
opt.apply(cfg)
|
||||
}
|
||||
if !cfg.hasPool {
|
||||
cfg.pool = synchelper.NewBytesBufferPool(512, 4096)
|
||||
}
|
||||
if cfg.output == nil {
|
||||
cfg.output = synchelper.NewSyncWriter(os.Stderr)
|
||||
}
|
||||
if cfg.timestampFormat == "" {
|
||||
cfg.timestampFormat = time.RFC3339Nano
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func defaultConfig() *config {
|
||||
return &config{
|
||||
hasPool: false,
|
||||
disableTime: false,
|
||||
disableHTMLEscape: false,
|
||||
prettyPrint: false,
|
||||
}
|
||||
}
|
||||
|
||||
type config struct {
|
||||
pool synchelper.BytesBufferPool
|
||||
output io.Writer
|
||||
timestampFormat string
|
||||
hasPool bool
|
||||
disableTime bool
|
||||
disableHTMLEscape bool
|
||||
prettyPrint bool
|
||||
}
|
||||
|
||||
type optionFunc func(cfg *config)
|
||||
|
||||
func (fn optionFunc) apply(cfg *config) {
|
||||
fn(cfg)
|
||||
}
|
||||
89
log/logsdk/logjson/processor.go
Normal file
89
log/logsdk/logjson/processor.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package logjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"git.blauwelle.com/go/crate/log/logsdk"
|
||||
"git.blauwelle.com/go/crate/runtimehelper"
|
||||
"git.blauwelle.com/go/crate/synchelper"
|
||||
)
|
||||
|
||||
var _ logsdk.EntryProcessor = &Processor{}
|
||||
|
||||
// New 返回日志处理对象,
|
||||
// 返回的对象是 [logsdk.EntryProcessor].
|
||||
func New(opts ...Option) *Processor {
|
||||
cfg := newConfig(opts...)
|
||||
return &Processor{
|
||||
bufferPool: cfg.pool,
|
||||
output: cfg.output,
|
||||
timeFormat: cfg.timestampFormat,
|
||||
disableTime: cfg.disableTime,
|
||||
disableHTMLEscape: cfg.disableHTMLEscape,
|
||||
prettyPrint: cfg.prettyPrint,
|
||||
}
|
||||
}
|
||||
|
||||
// Processor 日志处理对象, 把日志处理成 JSON.
|
||||
type Processor struct {
|
||||
bufferPool synchelper.BytesBufferPool
|
||||
output io.Writer
|
||||
timeFormat string
|
||||
disableTime bool
|
||||
disableHTMLEscape bool
|
||||
prettyPrint bool
|
||||
}
|
||||
|
||||
// Process 处理日志
|
||||
func (processor *Processor) Process(entry logsdk.ReadonlyEntry) {
|
||||
m := Entry{
|
||||
Stack: entry.Stack,
|
||||
Fields: entry.Fields,
|
||||
Level: entry.Level,
|
||||
Message: entry.Message,
|
||||
}
|
||||
if !processor.disableTime {
|
||||
m.Time = entry.Time.Format(processor.timeFormat) // 1次分配
|
||||
}
|
||||
if entry.Caller.IsValid() {
|
||||
// 1次分配
|
||||
// 直接取 &entry.Caller 会增加堆内存分配
|
||||
m.Caller = &runtimehelper.Frame{
|
||||
Function: entry.Caller.Function,
|
||||
File: entry.Caller.File,
|
||||
Line: entry.Caller.Line,
|
||||
}
|
||||
}
|
||||
|
||||
buf := processor.bufferPool.Get()
|
||||
buf.Reset()
|
||||
defer processor.bufferPool.Put(buf)
|
||||
|
||||
encoder := json.NewEncoder(buf)
|
||||
if processor.prettyPrint {
|
||||
encoder.SetIndent("", " ")
|
||||
}
|
||||
encoder.SetEscapeHTML(!processor.disableHTMLEscape)
|
||||
|
||||
// Encode 2次分配
|
||||
if err := encoder.Encode(m); err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "JSON processor cannot encode log %#v: %s\n", m, err.Error())
|
||||
}
|
||||
|
||||
if _, err := buf.WriteTo(processor.output); err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "JSON processor cannot write log: %s\n", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Entry 被用来 JSON 序列化
|
||||
type Entry struct {
|
||||
Message string `json:"msg"`
|
||||
Time string `json:"time,omitempty"`
|
||||
Caller *runtimehelper.Frame `json:"caller,omitempty"`
|
||||
Stack []runtimehelper.Frame `json:"stack,omitempty"`
|
||||
Fields []logsdk.Field `json:"fields,omitempty"`
|
||||
Level logsdk.Level `json:"level"`
|
||||
}
|
||||
Reference in New Issue
Block a user