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 }