github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/infra/logid/logid_v1.go (about) 1 package logid 2 3 import ( 4 "crypto/md5" 5 "crypto/rand" 6 "fmt" 7 "time" 8 9 "github.com/jxskiss/gopkg/v2/internal/machineid" 10 "github.com/jxskiss/gopkg/v2/internal/unsafeheader" 11 ) 12 13 var _ Generator = &V1Gen{} 14 var _ V1Info = &v1Info{} 15 16 const ( 17 v1Version = '1' 18 v1Length = 36 19 ) 20 21 // NewV1Gen creates a new v1 log ID generator. 22 func NewV1Gen() *V1Gen { 23 return &V1Gen{ 24 machineID: getMachineID(), 25 } 26 } 27 28 func getMachineID() [16]byte { 29 var machineID [16]byte 30 var mID [10]byte 31 if x, err := machineid.ID(); err == nil { 32 sum := md5.Sum([]byte(x)) 33 copy(mID[:], sum[:]) 34 } else { 35 _, err = rand.Read(mID[:]) 36 if err != nil { 37 panic("error calling crypto/rand.Read: " + err.Error()) 38 } 39 } 40 b32Enc.Encode(machineID[:], mID[:]) 41 return machineID 42 } 43 44 // V1Gen is a v1 log ID generator. 45 // 46 // A v1 log ID is consisted of the following parts: 47 // 48 // - 9 bytes milli timestamp, in base32 form 49 // - 16 bytes hash of the machine ID of current host if available, 50 // else 16 bytes random data 51 // - 10 bytes random data 52 // - 1 byte version flag "1" 53 // 54 // e.g. "1HMZ5YAD5M0RY2MKE72XWXGSW140NFEAD8J1" 55 type V1Gen struct { 56 machineID [16]byte 57 } 58 59 // Gen generates a new log ID string. 60 func (p *V1Gen) Gen() string { 61 buf := make([]byte, v1Length) 62 buf[len(buf)-1] = v1Version 63 64 // milli timestamp, fixed length, 9 bytes 65 encodeBase32(buf[0:9], time.Now().UnixMilli()) 66 67 // random bytes, fixed length, 10 bytes 68 randNum := rand50bits() 69 encodeBase32(buf[25:35], randNum) 70 71 // machine ID, fixed length, 16 bytes 72 copy(buf[9:25], p.machineID[:]) 73 74 return unsafeheader.BytesToString(buf) 75 } 76 77 func decodeV1Info(s string) (info *v1Info) { 78 info = &v1Info{} 79 if len(s) != v1Length { 80 return 81 } 82 mID := s[9:25] 83 r := s[25:35] 84 tMsec, err := decodeBase32(s[:9]) 85 if err != nil { 86 return 87 } 88 *info = v1Info{ 89 valid: true, 90 time: time.UnixMilli(tMsec), 91 machineID: mID, 92 random: r, 93 } 94 return 95 } 96 97 // V1Info holds parsed information of a v1 log ID string. 98 type V1Info interface { 99 Info 100 Time() time.Time 101 MachineID() string 102 Random() string 103 } 104 105 type v1Info struct { 106 valid bool 107 time time.Time 108 machineID string 109 random string 110 } 111 112 func (info *v1Info) Valid() bool { return info != nil && info.valid } 113 func (info *v1Info) Version() byte { return v1Version } 114 func (info *v1Info) Time() time.Time { return info.time } 115 func (info *v1Info) MachineID() string { return info.machineID } 116 func (info *v1Info) Random() string { return info.random } 117 118 func (info *v1Info) String() string { 119 if !info.Valid() { 120 return "1|invalid" 121 } 122 return fmt.Sprintf("1|%s|%s|%s", formatTime(info.time), info.machineID, info.random) 123 }