github.com/oarkflow/log@v1.0.78/tsv.go (about) 1 package log 2 3 import ( 4 "io" 5 "net" 6 "net/netip" 7 "os" 8 "strconv" 9 "sync" 10 ) 11 12 // TSVLogger represents an active logging object that generates lines of TSV output to an io.Writer. 13 type TSVLogger struct { 14 Separator byte 15 Writer io.Writer 16 } 17 18 // TSVEntry represents a tsv log entry. It is instanced by one of TSVLogger and finalized by the Msg method. 19 type TSVEntry struct { 20 buf []byte 21 w io.Writer 22 sep byte 23 } 24 25 var tepool = sync.Pool{ 26 New: func() interface{} { 27 return new(TSVEntry) 28 }, 29 } 30 31 // New starts a new tsv message. 32 func (l *TSVLogger) New() (e *TSVEntry) { 33 e = tepool.Get().(*TSVEntry) 34 e.sep = l.Separator 35 if l.Writer != nil { 36 e.w = l.Writer 37 } else { 38 e.w = os.Stderr 39 } 40 if e.sep == 0 { 41 e.sep = '\t' 42 } 43 e.buf = e.buf[:0] 44 return 45 } 46 47 // Timestamp adds the current time as UNIX timestamp 48 func (e *TSVEntry) Timestamp() *TSVEntry { 49 var tmp [11]byte 50 sec, _, _ := now() 51 // separator 52 tmp[10] = e.sep 53 // seconds 54 is := sec % 100 * 2 55 sec /= 100 56 tmp[9] = smallsString[is+1] 57 tmp[8] = smallsString[is] 58 is = sec % 100 * 2 59 sec /= 100 60 tmp[7] = smallsString[is+1] 61 tmp[6] = smallsString[is] 62 is = sec % 100 * 2 63 sec /= 100 64 tmp[5] = smallsString[is+1] 65 tmp[4] = smallsString[is] 66 is = sec % 100 * 2 67 sec /= 100 68 tmp[3] = smallsString[is+1] 69 tmp[2] = smallsString[is] 70 is = sec % 100 * 2 71 tmp[1] = smallsString[is+1] 72 tmp[0] = smallsString[is] 73 // append to buf 74 e.buf = append(e.buf, tmp[:]...) 75 return e 76 } 77 78 // TimestampMS adds the current time with milliseconds as UNIX timestamp 79 func (e *TSVEntry) TimestampMS() *TSVEntry { 80 var tmp [14]byte 81 sec, nsec, _ := now() 82 // separator 83 tmp[13] = e.sep 84 // milli seconds 85 a := int64(nsec) / 1000000 86 is := a % 100 * 2 87 tmp[12] = smallsString[is+1] 88 tmp[11] = smallsString[is] 89 tmp[10] = byte('0' + a/100) 90 // seconds 91 is = sec % 100 * 2 92 sec /= 100 93 tmp[9] = smallsString[is+1] 94 tmp[8] = smallsString[is] 95 is = sec % 100 * 2 96 sec /= 100 97 tmp[7] = smallsString[is+1] 98 tmp[6] = smallsString[is] 99 is = sec % 100 * 2 100 sec /= 100 101 tmp[5] = smallsString[is+1] 102 tmp[4] = smallsString[is] 103 is = sec % 100 * 2 104 sec /= 100 105 tmp[3] = smallsString[is+1] 106 tmp[2] = smallsString[is] 107 is = sec % 100 * 2 108 tmp[1] = smallsString[is+1] 109 tmp[0] = smallsString[is] 110 // append to buf 111 e.buf = append(e.buf, tmp[:]...) 112 return e 113 } 114 115 // Caller adds the file:line of to the entry. 116 func (e *TSVEntry) Caller(depth int) *TSVEntry { 117 var pc uintptr 118 i := caller1(depth, &pc, 1, 1) 119 if i < 1 { 120 return e 121 } 122 file, line := pcFileLine(pc) 123 for i = len(file) - 1; i >= 0; i-- { 124 if file[i] == '/' { 125 break 126 } 127 } 128 if i > 0 { 129 file = file[i+1:] 130 } 131 e.buf = append(e.buf, file...) 132 e.buf = append(e.buf, ':') 133 e.buf = strconv.AppendInt(e.buf, int64(line), 10) 134 e.buf = append(e.buf, e.sep) 135 return e 136 } 137 138 // Bool append the b as a bool to the entry, the value of output bool is 0 or 1. 139 func (e *TSVEntry) Bool(b bool) *TSVEntry { 140 if b { 141 e.buf = append(e.buf, '1', e.sep) 142 } else { 143 e.buf = append(e.buf, '0', e.sep) 144 } 145 return e 146 } 147 148 // BoolString append the b as a bool to the entry, the value of output bool is false or true. 149 func (e *TSVEntry) BoolString(b bool) *TSVEntry { 150 if b { 151 e.buf = append(e.buf, 't', 'r', 'u', 'e', e.sep) 152 } else { 153 e.buf = append(e.buf, 'f', 'a', 'l', 's', 'e', e.sep) 154 } 155 return e 156 } 157 158 // Byte append the b as a byte to the entry. 159 func (e *TSVEntry) Byte(b byte) *TSVEntry { 160 e.buf = append(e.buf, b, e.sep) 161 return e 162 } 163 164 // Float64 adds a float64 to the entry. 165 func (e *TSVEntry) Float64(f float64) *TSVEntry { 166 e.buf = strconv.AppendFloat(e.buf, f, 'f', -1, 64) 167 e.buf = append(e.buf, e.sep) 168 return e 169 } 170 171 // Int64 adds a int64 to the entry. 172 func (e *TSVEntry) Int64(i int64) *TSVEntry { 173 e.buf = strconv.AppendInt(e.buf, i, 10) 174 e.buf = append(e.buf, e.sep) 175 return e 176 } 177 178 // Uint64 adds a uint64 to the entry. 179 func (e *TSVEntry) Uint64(i uint64) *TSVEntry { 180 e.buf = strconv.AppendUint(e.buf, i, 10) 181 e.buf = append(e.buf, e.sep) 182 return e 183 } 184 185 // Float32 adds a float32 to the entry. 186 func (e *TSVEntry) Float32(f float32) *TSVEntry { 187 return e.Float64(float64(f)) 188 } 189 190 // Int adds a int to the entry. 191 func (e *TSVEntry) Int(i int) *TSVEntry { 192 return e.Int64(int64(i)) 193 } 194 195 // Int32 adds a int32 to the entry. 196 func (e *TSVEntry) Int32(i int32) *TSVEntry { 197 return e.Int64(int64(i)) 198 } 199 200 // Int16 adds a int16 to the entry. 201 func (e *TSVEntry) Int16(i int16) *TSVEntry { 202 return e.Int64(int64(i)) 203 } 204 205 // Int8 adds a int8 to the entry. 206 func (e *TSVEntry) Int8(i int8) *TSVEntry { 207 return e.Int64(int64(i)) 208 } 209 210 // Uint32 adds a uint32 to the entry. 211 func (e *TSVEntry) Uint32(i uint32) *TSVEntry { 212 return e.Uint64(uint64(i)) 213 } 214 215 // Uint16 adds a uint16 to the entry. 216 func (e *TSVEntry) Uint16(i uint16) *TSVEntry { 217 return e.Uint64(uint64(i)) 218 } 219 220 // Uint8 adds a uint8 to the entry. 221 func (e *TSVEntry) Uint8(i uint8) *TSVEntry { 222 return e.Uint64(uint64(i)) 223 } 224 225 // Uint adds a uint to the entry. 226 func (e *TSVEntry) Uint(i uint) *TSVEntry { 227 return e.Uint64(uint64(i)) 228 } 229 230 // Str adds a string to the entry. 231 func (e *TSVEntry) Str(val string) *TSVEntry { 232 e.buf = append(e.buf, val...) 233 e.buf = append(e.buf, e.sep) 234 return e 235 } 236 237 // Bytes adds a bytes as string to the entry. 238 func (e *TSVEntry) Bytes(val []byte) *TSVEntry { 239 e.buf = append(e.buf, val...) 240 e.buf = append(e.buf, e.sep) 241 return e 242 } 243 244 // IPAddr adds IPv4 or IPv6 Address to the entry. 245 func (e *TSVEntry) IPAddr(ip net.IP) *TSVEntry { 246 if ip4 := ip.To4(); ip4 != nil { 247 e.buf = strconv.AppendInt(e.buf, int64(ip4[0]), 10) 248 e.buf = append(e.buf, '.') 249 e.buf = strconv.AppendInt(e.buf, int64(ip4[1]), 10) 250 e.buf = append(e.buf, '.') 251 e.buf = strconv.AppendInt(e.buf, int64(ip4[2]), 10) 252 e.buf = append(e.buf, '.') 253 e.buf = strconv.AppendInt(e.buf, int64(ip4[3]), 10) 254 } else { 255 e.buf = append(e.buf, ip.String()...) 256 } 257 e.buf = append(e.buf, e.sep) 258 return e 259 } 260 261 // NetIPAddr adds IPv4 or IPv6 Address to the entry. 262 func (e *TSVEntry) NetIPAddr(ip netip.Addr) *TSVEntry { 263 e.buf = ip.AppendTo(e.buf) 264 e.buf = append(e.buf, e.sep) 265 return e 266 } 267 268 // NetIPAddrPort adds IPv4 or IPv6 with Port Address to the entry. 269 func (e *TSVEntry) NetIPAddrPort(ipPort netip.AddrPort) *TSVEntry { 270 e.buf = ipPort.AppendTo(e.buf) 271 e.buf = append(e.buf, e.sep) 272 return e 273 } 274 275 // NetIPPrefix adds IPv4 or IPv6 Prefix (address and mask) to the entry. 276 func (e *TSVEntry) NetIPPrefix(pfx netip.Prefix) *TSVEntry { 277 e.buf = pfx.AppendTo(e.buf) 278 e.buf = append(e.buf, e.sep) 279 return e 280 } 281 282 // Encode encodes bytes using enc.AppendEncode to the entry. 283 func (e *TSVEntry) Encode(key string, val []byte, enc interface { 284 AppendEncode(dst, src []byte) []byte 285 }) *TSVEntry { 286 e.buf = enc.AppendEncode(e.buf, val) 287 e.buf = append(e.buf, e.sep) 288 return e 289 } 290 291 // Msg sends the entry. 292 func (e *TSVEntry) Msg() { 293 if len(e.buf) != 0 { 294 e.buf[len(e.buf)-1] = '\n' 295 } 296 _, _ = e.w.Write(e.buf) 297 if cap(e.buf) <= bbcap { 298 tepool.Put(e) 299 } 300 }