gitee.com/mirrors/gauge@v1.0.6/logger/logger.go (about) 1 // Copyright 2019 ThoughtWorks, Inc. 2 3 // This file is part of Gauge. 4 5 // Gauge is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 10 // Gauge 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 13 // GNU General Public License for more details. 14 15 // You should have received a copy of the GNU General Public License 16 // along with Gauge. If not, see <http://www.gnu.org/licenses/>. 17 18 package logger 19 20 import ( 21 "encoding/json" 22 "fmt" 23 "os" 24 "path/filepath" 25 "runtime" 26 "strings" 27 28 "github.com/getgauge/common" 29 "github.com/getgauge/gauge/config" 30 "github.com/getgauge/gauge/plugin/pluginInfo" 31 "github.com/getgauge/gauge/version" 32 "github.com/natefinch/lumberjack" 33 "github.com/op/go-logging" 34 ) 35 36 const ( 37 gaugeModuleID = "Gauge" 38 logsDirectory = "logs_directory" 39 logs = "logs" 40 gaugeLogFileName = "gauge.log" 41 apiLogFileName = "api.log" 42 lspLogFileName = "lsp.log" 43 // CLI indicates gauge is used as a CLI. 44 CLI = iota 45 // API indicates gauge is in daemon mode. Used in IDEs. 46 API 47 // LSP indicates that gauge is acting as an LSP server. 48 LSP 49 ) 50 51 var level logging.Level 52 var initialized bool 53 var loggersMap map[string]*logging.Logger 54 var fatalErrors []string 55 var fileLogFormat = logging.MustStringFormatter("%{time:02-01-2006 15:04:05.000} [%{module}] [%{level}] %{message}") 56 var fileLoggerLeveled logging.LeveledBackend 57 58 // ActiveLogFile log file represents the file which will be used for the backend logging 59 var ActiveLogFile string 60 var machineReadable bool 61 var isLSP bool 62 63 // Initialize logger with given level 64 func Initialize(mr bool, logLevel string, c int) { 65 machineReadable = mr 66 level = loggingLevel(logLevel) 67 switch c { 68 case CLI: 69 ActiveLogFile = getLogFile(gaugeLogFileName) 70 case API: 71 ActiveLogFile = getLogFile(apiLogFileName) 72 case LSP: 73 isLSP = true 74 ActiveLogFile = getLogFile(lspLogFileName) 75 } 76 initFileLoggerBackend() 77 addLogger(gaugeModuleID) 78 initialized = true 79 } 80 81 // GetLogger gets logger for given modules. It creates a new logger for the module if not exists 82 func GetLogger(module string) *logging.Logger { 83 if module == "" { 84 return loggersMap[gaugeModuleID] 85 } 86 if _, ok := loggersMap[module]; !ok { 87 addLogger(module) 88 } 89 return loggersMap[module] 90 91 } 92 93 func logInfo(logger *logging.Logger, stdout bool, msg string) { 94 if level >= logging.INFO { 95 write(stdout, msg) 96 } 97 if !initialized { 98 return 99 } 100 logger.Infof(msg) 101 } 102 103 func logError(logger *logging.Logger, stdout bool, msg string) { 104 if level >= logging.ERROR { 105 write(stdout, msg) 106 } 107 if !initialized { 108 fmt.Fprintf(os.Stderr, msg) 109 return 110 } 111 logger.Errorf(msg) 112 } 113 114 func logWarning(logger *logging.Logger, stdout bool, msg string) { 115 if level >= logging.WARNING { 116 write(stdout, msg) 117 } 118 if !initialized { 119 return 120 } 121 logger.Warningf(msg) 122 } 123 124 func logDebug(logger *logging.Logger, stdout bool, msg string) { 125 if level >= logging.DEBUG { 126 write(stdout, msg) 127 } 128 if !initialized { 129 return 130 } 131 logger.Debugf(msg) 132 } 133 134 func logCritical(logger *logging.Logger, msg string) { 135 if !initialized { 136 fmt.Fprintf(os.Stderr, msg) 137 return 138 } 139 logger.Criticalf(msg) 140 141 } 142 143 func write(stdout bool, msg string) { 144 if !isLSP && stdout { 145 if machineReadable { 146 machineReadableLog(msg) 147 } else { 148 fmt.Println(msg) 149 } 150 } 151 } 152 153 // OutMessage contains information for output log 154 type OutMessage struct { 155 MessageType string `json:"type"` 156 Message string `json:"message"` 157 } 158 159 // ToJSON converts OutMessage into JSON 160 func (out *OutMessage) ToJSON() (string, error) { 161 json, err := json.Marshal(out) 162 if err != nil { 163 return "", err 164 } 165 return string(json), nil 166 } 167 168 func machineReadableLog(msg string) { 169 strs := strings.Split(msg, "\n") 170 for _, m := range strs { 171 outMessage := &OutMessage{MessageType: "out", Message: m} 172 m, _ = outMessage.ToJSON() 173 fmt.Println(m) 174 } 175 } 176 177 func addLogger(module string) { 178 l := logging.MustGetLogger(module) 179 if loggersMap == nil { 180 loggersMap = make(map[string]*logging.Logger) 181 } 182 l.SetBackend(fileLoggerLeveled) 183 loggersMap[module] = l 184 } 185 186 func initFileLoggerBackend() { 187 var backend logging.Backend 188 backend = createFileLogger(ActiveLogFile, 10) 189 fileFormatter := logging.NewBackendFormatter(backend, fileLogFormat) 190 fileLoggerLeveled = logging.AddModuleLevel(fileFormatter) 191 fileLoggerLeveled.SetLevel(logging.DEBUG, "") 192 } 193 194 func createFileLogger(name string, size int) logging.Backend { 195 return logging.NewLogBackend(&lumberjack.Logger{ 196 Filename: name, 197 MaxSize: size, // megabytes 198 MaxBackups: 3, 199 MaxAge: 28, //days 200 }, "", 0) 201 } 202 203 func addLogsDirPath(logFileName string) string { 204 customLogsDir := os.Getenv(logsDirectory) 205 if customLogsDir == "" { 206 return filepath.Join(logs, logFileName) 207 } 208 return filepath.Join(customLogsDir, logFileName) 209 } 210 211 func getLogFile(logFileName string) string { 212 logDirPath := addLogsDirPath(logFileName) 213 if filepath.IsAbs(logDirPath) { 214 return logDirPath 215 } 216 if config.ProjectRoot != "" { 217 return filepath.Join(config.ProjectRoot, logDirPath) 218 } 219 gaugeHome, err := common.GetGaugeHomeDirectory() 220 if err != nil { 221 return logDirPath 222 } 223 return filepath.Join(gaugeHome, logDirPath) 224 } 225 226 func loggingLevel(logLevel string) logging.Level { 227 if logLevel != "" { 228 switch strings.ToLower(logLevel) { 229 case "debug": 230 return logging.DEBUG 231 case "info": 232 return logging.INFO 233 case "warning": 234 return logging.WARNING 235 case "error": 236 return logging.ERROR 237 } 238 } 239 return logging.INFO 240 } 241 242 func getFatalErrorMsg() string { 243 env := []string{runtime.GOOS, version.FullVersion()} 244 if version.GetCommitHash() != "" { 245 env = append(env, version.GetCommitHash()) 246 } 247 envText := strings.Join(env, ", ") 248 249 return fmt.Sprintf(`Error ---------------------------------- 250 251 %s 252 253 Get Support ---------------------------- 254 Docs: https://docs.gauge.org 255 Bugs: https://github.com/getgauge/gauge/issues 256 Chat: https://gitter.im/getgauge/chat 257 258 Your Environment Information ----------- 259 %s 260 %s`, strings.Join(fatalErrors, "\n\n"), 261 envText, 262 getPluginVersions()) 263 } 264 265 func addFatalError(module, msg string) { 266 msg = strings.TrimSpace(msg) 267 fatalErrors = append(fatalErrors, fmt.Sprintf("[%s]\n%s", module, msg)) 268 } 269 270 func getPluginVersions() string { 271 pis, err := pluginInfo.GetAllInstalledPluginsWithVersion() 272 if err != nil { 273 return fmt.Sprintf("Could not retrieve plugin information.") 274 } 275 pluginVersions := make([]string, 0, 0) 276 for _, pi := range pis { 277 pluginVersions = append(pluginVersions, fmt.Sprintf(`%s (%s)`, pi.Name, pi.Version)) 278 } 279 return strings.Join(pluginVersions, ", ") 280 }