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  }