github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/infra/logid/logid_v2.go (about) 1 package logid 2 3 import ( 4 "fmt" 5 "net" 6 "time" 7 8 "github.com/jxskiss/gopkg/v2/internal/unsafeheader" 9 ) 10 11 var _ Generator = &V2Gen{} 12 var _ V2Info = &v2Info{} 13 14 // IPUnknown represents unknown IP address. 15 const IPUnknown = "0000000" 16 17 const ( 18 v2Version = '2' 19 v2IPv4Length = 27 20 v2IPv6Length = 46 21 ) 22 23 // NewV2Gen creates a new v2 log ID generator. 24 func NewV2Gen(ip net.IP) *V2Gen { 25 ipStr := IPUnknown 26 if v4 := ip.To4(); len(v4) > 0 { 27 ipStr = b32Enc.EncodeToString(v4) 28 } else if len(ip) > 0 { 29 ipStr = b32Enc.EncodeToString(ip) 30 } 31 return &V2Gen{ 32 ipStr: ipStr, 33 } 34 } 35 36 // V2Gen is a v2 log ID generator. 37 // 38 // A v2 log ID is consisted by the following parts: 39 // 40 // - 9 bytes milli timestamp, in base32 form 41 // - 7 bytes IPv4 address, or 26 bytes IPv6 address, in base32 form 42 // - 10 bytes random data 43 // - 1 byte version flag "2" 44 // 45 // e.g. 46 // - "1HMZ5YAD6041061072VFVV7C2J2" 47 // - "1HMZ5YAD6ZPYXR0802R01C00000000000JGCBDDWZEJH42" 48 type V2Gen struct { 49 ipStr string 50 } 51 52 // Gen generates a new log ID string. 53 func (p *V2Gen) Gen() string { 54 buf := make([]byte, 20+len(p.ipStr)) 55 buf[len(buf)-1] = v2Version 56 57 // milli timestamp, fixed length, 9 bytes 58 encodeBase32(buf[0:9], time.Now().UnixMilli()) 59 60 // random bytes, fixed length, 10 bytes 61 randNum := rand50bits() 62 encodeBase32(buf[len(buf)-11:len(buf)-1], randNum) 63 64 // ip address, 7 bytes for IPv4 or 26 bytes for IPv6 65 copy(buf[9:], p.ipStr) 66 67 return unsafeheader.BytesToString(buf) 68 } 69 70 func decodeV2Info(s string) (info *v2Info) { 71 info = &v2Info{} 72 if len(s) != v2IPv4Length && len(s) != v2IPv6Length { 73 return 74 } 75 r := s[len(s)-11 : len(s)-1] 76 tMsec, err := decodeBase32(s[:9]) 77 if err != nil { 78 return 79 } 80 ip, err := b32Enc.DecodeString(s[9 : len(s)-11]) 81 if err != nil { 82 return 83 } 84 *info = v2Info{ 85 valid: true, 86 time: time.UnixMilli(tMsec), 87 ip: ip, 88 random: r, 89 } 90 return 91 } 92 93 // V2Info holds parsed information of a v2 log ID string. 94 type V2Info interface { 95 Info 96 Time() time.Time 97 IP() net.IP 98 Random() string 99 } 100 101 type v2Info struct { 102 valid bool 103 time time.Time 104 ip net.IP 105 random string 106 } 107 108 func (info *v2Info) Valid() bool { return info != nil && info.valid } 109 func (info *v2Info) Version() byte { return v2Version } 110 func (info *v2Info) Time() time.Time { return info.time } 111 func (info *v2Info) IP() net.IP { return info.ip } 112 func (info *v2Info) Random() string { return info.random } 113 114 func (info *v2Info) String() string { 115 if !info.Valid() { 116 return "2|invalid" 117 } 118 return fmt.Sprintf("2|%s|%s|%s", formatTime(info.time), info.ip.String(), info.random) 119 }