github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/logging/logging.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package logging
    20  
    21  import (
    22  	"os"
    23  	"strings"
    24  	"sync"
    25  
    26  	"github.com/e154/smart-home/common"
    27  	m "github.com/e154/smart-home/models"
    28  	"go.uber.org/zap"
    29  	"go.uber.org/zap/zapcore"
    30  )
    31  
    32  // Logging ...
    33  type Logging struct {
    34  	logger     *zap.Logger
    35  	dbSaver    ISaver
    36  	wsSaver    ISaver
    37  	oldLogLock *sync.Mutex
    38  	oldLog     m.Log
    39  }
    40  
    41  // NewLogger ...
    42  func NewLogger(cfg *Config) (logging *Logging) {
    43  
    44  	// First, define our level-handling logic.
    45  	highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    46  		return lvl >= zapcore.WarnLevel
    47  	})
    48  	lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    49  		return lvl < zapcore.WarnLevel
    50  	})
    51  
    52  	// High-priority output should also go to standard error, and low-priority
    53  	// output should also go to standard out.
    54  	consoleDebugging := zapcore.Lock(os.Stdout)
    55  	consoleErrors := zapcore.Lock(os.Stderr)
    56  
    57  	config := zap.NewDevelopmentEncoderConfig()
    58  	config.EncodeTime = nil
    59  	if cfg.ColoredLogging {
    60  		config.EncodeLevel = CustomLevelEncoder
    61  	}
    62  	config.EncodeName = CustomNameEncoder
    63  	config.EncodeCaller = CustomCallerEncoder
    64  	consoleEncoder := zapcore.NewConsoleEncoder(config)
    65  
    66  	// Join the outputs, encoders, and level-handling functions into
    67  	// zapcore.Cores, then tee the four cores together.
    68  	core := zapcore.NewTee(
    69  		zapcore.NewCore(consoleEncoder, consoleErrors, highPriority),
    70  		zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),
    71  	)
    72  
    73  	logging = &Logging{
    74  		oldLogLock: &sync.Mutex{},
    75  	}
    76  
    77  	// From a zapcore.Core, it's easy to construct a Logger.
    78  	logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.Hooks(logging.selfSaver))
    79  
    80  	zap.ReplaceGlobals(logger)
    81  
    82  	logging.logger = logger
    83  
    84  	return
    85  }
    86  
    87  func (b *Logging) selfSaver(e zapcore.Entry) (err error) {
    88  
    89  	var logLevel common.LogLevel
    90  	switch e.Level {
    91  	case zapcore.ErrorLevel, zapcore.FatalLevel:
    92  		logLevel = "Error"
    93  	case zapcore.WarnLevel:
    94  		logLevel = "Warning"
    95  	case zapcore.InfoLevel:
    96  		logLevel = "Info"
    97  	case zapcore.DebugLevel:
    98  		logLevel = "Debug"
    99  	default:
   100  		logLevel = "Warning"
   101  	}
   102  
   103  	if LogsHook != nil {
   104  		LogsHook(logLevel)
   105  	}
   106  
   107  	b.oldLogLock.Lock()
   108  	defer b.oldLogLock.Unlock()
   109  
   110  	if b.oldLog.Body == e.Message && b.oldLog.Level == logLevel {
   111  		return
   112  	}
   113  
   114  	record := m.Log{
   115  		Level:     logLevel,
   116  		Body:      e.Message,
   117  		Owner:     e.LoggerName,
   118  		CreatedAt: e.Time,
   119  	}
   120  
   121  	b.oldLog = record
   122  
   123  	// db
   124  	if b.dbSaver != nil {
   125  		b.dbSaver.Save(record)
   126  	}
   127  
   128  	// ws
   129  	if b.wsSaver != nil {
   130  		b.wsSaver.Save(record)
   131  	}
   132  
   133  	return nil
   134  }
   135  
   136  // SetDbSaver ...
   137  func (b *Logging) SetDbSaver(saver ISaver) {
   138  	b.dbSaver = saver
   139  }
   140  
   141  // SetWsSaver ...
   142  func (b *Logging) SetWsSaver(saver ISaver) {
   143  	b.wsSaver = saver
   144  }
   145  
   146  // CustomLevelEncoder ...
   147  func CustomLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
   148  	s, ok := _levelToCapitalColorString[l]
   149  	if !ok {
   150  		s = _unknownLevelColor.Add(l.CapitalString())
   151  	}
   152  	enc.AppendString(s)
   153  }
   154  
   155  // TODO fix
   156  func CustomNameEncoder(v string, enc zapcore.PrimitiveArrayEncoder) {
   157  	var builder strings.Builder
   158  	builder.WriteString(White.Add(v))
   159  	builder.WriteString("                                      ")
   160  	enc.AppendString(builder.String()[0:25])
   161  }
   162  
   163  // CustomCallerEncoder ...
   164  func CustomCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
   165  	enc.AppendString(caller.TrimmedPath() + " >")
   166  }