github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/log/writer.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package log
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"regexp"
    13  	"strings"
    14  	"sync"
    15  )
    16  
    17  type byteArrayWriter []byte
    18  
    19  func (b *byteArrayWriter) Write(p []byte) (int, error) {
    20  	*b = append(*b, p...)
    21  	return len(p), nil
    22  }
    23  
    24  // WriterLogger represent a basic logger for GitBundle
    25  type WriterLogger struct {
    26  	out io.WriteCloser
    27  	mu  sync.Mutex
    28  
    29  	Level           Level  `json:"level"`
    30  	StacktraceLevel Level  `json:"stacktraceLevel"`
    31  	Flags           int    `json:"flags"`
    32  	Prefix          string `json:"prefix"`
    33  	Colorize        bool   `json:"colorize"`
    34  	Expression      string `json:"expression"`
    35  	regexp          *regexp.Regexp
    36  }
    37  
    38  // NewWriterLogger creates a new WriterLogger from the provided WriteCloser.
    39  // Optionally the level can be changed at the same time.
    40  func (logger *WriterLogger) NewWriterLogger(out io.WriteCloser, level ...Level) {
    41  	logger.mu.Lock()
    42  	defer logger.mu.Unlock()
    43  	logger.out = out
    44  	switch logger.Flags {
    45  	case 0:
    46  		logger.Flags = LstdFlags
    47  	case -1:
    48  		logger.Flags = 0
    49  	}
    50  	if len(level) > 0 {
    51  		logger.Level = level[0]
    52  	}
    53  	logger.createExpression()
    54  }
    55  
    56  func (logger *WriterLogger) createExpression() {
    57  	if len(logger.Expression) > 0 {
    58  		var err error
    59  		logger.regexp, err = regexp.Compile(logger.Expression)
    60  		if err != nil {
    61  			logger.regexp = nil
    62  		}
    63  	}
    64  }
    65  
    66  // GetLevel returns the logging level for this logger
    67  func (logger *WriterLogger) GetLevel() Level {
    68  	return logger.Level
    69  }
    70  
    71  // GetStacktraceLevel returns the stacktrace logging level for this logger
    72  func (logger *WriterLogger) GetStacktraceLevel() Level {
    73  	return logger.StacktraceLevel
    74  }
    75  
    76  // Copy of cheap integer to fixed-width decimal to ascii from logger.
    77  func itoa(buf *[]byte, i, wid int) {
    78  	var logger [20]byte
    79  	bp := len(logger) - 1
    80  	for i >= 10 || wid > 1 {
    81  		wid--
    82  		q := i / 10
    83  		logger[bp] = byte('0' + i - q*10)
    84  		bp--
    85  		i = q
    86  	}
    87  	// i < 10
    88  	logger[bp] = byte('0' + i)
    89  	*buf = append(*buf, logger[bp:]...)
    90  }
    91  
    92  func (logger *WriterLogger) createMsg(buf *[]byte, event *Event) {
    93  	*buf = append(*buf, logger.Prefix...)
    94  	t := event.time
    95  	if logger.Flags&(Ldate|Ltime|Lmicroseconds) != 0 {
    96  		if logger.Colorize {
    97  			*buf = append(*buf, fgCyanBytes...)
    98  		}
    99  		if logger.Flags&LUTC != 0 {
   100  			t = t.UTC()
   101  		}
   102  		if logger.Flags&Ldate != 0 {
   103  			year, month, day := t.Date()
   104  			itoa(buf, year, 4)
   105  			*buf = append(*buf, '/')
   106  			itoa(buf, int(month), 2)
   107  			*buf = append(*buf, '/')
   108  			itoa(buf, day, 2)
   109  			*buf = append(*buf, ' ')
   110  		}
   111  		if logger.Flags&(Ltime|Lmicroseconds) != 0 {
   112  			hour, min, sec := t.Clock()
   113  			itoa(buf, hour, 2)
   114  			*buf = append(*buf, ':')
   115  			itoa(buf, min, 2)
   116  			*buf = append(*buf, ':')
   117  			itoa(buf, sec, 2)
   118  			if logger.Flags&Lmicroseconds != 0 {
   119  				*buf = append(*buf, '.')
   120  				itoa(buf, t.Nanosecond()/1e3, 6)
   121  			}
   122  			*buf = append(*buf, ' ')
   123  		}
   124  		if logger.Colorize {
   125  			*buf = append(*buf, resetBytes...)
   126  		}
   127  
   128  	}
   129  	if logger.Flags&(Lshortfile|Llongfile) != 0 {
   130  		if logger.Colorize {
   131  			*buf = append(*buf, fgGreenBytes...)
   132  		}
   133  		file := event.filename
   134  		if logger.Flags&Lmedfile == Lmedfile {
   135  			startIndex := len(file) - 20
   136  			if startIndex > 0 {
   137  				file = "..." + file[startIndex:]
   138  			}
   139  		} else if logger.Flags&Lshortfile != 0 {
   140  			startIndex := strings.LastIndexByte(file, '/')
   141  			if startIndex > 0 && startIndex < len(file) {
   142  				file = file[startIndex+1:]
   143  			}
   144  		}
   145  		*buf = append(*buf, file...)
   146  		*buf = append(*buf, ':')
   147  		itoa(buf, event.line, -1)
   148  		if logger.Flags&(Lfuncname|Lshortfuncname) != 0 {
   149  			*buf = append(*buf, ':')
   150  		} else {
   151  			if logger.Colorize {
   152  				*buf = append(*buf, resetBytes...)
   153  			}
   154  			*buf = append(*buf, ' ')
   155  		}
   156  	}
   157  	if logger.Flags&(Lfuncname|Lshortfuncname) != 0 {
   158  		if logger.Colorize {
   159  			*buf = append(*buf, fgGreenBytes...)
   160  		}
   161  		funcname := event.caller
   162  		if logger.Flags&Lshortfuncname != 0 {
   163  			lastIndex := strings.LastIndexByte(funcname, '.')
   164  			if lastIndex > 0 && len(funcname) > lastIndex+1 {
   165  				funcname = funcname[lastIndex+1:]
   166  			}
   167  		}
   168  		*buf = append(*buf, funcname...)
   169  		if logger.Colorize {
   170  			*buf = append(*buf, resetBytes...)
   171  		}
   172  		*buf = append(*buf, ' ')
   173  
   174  	}
   175  	if logger.Flags&(Llevel|Llevelinitial) != 0 {
   176  		// level := strings.ToUpper(event.level.String())
   177  		if logger.Colorize {
   178  			*buf = append(*buf, levelToColor[event.level]...)
   179  		}
   180  		*buf = append(*buf, '[')
   181  		*buf = append(*buf, event.level.Icon()...)
   182  		// if logger.Flags&Llevelinitial != 0 {
   183  		// 	*buf = append(*buf, level[0])
   184  		// } else {
   185  		// 	*buf = append(*buf, level...)
   186  		// }
   187  		*buf = append(*buf, ']')
   188  		if logger.Colorize {
   189  			*buf = append(*buf, resetBytes...)
   190  		}
   191  		*buf = append(*buf, ' ')
   192  	}
   193  
   194  	msg := []byte(event.msg)
   195  	if len(msg) > 0 && msg[len(msg)-1] == '\n' {
   196  		msg = msg[:len(msg)-1]
   197  	}
   198  
   199  	pawMode := allowColor
   200  	if !logger.Colorize {
   201  		pawMode = removeColor
   202  	}
   203  
   204  	baw := byteArrayWriter(*buf)
   205  	(&protectedANSIWriter{
   206  		w:    &baw,
   207  		mode: pawMode,
   208  	}).Write(msg)
   209  	*buf = baw
   210  
   211  	if event.stacktrace != "" && logger.StacktraceLevel <= event.level {
   212  		lines := bytes.Split([]byte(event.stacktrace), []byte("\n"))
   213  		if len(lines) > 1 {
   214  			for _, line := range lines {
   215  				*buf = append(*buf, "\n\t"...)
   216  				*buf = append(*buf, line...)
   217  			}
   218  		}
   219  		*buf = append(*buf, '\n')
   220  	}
   221  	*buf = append(*buf, '\n')
   222  }
   223  
   224  // LogEvent logs the event to the internal writer
   225  func (logger *WriterLogger) LogEvent(event *Event) error {
   226  	if logger.Level > event.level {
   227  		return nil
   228  	}
   229  
   230  	logger.mu.Lock()
   231  	defer logger.mu.Unlock()
   232  	if !logger.Match(event) {
   233  		return nil
   234  	}
   235  	var buf []byte
   236  	logger.createMsg(&buf, event)
   237  	_, err := logger.out.Write(buf)
   238  	return err
   239  }
   240  
   241  // Match checks if the given event matches the logger's regexp expression
   242  func (logger *WriterLogger) Match(event *Event) bool {
   243  	if logger.regexp == nil {
   244  		return true
   245  	}
   246  	if logger.regexp.Match(
   247  		[]byte(fmt.Sprintf("%s:%d:%s", event.filename, event.line, event.caller)),
   248  	) {
   249  		return true
   250  	}
   251  	// Match on the non-colored msg - therefore strip out colors
   252  	var msg []byte
   253  	baw := byteArrayWriter(msg)
   254  	(&protectedANSIWriter{
   255  		w:    &baw,
   256  		mode: removeColor,
   257  	}).Write([]byte(event.msg))
   258  	msg = baw
   259  	return logger.regexp.Match(msg)
   260  }
   261  
   262  // Close the base logger
   263  func (logger *WriterLogger) Close() {
   264  	logger.mu.Lock()
   265  	defer logger.mu.Unlock()
   266  	if logger.out != nil {
   267  		logger.out.Close()
   268  	}
   269  }
   270  
   271  // GetName returns empty for these provider loggers
   272  func (logger *WriterLogger) GetName() string {
   273  	return ""
   274  }