github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/xlog/file_core.go (about)

     1  package xlog
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"time"
    10  
    11  	"go.uber.org/zap"
    12  	"go.uber.org/zap/zapcore"
    13  )
    14  
    15  var _ xLogCore = (*consoleCore)(nil)
    16  
    17  type fileCore struct {
    18  	core *commonCore
    19  }
    20  
    21  func (cc *fileCore) timeEncoder() zapcore.TimeEncoder   { return cc.core.tsEnc }
    22  func (cc *fileCore) levelEncoder() zapcore.LevelEncoder { return cc.core.lvlEnc }
    23  func (cc *fileCore) writeSyncer() zapcore.WriteSyncer   { return cc.core.ws }
    24  func (cc *fileCore) outEncoder() func(cfg zapcore.EncoderConfig) zapcore.Encoder {
    25  	return cc.core.enc
    26  }
    27  func (cc *fileCore) context() context.Context             { return cc.core.ctx }
    28  func (cc *fileCore) Enabled(lvl zapcore.Level) bool       { return cc.core.lvlEnabler.Enabled(lvl) }
    29  func (cc *fileCore) With(fields []zap.Field) zapcore.Core { return cc.core.With(fields) }
    30  func (cc *fileCore) Sync() error                          { return cc.core.Sync() }
    31  func (cc *fileCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
    32  	return cc.core.Check(ent, ce)
    33  }
    34  
    35  func (cc *fileCore) Write(ent zapcore.Entry, fields []zap.Field) error {
    36  	return cc.core.Write(ent, fields)
    37  }
    38  
    39  type FileCoreConfig struct {
    40  	FilePath                string `json:"filePath" yaml:"filePath"`
    41  	Filename                string `json:"filename" yaml:"filename"`
    42  	FileMaxSize             string `json:"fileMaxSize" yaml:"fileMaxSize"`
    43  	FileMaxAge              string `json:"fileMaxAge" yaml:"fileMaxAge"`
    44  	FileZipName             string `json:"fileZipName" yaml:"fileZipName"`
    45  	FileBufferSize          string `json:"fileBufferSize" yaml:"fileBufferSize"`
    46  	FileBufferFlushInterval int64  `json:"fileBufferFlushInterval" yaml:"fileBufferFlushInterval"` // Milliseconds
    47  	FileMaxBackups          int    `json:"fileMaxBackups" yaml:"fileMaxBackups"`
    48  	FileCompressBatch       int    `json:"fileCompressBatch" yaml:"fileCompressBatch"`
    49  	FileCompressible        bool   `json:"fileCompressible" yaml:"fileCompressible"`
    50  	FileRotateEnable        bool   `json:"fileRotateEnable" yaml:"fileRotateEnable"`
    51  }
    52  
    53  // TODO Runtime modification and applying.
    54  
    55  func newFileCore(cfg *FileCoreConfig) XLogCoreConstructor {
    56  	return func(
    57  		ctx context.Context,
    58  		lvlEnabler zapcore.LevelEnabler,
    59  		encoder logEncoderType,
    60  		lvlEnc zapcore.LevelEncoder,
    61  		tsEnc zapcore.TimeEncoder,
    62  	) xLogCore {
    63  		// The root context is done and root writer will be close globally.
    64  		if ctx == nil {
    65  			return nil
    66  		}
    67  
    68  		if cfg == nil {
    69  			cfg = &FileCoreConfig{
    70  				Filename:         filepath.Base(os.Args[0]) + "_xlog.log",
    71  				FilePath:         os.TempDir(),
    72  				FileRotateEnable: false,
    73  			}
    74  		}
    75  
    76  		var (
    77  			err           error
    78  			bufferEnabled = false
    79  			bufSize       uint64
    80  			bufInterval   int64
    81  			fileWriter    io.WriteCloser
    82  			ws            zapcore.WriteSyncer
    83  		)
    84  		if cfg.FileBufferSize != "" && cfg.FileBufferFlushInterval > 0 {
    85  			bufSize, err = parseBufferSize(cfg.FileBufferSize)
    86  			if err != nil {
    87  				goto writerInit
    88  			}
    89  			if time.Duration(cfg.FileBufferFlushInterval).Milliseconds() < 200 {
    90  				bufInterval = 200
    91  			} else {
    92  				if bufInterval = cfg.FileBufferFlushInterval; bufInterval > _maxBufferFlushMs {
    93  					bufInterval = _maxBufferFlushMs
    94  				}
    95  			}
    96  			bufferEnabled = true
    97  		}
    98  	writerInit:
    99  		if cfg.FileRotateEnable {
   100  			fileWriter = RotateLog(ctx, cfg)
   101  		} else {
   102  			fileWriter = SingleLog(ctx, cfg)
   103  		}
   104  		if bufferEnabled {
   105  			ws = XLogBufferSyncer(ctx, fileWriter, bufSize, bufInterval)
   106  		} else {
   107  			ws = XLogLockSyncer(ctx, fileWriter)
   108  		}
   109  
   110  		cc := &fileCore{
   111  			core: &commonCore{
   112  				ctx:        ctx,
   113  				lvlEnabler: lvlEnabler,
   114  				lvlEnc:     lvlEnc,
   115  				tsEnc:      tsEnc,
   116  				ws:         ws,
   117  				enc:        getEncoderByType(encoder),
   118  			},
   119  		}
   120  		cfg := defaultCoreEncoderCfg()
   121  		cfg.EncodeLevel = cc.core.lvlEnc
   122  		cfg.EncodeTime = cc.core.tsEnc
   123  		cc.core.core = zapcore.NewCore(cc.core.enc(cfg), cc.core.ws, cc.core.lvlEnabler)
   124  		return cc
   125  	}
   126  }
   127  
   128  const (
   129  	_maxBufferSize    = 10 * MB
   130  	_maxBufferFlushMs = 3000
   131  )
   132  
   133  func parseBufferSize(size string) (uint64, error) {
   134  	_size, err := parseFileSize(size)
   135  	if err != nil {
   136  		return 0, err
   137  	}
   138  	if _size > uint64(_maxBufferSize) {
   139  		return 0, errors.New("file buffer size too large")
   140  	}
   141  	return _size, nil
   142  }