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  }