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 }