github.com/ipfans/trojan-go@v0.11.0/log/golog/golog.go (about) 1 // The colorful and simple logging library 2 // Copyright (c) 2017 Fadhli Dzil Ikram 3 4 package golog 5 6 import ( 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "runtime" 12 "sync" 13 "sync/atomic" 14 "time" 15 16 terminal "golang.org/x/term" 17 18 "github.com/ipfans/trojan-go/log" 19 "github.com/ipfans/trojan-go/log/golog/colorful" 20 ) 21 22 func init() { 23 log.RegisterLogger(New(os.Stdout)) 24 } 25 26 // FdWriter interface extends existing io.Writer with file descriptor function 27 // support 28 type FdWriter interface { 29 io.Writer 30 Fd() uintptr 31 } 32 33 // Logger struct define the underlying storage for single logger 34 type Logger struct { 35 mu sync.RWMutex 36 color bool 37 out io.Writer 38 debug bool 39 timestamp bool 40 quiet bool 41 buf colorful.ColorBuffer 42 logLevel int32 43 } 44 45 // Prefix struct define plain and color byte 46 type Prefix struct { 47 Plain []byte 48 Color []byte 49 File bool 50 } 51 52 var ( 53 // Plain prefix template 54 plainFatal = []byte("[FATAL] ") 55 plainError = []byte("[ERROR] ") 56 plainWarn = []byte("[WARN] ") 57 plainInfo = []byte("[INFO] ") 58 plainDebug = []byte("[DEBUG] ") 59 plainTrace = []byte("[TRACE] ") 60 61 // FatalPrefix show fatal prefix 62 FatalPrefix = Prefix{ 63 Plain: plainFatal, 64 Color: colorful.Red(plainFatal), 65 File: true, 66 } 67 68 // ErrorPrefix show error prefix 69 ErrorPrefix = Prefix{ 70 Plain: plainError, 71 Color: colorful.Red(plainError), 72 File: true, 73 } 74 75 // WarnPrefix show warn prefix 76 WarnPrefix = Prefix{ 77 Plain: plainWarn, 78 Color: colorful.Orange(plainWarn), 79 } 80 81 // InfoPrefix show info prefix 82 InfoPrefix = Prefix{ 83 Plain: plainInfo, 84 Color: colorful.Green(plainInfo), 85 } 86 87 // DebugPrefix show info prefix 88 DebugPrefix = Prefix{ 89 Plain: plainDebug, 90 Color: colorful.Purple(plainDebug), 91 File: true, 92 } 93 94 // TracePrefix show info prefix 95 TracePrefix = Prefix{ 96 Plain: plainTrace, 97 Color: colorful.Cyan(plainTrace), 98 } 99 ) 100 101 // New returns new Logger instance with predefined writer output and 102 // automatically detect terminal coloring support 103 func New(out FdWriter) *Logger { 104 return &Logger{ 105 color: terminal.IsTerminal(int(out.Fd())), 106 out: out, 107 timestamp: true, 108 } 109 } 110 111 func (l *Logger) SetLogLevel(level log.LogLevel) { 112 l.mu.Lock() 113 defer l.mu.Unlock() 114 atomic.StoreInt32(&l.logLevel, int32(level)) 115 } 116 117 func (l *Logger) SetOutput(w io.Writer) { 118 l.mu.Lock() 119 defer l.mu.Unlock() 120 l.color = false 121 if fdw, ok := w.(FdWriter); ok { 122 l.color = terminal.IsTerminal(int(fdw.Fd())) 123 } 124 l.out = w 125 } 126 127 // WithColor explicitly turn on colorful features on the log 128 func (l *Logger) WithColor() *Logger { 129 l.mu.Lock() 130 defer l.mu.Unlock() 131 l.color = true 132 return l 133 } 134 135 // WithoutColor explicitly turn off colorful features on the log 136 func (l *Logger) WithoutColor() *Logger { 137 l.mu.Lock() 138 defer l.mu.Unlock() 139 l.color = false 140 return l 141 } 142 143 // WithDebug turn on debugging output on the log to reveal debug and trace level 144 func (l *Logger) WithDebug() *Logger { 145 l.mu.Lock() 146 defer l.mu.Unlock() 147 l.debug = true 148 return l 149 } 150 151 // WithoutDebug turn off debugging output on the log 152 func (l *Logger) WithoutDebug() *Logger { 153 l.mu.Lock() 154 defer l.mu.Unlock() 155 l.debug = false 156 return l 157 } 158 159 // IsDebug check the state of debugging output 160 func (l *Logger) IsDebug() bool { 161 l.mu.RLock() 162 defer l.mu.RUnlock() 163 return l.debug 164 } 165 166 // WithTimestamp turn on timestamp output on the log 167 func (l *Logger) WithTimestamp() *Logger { 168 l.mu.Lock() 169 defer l.mu.Unlock() 170 l.timestamp = true 171 return l 172 } 173 174 // WithoutTimestamp turn off timestamp output on the log 175 func (l *Logger) WithoutTimestamp() *Logger { 176 l.mu.Lock() 177 defer l.mu.Unlock() 178 l.timestamp = false 179 return l 180 } 181 182 // Quiet turn off all log output 183 func (l *Logger) Quiet() *Logger { 184 l.mu.Lock() 185 defer l.mu.Unlock() 186 l.quiet = true 187 return l 188 } 189 190 // NoQuiet turn on all log output 191 func (l *Logger) NoQuiet() *Logger { 192 l.mu.Lock() 193 defer l.mu.Unlock() 194 l.quiet = false 195 return l 196 } 197 198 // IsQuiet check for quiet state 199 func (l *Logger) IsQuiet() bool { 200 l.mu.RLock() 201 defer l.mu.RUnlock() 202 return l.quiet 203 } 204 205 // Output print the actual value 206 func (l *Logger) Output(depth int, prefix Prefix, data string) error { 207 // Check if quiet is requested, and try to return no error and be quiet 208 if l.IsQuiet() { 209 return nil 210 } 211 // Get current time 212 now := time.Now() 213 // Temporary storage for file and line tracing 214 var file string 215 var line int 216 var fn string 217 // Check if the specified prefix needs to be included with file logging 218 if prefix.File { 219 var ok bool 220 var pc uintptr 221 222 // Get the caller filename and line 223 if pc, file, line, ok = runtime.Caller(depth + 2); !ok { 224 file = "<unknown file>" 225 fn = "<unknown function>" 226 line = 0 227 } else { 228 file = filepath.Base(file) 229 fn = runtime.FuncForPC(pc).Name() 230 } 231 } 232 // Acquire exclusive access to the shared buffer 233 l.mu.Lock() 234 defer l.mu.Unlock() 235 // Reset buffer so it start from the beginning 236 l.buf.Reset() 237 // Write prefix to the buffer 238 if l.color { 239 l.buf.Append(prefix.Color) 240 } else { 241 l.buf.Append(prefix.Plain) 242 } 243 // Check if the log require timestamping 244 if l.timestamp { 245 // Print timestamp color if color enabled 246 if l.color { 247 l.buf.Blue() 248 } 249 // Print date and time 250 year, month, day := now.Date() 251 l.buf.AppendInt(year, 4) 252 l.buf.AppendByte('/') 253 l.buf.AppendInt(int(month), 2) 254 l.buf.AppendByte('/') 255 l.buf.AppendInt(day, 2) 256 l.buf.AppendByte(' ') 257 hour, min, sec := now.Clock() 258 l.buf.AppendInt(hour, 2) 259 l.buf.AppendByte(':') 260 l.buf.AppendInt(min, 2) 261 l.buf.AppendByte(':') 262 l.buf.AppendInt(sec, 2) 263 l.buf.AppendByte(' ') 264 // Print reset color if color enabled 265 if l.color { 266 l.buf.Off() 267 } 268 } 269 // Add caller filename and line if enabled 270 if prefix.File { 271 // Print color start if enabled 272 if l.color { 273 l.buf.Orange() 274 } 275 // Print filename and line 276 l.buf.Append([]byte(fn)) 277 l.buf.AppendByte(':') 278 l.buf.Append([]byte(file)) 279 l.buf.AppendByte(':') 280 l.buf.AppendInt(line, 0) 281 l.buf.AppendByte(' ') 282 // Print color stop 283 if l.color { 284 l.buf.Off() 285 } 286 } 287 // Print the actual string data from caller 288 l.buf.Append([]byte(data)) 289 if len(data) == 0 || data[len(data)-1] != '\n' { 290 l.buf.AppendByte('\n') 291 } 292 // Flush buffer to output 293 _, err := l.out.Write(l.buf.Buffer) 294 return err 295 } 296 297 // Fatal print fatal message to output and quit the application with status 1 298 func (l *Logger) Fatal(v ...interface{}) { 299 if atomic.LoadInt32(&l.logLevel) <= 4 { 300 l.Output(1, FatalPrefix, fmt.Sprintln(v...)) 301 } 302 os.Exit(1) 303 } 304 305 // Fatalf print formatted fatal message to output and quit the application 306 // with status 1 307 func (l *Logger) Fatalf(format string, v ...interface{}) { 308 if atomic.LoadInt32(&l.logLevel) <= 4 { 309 l.Output(1, FatalPrefix, fmt.Sprintf(format, v...)) 310 } 311 os.Exit(1) 312 } 313 314 // Error print error message to output 315 func (l *Logger) Error(v ...interface{}) { 316 if atomic.LoadInt32(&l.logLevel) <= 3 { 317 l.Output(1, ErrorPrefix, fmt.Sprintln(v...)) 318 } 319 } 320 321 // Errorf print formatted error message to output 322 func (l *Logger) Errorf(format string, v ...interface{}) { 323 if atomic.LoadInt32(&l.logLevel) <= 3 { 324 l.Output(1, ErrorPrefix, fmt.Sprintf(format, v...)) 325 } 326 } 327 328 // Warn print warning message to output 329 func (l *Logger) Warn(v ...interface{}) { 330 if atomic.LoadInt32(&l.logLevel) <= 2 { 331 l.Output(1, WarnPrefix, fmt.Sprintln(v...)) 332 } 333 } 334 335 // Warnf print formatted warning message to output 336 func (l *Logger) Warnf(format string, v ...interface{}) { 337 if atomic.LoadInt32(&l.logLevel) <= 2 { 338 l.Output(1, WarnPrefix, fmt.Sprintf(format, v...)) 339 } 340 } 341 342 // Info print informational message to output 343 func (l *Logger) Info(v ...interface{}) { 344 if atomic.LoadInt32(&l.logLevel) <= 1 { 345 l.Output(1, InfoPrefix, fmt.Sprintln(v...)) 346 } 347 } 348 349 // Infof print formatted informational message to output 350 func (l *Logger) Infof(format string, v ...interface{}) { 351 if atomic.LoadInt32(&l.logLevel) <= 1 { 352 l.Output(1, InfoPrefix, fmt.Sprintf(format, v...)) 353 } 354 } 355 356 // Debug print debug message to output if debug output enabled 357 func (l *Logger) Debug(v ...interface{}) { 358 if atomic.LoadInt32(&l.logLevel) == 0 { 359 l.Output(1, DebugPrefix, fmt.Sprintln(v...)) 360 } 361 } 362 363 // Debugf print formatted debug message to output if debug output enabled 364 func (l *Logger) Debugf(format string, v ...interface{}) { 365 if atomic.LoadInt32(&l.logLevel) == 0 { 366 l.Output(1, DebugPrefix, fmt.Sprintf(format, v...)) 367 } 368 } 369 370 // Trace print trace message to output if debug output enabled 371 func (l *Logger) Trace(v ...interface{}) { 372 if atomic.LoadInt32(&l.logLevel) == 0 { 373 l.Output(1, TracePrefix, fmt.Sprintln(v...)) 374 } 375 } 376 377 // Tracef print formatted trace message to output if debug output enabled 378 func (l *Logger) Tracef(format string, v ...interface{}) { 379 if atomic.LoadInt32(&l.logLevel) == 0 { 380 l.Output(1, TracePrefix, fmt.Sprintf(format, v...)) 381 } 382 }