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  }