github.com/ronaksoft/rony@v0.16.26-0.20230807065236-1743dbfe6959/log/logger.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"runtime/debug"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/ronaksoft/rony/di"
    12  	"go.uber.org/zap"
    13  	"go.uber.org/zap/zapcore"
    14  )
    15  
    16  /*
    17     Creation Time: 2019 - Mar - 02
    18     Created by:  (ehsan)
    19     Maintainers:
    20        1.  Ehsan N. Moosa (E2)
    21     Auditor: Ehsan N. Moosa (E2)
    22     Copyright Ronak Software Group 2020
    23  */
    24  
    25  // ronyLogger is a wrapper around zap.Logger and adds a good few features to it.
    26  // It provides layered logs which could be used by separate packages, and could be turned off or on
    27  // separately. Separate layers could also have independent log levels.
    28  // Whenever you change log level it propagates through its children.
    29  type ronyLogger struct {
    30  	prefix     string
    31  	skipCaller int
    32  	encoder    zapcore.Encoder
    33  	z          *zap.Logger
    34  	sz         *zap.SugaredLogger
    35  	lvl        zap.AtomicLevel
    36  }
    37  
    38  func New(opts ...Option) *ronyLogger {
    39  	cfg := defaultConfig
    40  	for _, opt := range opts {
    41  		opt(&cfg)
    42  	}
    43  
    44  	l := &ronyLogger{
    45  		lvl:        zap.NewAtomicLevelAt(cfg.level),
    46  		skipCaller: cfg.skipCaller,
    47  	}
    48  
    49  	l.encoder = EncoderBuilder().
    50  		WithTimeKey("ts").
    51  		WithLevelKey("level").
    52  		WithNameKey("name").
    53  		WithCallerKey("caller").
    54  		WithMessageKey("msg").
    55  		ConsoleEncoder()
    56  
    57  	cores := append([]zapcore.Core{},
    58  		zapcore.NewCore(l.encoder, zapcore.Lock(os.Stdout), l.lvl),
    59  	)
    60  
    61  	if cfg.syslogTag != "" {
    62  		syslogCore, err := NewSyslogCore(l.lvl, l.encoder, cfg.syslogTag)
    63  		if err != nil {
    64  			fmt.Println("got error on enabling syslog:", err)
    65  		} else {
    66  			cores = append(cores, syslogCore)
    67  		}
    68  	}
    69  
    70  	if cfg.sentryDSN != "" {
    71  		sentryCore := NewSentryCore(cfg.sentryDSN, cfg.release, cfg.environment, cfg.sentryLevel, nil)
    72  		if sentryCore != nil {
    73  			cores = append(cores, sentryCore)
    74  		}
    75  	}
    76  
    77  	l.z = zap.New(
    78  		zapcore.NewTee(cores...),
    79  		zap.AddCaller(),
    80  		zap.AddStacktrace(ErrorLevel),
    81  		zap.AddCallerSkip(cfg.skipCaller),
    82  	)
    83  
    84  	l.sz = l.z.Sugar()
    85  
    86  	return l
    87  }
    88  
    89  func newNOP() *ronyLogger {
    90  	l := &ronyLogger{}
    91  	l.z = zap.NewNop()
    92  	l.sz = l.z.Sugar()
    93  
    94  	return l
    95  }
    96  
    97  var (
    98  	once sync.Once
    99  )
   100  
   101  // ProvideDI is protected by sync.Once and provides the Logger interface for other packages.
   102  func ProvideDI(opts ...Option) {
   103  	once.Do(func() {
   104  		di.MustProvide(
   105  			func() Logger {
   106  				return New(opts...)
   107  			},
   108  		)
   109  	})
   110  }
   111  
   112  func (l *ronyLogger) Sugared() *sugaredRonyLogger {
   113  	return &sugaredRonyLogger{
   114  		l: l,
   115  	}
   116  }
   117  
   118  func (l *ronyLogger) Sync() error {
   119  	return l.z.Sync()
   120  }
   121  
   122  func (l *ronyLogger) SetLevel(lvl Level) {
   123  	l.lvl.SetLevel(lvl)
   124  }
   125  
   126  func (l *ronyLogger) With(name string) Logger {
   127  	return l.WithSkip(name, l.skipCaller)
   128  }
   129  
   130  func (l *ronyLogger) WithSkip(name string, skipCaller int) Logger {
   131  	return l.with(l.z.Core(), name, skipCaller)
   132  }
   133  
   134  func (l *ronyLogger) WithCore(enc Encoder, w io.Writer) Logger {
   135  	core := zapcore.NewTee(
   136  		l.z.Core(),
   137  		zapcore.NewCore(enc, zapcore.AddSync(w), l.lvl),
   138  	)
   139  
   140  	return l.with(core, "", l.skipCaller)
   141  }
   142  
   143  func (l *ronyLogger) with(core zapcore.Core, name string, skip int) Logger {
   144  	prefix := l.prefix
   145  	if name != "" {
   146  		prefix = fmt.Sprintf("%s[%s]", l.prefix, name)
   147  	}
   148  	childLogger := &ronyLogger{
   149  		prefix:     prefix,
   150  		skipCaller: l.skipCaller,
   151  		encoder:    l.encoder.Clone(),
   152  		z: zap.New(
   153  			core,
   154  			zap.AddCaller(),
   155  			zap.AddStacktrace(ErrorLevel),
   156  			zap.AddCallerSkip(skip),
   157  		),
   158  		sz: zap.New(
   159  			core,
   160  			zap.AddCaller(),
   161  			zap.AddStacktrace(ErrorLevel),
   162  			zap.AddCallerSkip(skip)).Sugar(),
   163  		lvl: l.lvl,
   164  	}
   165  
   166  	return childLogger
   167  }
   168  
   169  func (l *ronyLogger) addPrefix(in string) (out string) {
   170  	if l.prefix != "" {
   171  		sb := &strings.Builder{}
   172  		sb.WriteString(l.prefix)
   173  		sb.WriteRune(' ')
   174  		sb.WriteString(in)
   175  		out = sb.String()
   176  
   177  		return out
   178  	}
   179  
   180  	return in
   181  }
   182  
   183  func (l *ronyLogger) WarnOnErr(guideTxt string, err error, fields ...Field) {
   184  	if err != nil {
   185  		fields = append(fields, zap.Error(err))
   186  		l.Warn(guideTxt, fields...)
   187  	}
   188  }
   189  
   190  func (l *ronyLogger) ErrorOnErr(guideTxt string, err error, fields ...Field) {
   191  	if err != nil {
   192  		fields = append(fields, zap.Error(err))
   193  		l.Error(guideTxt, fields...)
   194  	}
   195  }
   196  
   197  func (l *ronyLogger) checkLevel(lvl Level) bool {
   198  	// Check the level first to reduce the cost of disabled log calls.
   199  	// Since Panic and higher may exit, we skip the optimization for those levels.
   200  	if lvl < zapcore.DPanicLevel && !l.z.Core().Enabled(lvl) {
   201  		return false
   202  	}
   203  
   204  	return true
   205  }
   206  
   207  func (l *ronyLogger) Check(lvl Level, msg string) *CheckedEntry {
   208  	if !l.checkLevel(lvl) {
   209  		return nil
   210  	}
   211  
   212  	return l.z.Check(lvl, l.addPrefix(msg))
   213  }
   214  
   215  func (l *ronyLogger) Debug(msg string, fields ...Field) {
   216  	if !l.checkLevel(DebugLevel) {
   217  		return
   218  	}
   219  	if ce := l.z.Check(DebugLevel, l.addPrefix(msg)); ce != nil {
   220  		ce.Write(fields...)
   221  	}
   222  }
   223  
   224  func (l *ronyLogger) Info(msg string, fields ...Field) {
   225  	if !l.checkLevel(InfoLevel) {
   226  		return
   227  	}
   228  	if ce := l.z.Check(InfoLevel, l.addPrefix(msg)); ce != nil {
   229  		ce.Write(fields...)
   230  	}
   231  }
   232  
   233  func (l *ronyLogger) Warn(msg string, fields ...Field) {
   234  	if !l.checkLevel(WarnLevel) {
   235  		return
   236  	}
   237  	if ce := l.z.Check(WarnLevel, l.addPrefix(msg)); ce != nil {
   238  		ce.Write(fields...)
   239  	}
   240  }
   241  
   242  func (l *ronyLogger) Error(msg string, fields ...Field) {
   243  	if !l.checkLevel(ErrorLevel) {
   244  		return
   245  	}
   246  	if ce := l.z.Check(ErrorLevel, l.addPrefix(msg)); ce != nil {
   247  		ce.Write(fields...)
   248  	}
   249  }
   250  
   251  func (l *ronyLogger) Fatal(msg string, fields ...Field) {
   252  	l.z.Fatal(l.addPrefix(msg), fields...)
   253  }
   254  
   255  func (l *ronyLogger) RecoverPanic(funcName string, extraInfo interface{}, compensationFunc func()) {
   256  	if r := recover(); r != nil {
   257  		l.Error("Panic Recovered",
   258  			zap.String("Func", funcName),
   259  			zap.Any("Info", extraInfo),
   260  			zap.Any("Recover", r),
   261  			zap.ByteString("StackTrace", debug.Stack()),
   262  		)
   263  		if compensationFunc != nil {
   264  			go compensationFunc()
   265  		}
   266  	}
   267  }
   268  
   269  type sugaredRonyLogger struct {
   270  	l *ronyLogger
   271  }
   272  
   273  func (l sugaredRonyLogger) Debugf(template string, args ...interface{}) {
   274  	l.l.sz.Debugf(l.l.addPrefix(template), args...)
   275  }
   276  
   277  func (l sugaredRonyLogger) Infof(template string, args ...interface{}) {
   278  	l.l.sz.Infof(l.l.addPrefix(template), args...)
   279  }
   280  
   281  func (l sugaredRonyLogger) Printf(template string, args ...interface{}) {
   282  	fmt.Printf(template, args...)
   283  }
   284  
   285  func (l sugaredRonyLogger) Warnf(template string, args ...interface{}) {
   286  	l.l.sz.Warnf(l.l.addPrefix(template), args...)
   287  }
   288  
   289  func (l sugaredRonyLogger) Errorf(template string, args ...interface{}) {
   290  	l.l.sz.Errorf(l.l.addPrefix(template), args...)
   291  }
   292  
   293  func (l sugaredRonyLogger) Fatalf(template string, args ...interface{}) {
   294  	l.l.sz.Fatalf(l.l.addPrefix(template), args...)
   295  }
   296  
   297  func (l sugaredRonyLogger) Debug(args ...interface{}) {
   298  	l.l.sz.Debug(args...)
   299  }
   300  
   301  func (l sugaredRonyLogger) Info(args ...interface{}) {
   302  	l.l.sz.Info(args...)
   303  }
   304  
   305  func (l sugaredRonyLogger) Warn(args ...interface{}) {
   306  	l.l.sz.Warn(args...)
   307  }
   308  
   309  func (l sugaredRonyLogger) Error(args ...interface{}) {
   310  	l.l.sz.Error(args...)
   311  }
   312  
   313  func (l sugaredRonyLogger) Fatal(args ...interface{}) {
   314  	l.l.sz.Fatal(args...)
   315  }
   316  
   317  func (l sugaredRonyLogger) Panic(args ...interface{}) {
   318  	l.l.sz.Panic(args...)
   319  }