github.com/keakon/golog@v0.0.0-20230330091222-cac71197c18d/formatter.go (about)

     1  package golog
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  )
     8  
     9  var unknownFile = []byte("???")
    10  
    11  var (
    12  	// DefaultFormatter is the default formatter
    13  	DefaultFormatter = ParseFormat("[%l %D %T %s] %m")
    14  	// TimedRotatingFormatter is a formatter for TimedRotatingFileWriter
    15  	TimedRotatingFormatter = ParseFormat("[%l %T %s] %m")
    16  )
    17  
    18  // A Formatter containing a sequence of FormatParts.
    19  type Formatter struct {
    20  	formatParts []FormatPart
    21  }
    22  
    23  // ParseFormat parses a format string into a formatter.
    24  func ParseFormat(format string) (formatter *Formatter) {
    25  	if format == "" {
    26  		return
    27  	}
    28  	formatter = &Formatter{}
    29  	formatter.findParts([]byte(format))
    30  	formatter.appendByte('\n')
    31  	return
    32  }
    33  
    34  /*
    35  Format formats a record to a bytes.Buffer.
    36  Supported format directives:
    37  
    38  	%%: %
    39  	%l: short name of the level
    40  	%T: time string (HH:MM:SS)
    41  	%D: date string (YYYY-mm-DD)
    42  	%s: source code string (filename:line)
    43  	%S: full source code string (/path/filename.go:line)
    44  */
    45  func (f *Formatter) Format(r *Record, buf *bytes.Buffer) {
    46  	for _, part := range f.formatParts {
    47  		part.Format(r, buf)
    48  	}
    49  }
    50  
    51  func (f *Formatter) findParts(format []byte) {
    52  	length := len(format)
    53  	index := bytes.IndexByte(format, '%')
    54  	if index == -1 || index == length-1 {
    55  		if length == 0 {
    56  			return
    57  		}
    58  		if length == 1 {
    59  			f.appendByte(format[0])
    60  		} else {
    61  			f.appendBytes(format)
    62  		}
    63  		return
    64  	}
    65  
    66  	if index > 1 {
    67  		f.appendBytes(format[:index])
    68  	} else if index == 1 {
    69  		f.appendByte(format[0])
    70  	}
    71  	switch c := format[index+1]; c {
    72  	case '%':
    73  		f.appendByte('%')
    74  	case 'l':
    75  		f.formatParts = append(f.formatParts, &LevelFormatPart{})
    76  	case 'T':
    77  		f.formatParts = append(f.formatParts, &TimeFormatPart{})
    78  	case 'D':
    79  		f.formatParts = append(f.formatParts, &DateFormatPart{})
    80  	case 's':
    81  		f.formatParts = append(f.formatParts, &SourceFormatPart{})
    82  	case 'S':
    83  		f.formatParts = append(f.formatParts, &FullSourceFormatPart{})
    84  	case 'm':
    85  		f.formatParts = append(f.formatParts, &MessageFormatPart{})
    86  	default:
    87  		f.appendBytes([]byte{'%', c})
    88  	}
    89  	f.findParts(format[index+2:])
    90  	return
    91  }
    92  
    93  // FormatPart is an interface containing the Format() method.
    94  type FormatPart interface {
    95  	Format(r *Record, buf *bytes.Buffer)
    96  }
    97  
    98  // ByteFormatPart is a FormatPart containing a byte.
    99  type ByteFormatPart struct {
   100  	byte byte
   101  }
   102  
   103  // Format writes its byte to the buf.
   104  func (p *ByteFormatPart) Format(r *Record, buf *bytes.Buffer) {
   105  	buf.WriteByte(p.byte)
   106  }
   107  
   108  // appendByte appends a byte to the formatter.
   109  // If the previous FormatPart is a ByteFormatPart or BytesFormatPart, they will be merged into a BytesFormatPart;
   110  // otherwise a new ByteFormatPart will be created.
   111  func (f *Formatter) appendByte(b byte) {
   112  	parts := f.formatParts
   113  	count := len(parts)
   114  	if count == 0 {
   115  		f.formatParts = append(parts, &ByteFormatPart{byte: b})
   116  	} else {
   117  		var p FormatPart
   118  		lastPart := parts[count-1]
   119  		switch lp := lastPart.(type) {
   120  		case *ByteFormatPart:
   121  			p = &BytesFormatPart{
   122  				bytes: []byte{lp.byte, b},
   123  			}
   124  		case *BytesFormatPart:
   125  			p = &BytesFormatPart{
   126  				bytes: append(lp.bytes, b),
   127  			}
   128  		default:
   129  			p = &ByteFormatPart{byte: b}
   130  			f.formatParts = append(parts, p)
   131  			return
   132  		}
   133  		f.formatParts[count-1] = p
   134  	}
   135  }
   136  
   137  // BytesFormatPart is a FormatPart containing a byte slice.
   138  type BytesFormatPart struct {
   139  	bytes []byte
   140  }
   141  
   142  // Format writes its bytes to the buf.
   143  func (p *BytesFormatPart) Format(r *Record, buf *bytes.Buffer) {
   144  	buf.Write(p.bytes)
   145  }
   146  
   147  // appendBytes appends a byte slice to the formatter.
   148  // If the previous FormatPart is a ByteFormatPart or BytesFormatPart, they will be merged into a BytesFormatPart;
   149  // otherwise a new BytesFormatPart will be created.
   150  func (f *Formatter) appendBytes(bs []byte) {
   151  	parts := f.formatParts
   152  	count := len(parts)
   153  	if count == 0 {
   154  		f.formatParts = append(parts, &BytesFormatPart{bytes: bs})
   155  	} else {
   156  		var p FormatPart
   157  		lastPart := parts[count-1]
   158  		switch lp := lastPart.(type) {
   159  		case *ByteFormatPart: // won't reach here
   160  			p = &BytesFormatPart{
   161  				bytes: append([]byte{lp.byte}, bs...),
   162  			}
   163  		case *BytesFormatPart:
   164  			p = &BytesFormatPart{
   165  				bytes: append(lp.bytes, bs...),
   166  			}
   167  		default:
   168  			p = &BytesFormatPart{bytes: bs}
   169  			f.formatParts = append(parts, p)
   170  			return
   171  		}
   172  		f.formatParts[count-1] = p
   173  	}
   174  }
   175  
   176  // LevelFormatPart is a FormatPart of the level placeholder.
   177  type LevelFormatPart struct{}
   178  
   179  // Format writes the short level name of the record to the buf.
   180  func (p *LevelFormatPart) Format(r *Record, buf *bytes.Buffer) {
   181  	buf.WriteByte(levelNames[int(r.level)])
   182  }
   183  
   184  // TimeFormatPart is a FormatPart of the time placeholder.
   185  type TimeFormatPart struct{}
   186  
   187  // Format writes the time string of the record to the buf.
   188  func (p *TimeFormatPart) Format(r *Record, buf *bytes.Buffer) {
   189  	if r.time == "" {
   190  		hour, min, sec := r.tm.Clock()
   191  		buf.Write(uint2Bytes2(hour))
   192  		buf.WriteByte(':')
   193  		buf.Write(uint2Bytes2(min))
   194  		buf.WriteByte(':')
   195  		buf.Write(uint2Bytes2(sec))
   196  	} else {
   197  		buf.WriteString(r.time)
   198  	}
   199  }
   200  
   201  // DateFormatPart is a FormatPart of the date placeholder.
   202  type DateFormatPart struct{}
   203  
   204  // Format writes the date string of the record to the buf.
   205  func (p *DateFormatPart) Format(r *Record, buf *bytes.Buffer) {
   206  	if r.date == "" {
   207  		year, mon, day := r.tm.Date()
   208  		buf.Write(uint2Bytes4(year))
   209  		buf.WriteByte('-')
   210  		buf.Write(uint2Bytes2(int(mon)))
   211  		buf.WriteByte('-')
   212  		buf.Write(uint2Bytes2(day))
   213  	} else {
   214  		buf.WriteString(r.date)
   215  	}
   216  }
   217  
   218  // SourceFormatPart is a FormatPart of the source code placeholder.
   219  type SourceFormatPart struct{}
   220  
   221  // Format writes the source file name and line number of the record to the buf.
   222  func (p *SourceFormatPart) Format(r *Record, buf *bytes.Buffer) {
   223  	if r.line > 0 {
   224  		length := len(r.file)
   225  		if length > 0 {
   226  			start := 0
   227  			end := length
   228  			for i := length - 1; i >= 0; i-- {
   229  				c := r.file[i]
   230  				if os.IsPathSeparator(c) {
   231  					start = i + 1
   232  					break
   233  				} else if c == '.' {
   234  					end = i
   235  				}
   236  			}
   237  			buf.WriteString(r.file[start:end])
   238  			buf.WriteByte(':')
   239  			buf.Write(fastUint2DynamicBytes(r.line))
   240  			return
   241  		}
   242  	}
   243  	buf.Write(unknownFile)
   244  }
   245  
   246  // FullSourceFormatPart is a FormatPart of the full source code placeholder.
   247  type FullSourceFormatPart struct{}
   248  
   249  // Format writes the source file path and line number of the record to the buf.
   250  func (p *FullSourceFormatPart) Format(r *Record, buf *bytes.Buffer) {
   251  	if r.line > 0 {
   252  		buf.WriteString(r.file)
   253  		buf.WriteByte(':')
   254  		buf.Write(fastUint2DynamicBytes(r.line))
   255  	} else {
   256  		buf.Write(unknownFile)
   257  	}
   258  }
   259  
   260  // MessageFormatPart is a FormatPart of the message placeholder.
   261  type MessageFormatPart struct{}
   262  
   263  // Format writes the formatted message with args to the buf.
   264  func (p *MessageFormatPart) Format(r *Record, buf *bytes.Buffer) {
   265  	if len(r.args) > 0 {
   266  		if r.message == "" {
   267  			fmt.Fprint(buf, r.args...)
   268  		} else {
   269  			fmt.Fprintf(buf, r.message, r.args...)
   270  		}
   271  	} else if r.message != "" {
   272  		buf.WriteString(r.message)
   273  	}
   274  }