github.com/gogf/gf/v2@v2.7.4/os/glog/glog_logger_handler_structure.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package glog
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"strconv"
    13  	"unicode"
    14  	"unicode/utf8"
    15  
    16  	"github.com/gogf/gf/v2/util/gconv"
    17  )
    18  
    19  type structuredBuffer struct {
    20  	in     *HandlerInput
    21  	buffer *bytes.Buffer
    22  }
    23  
    24  const (
    25  	structureKeyTime       = "Time"
    26  	structureKeyLevel      = "Level"
    27  	structureKeyPrefix     = "Prefix"
    28  	structureKeyContent    = "Content"
    29  	structureKeyTraceId    = "TraceId"
    30  	structureKeyCallerFunc = "CallerFunc"
    31  	structureKeyCallerPath = "CallerPath"
    32  	structureKeyCtxStr     = "CtxStr"
    33  	structureKeyStack      = "Stack"
    34  )
    35  
    36  // Copied from encoding/json/tables.go.
    37  //
    38  // safeSet holds the value true if the ASCII character with the given array
    39  // position can be represented inside a JSON string without any further
    40  // escaping.
    41  //
    42  // All values are true except for the ASCII control characters (0-31), the
    43  // double quote ("), and the backslash character ("\").
    44  var safeSet = [utf8.RuneSelf]bool{
    45  	' ':      true,
    46  	'!':      true,
    47  	'"':      false,
    48  	'#':      true,
    49  	'$':      true,
    50  	'%':      true,
    51  	'&':      true,
    52  	'\'':     true,
    53  	'(':      true,
    54  	')':      true,
    55  	'*':      true,
    56  	'+':      true,
    57  	',':      true,
    58  	'-':      true,
    59  	'.':      true,
    60  	'/':      true,
    61  	'0':      true,
    62  	'1':      true,
    63  	'2':      true,
    64  	'3':      true,
    65  	'4':      true,
    66  	'5':      true,
    67  	'6':      true,
    68  	'7':      true,
    69  	'8':      true,
    70  	'9':      true,
    71  	':':      true,
    72  	';':      true,
    73  	'<':      true,
    74  	'=':      true,
    75  	'>':      true,
    76  	'?':      true,
    77  	'@':      true,
    78  	'A':      true,
    79  	'B':      true,
    80  	'C':      true,
    81  	'D':      true,
    82  	'E':      true,
    83  	'F':      true,
    84  	'G':      true,
    85  	'H':      true,
    86  	'I':      true,
    87  	'J':      true,
    88  	'K':      true,
    89  	'L':      true,
    90  	'M':      true,
    91  	'N':      true,
    92  	'O':      true,
    93  	'P':      true,
    94  	'Q':      true,
    95  	'R':      true,
    96  	'S':      true,
    97  	'T':      true,
    98  	'U':      true,
    99  	'V':      true,
   100  	'W':      true,
   101  	'X':      true,
   102  	'Y':      true,
   103  	'Z':      true,
   104  	'[':      true,
   105  	'\\':     false,
   106  	']':      true,
   107  	'^':      true,
   108  	'_':      true,
   109  	'`':      true,
   110  	'a':      true,
   111  	'b':      true,
   112  	'c':      true,
   113  	'd':      true,
   114  	'e':      true,
   115  	'f':      true,
   116  	'g':      true,
   117  	'h':      true,
   118  	'i':      true,
   119  	'j':      true,
   120  	'k':      true,
   121  	'l':      true,
   122  	'm':      true,
   123  	'n':      true,
   124  	'o':      true,
   125  	'p':      true,
   126  	'q':      true,
   127  	'r':      true,
   128  	's':      true,
   129  	't':      true,
   130  	'u':      true,
   131  	'v':      true,
   132  	'w':      true,
   133  	'x':      true,
   134  	'y':      true,
   135  	'z':      true,
   136  	'{':      true,
   137  	'|':      true,
   138  	'}':      true,
   139  	'~':      true,
   140  	'\u007f': true,
   141  }
   142  
   143  // HandlerStructure is a handler for output logging content as a structured string.
   144  func HandlerStructure(ctx context.Context, in *HandlerInput) {
   145  	s := newStructuredBuffer(in)
   146  	in.Buffer.Write(s.Bytes())
   147  	in.Buffer.Write([]byte("\n"))
   148  	in.Next(ctx)
   149  }
   150  
   151  func newStructuredBuffer(in *HandlerInput) *structuredBuffer {
   152  	return &structuredBuffer{
   153  		in:     in,
   154  		buffer: bytes.NewBuffer(nil),
   155  	}
   156  }
   157  
   158  func (buf *structuredBuffer) Bytes() []byte {
   159  	buf.addValue(structureKeyTime, buf.in.TimeFormat)
   160  	if buf.in.TraceId != "" {
   161  		buf.addValue(structureKeyTraceId, buf.in.TraceId)
   162  	}
   163  	if buf.in.CtxStr != "" {
   164  		buf.addValue(structureKeyCtxStr, buf.in.CtxStr)
   165  	}
   166  	if buf.in.LevelFormat != "" {
   167  		buf.addValue(structureKeyLevel, buf.in.LevelFormat)
   168  	}
   169  	if buf.in.CallerPath != "" {
   170  		buf.addValue(structureKeyCallerPath, buf.in.CallerPath)
   171  	}
   172  	if buf.in.CallerFunc != "" {
   173  		buf.addValue(structureKeyCallerFunc, buf.in.CallerFunc)
   174  	}
   175  	if buf.in.Prefix != "" {
   176  		buf.addValue(structureKeyPrefix, buf.in.Prefix)
   177  	}
   178  	// If the values cannot be the pair, move the first one to content.
   179  	values := buf.in.Values
   180  	if len(values)%2 != 0 {
   181  		if buf.in.Content != "" {
   182  			buf.in.Content += " "
   183  		}
   184  		buf.in.Content += gconv.String(values[0])
   185  		values = values[1:]
   186  	}
   187  	if buf.in.Content != "" {
   188  		buf.addValue(structureKeyContent, buf.in.Content)
   189  	}
   190  	// Values pairs.
   191  	for i := 0; i < len(values); i += 2 {
   192  		buf.addValue(values[i], values[i+1])
   193  	}
   194  	if buf.in.Stack != "" {
   195  		buf.addValue(structureKeyStack, buf.in.Stack)
   196  	}
   197  	contentBytes := buf.buffer.Bytes()
   198  	buf.buffer.Reset()
   199  	contentBytes = bytes.ReplaceAll(contentBytes, []byte{'\n'}, []byte{' '})
   200  	return contentBytes
   201  }
   202  
   203  func (buf *structuredBuffer) addValue(k, v any) {
   204  	var (
   205  		ks = gconv.String(k)
   206  		vs = gconv.String(v)
   207  	)
   208  	if buf.buffer.Len() > 0 {
   209  		buf.buffer.WriteByte(' ')
   210  	}
   211  	buf.appendString(ks)
   212  	buf.buffer.WriteByte('=')
   213  	buf.appendString(vs)
   214  }
   215  
   216  func (buf *structuredBuffer) appendString(s string) {
   217  	if buf.needsQuoting(s) {
   218  		s = strconv.Quote(s)
   219  	}
   220  	buf.buffer.WriteString(s)
   221  }
   222  
   223  func (buf *structuredBuffer) needsQuoting(s string) bool {
   224  	if len(s) == 0 {
   225  		return true
   226  	}
   227  	for i := 0; i < len(s); {
   228  		b := s[i]
   229  		if b < utf8.RuneSelf {
   230  			// Quote anything except a backslash that would need quoting in a
   231  			// JSON string, as well as space and '='
   232  			if b != '\\' && (b == ' ' || b == '=' || !safeSet[b]) {
   233  				return true
   234  			}
   235  			i++
   236  			continue
   237  		}
   238  		r, size := utf8.DecodeRuneInString(s[i:])
   239  		if r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r) {
   240  			return true
   241  		}
   242  		i += size
   243  	}
   244  	return false
   245  }