github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/infra/logid/logid.go (about)

     1  // Package logid provides log ID generators to help service observability,
     2  // such as logging, tracing, metrics, etc.
     3  package logid
     4  
     5  import (
     6  	"encoding/base32"
     7  	"time"
     8  
     9  	"github.com/jxskiss/gopkg/v2/perf/fastrand"
    10  )
    11  
    12  var defaultGen Generator = NewV1Gen()
    13  
    14  type Generator interface {
    15  
    16  	// Gen generates a new log ID string, it should always return
    17  	// a valid log ID, and don't generate duplicate log IDs.
    18  	Gen() string
    19  }
    20  
    21  // SetDefault changes the default generator.
    22  //
    23  // The default generator may be changed by the main program,
    24  // but generally library code shall not call this function.
    25  func SetDefault(gen Generator) {
    26  	defaultGen = gen
    27  }
    28  
    29  // Gen generates a new log ID string using the default generator.
    30  func Gen() string {
    31  	return defaultGen.Gen()
    32  }
    33  
    34  // Crockford's Base32 Encoding
    35  // https://www.crockford.com/base32.html
    36  var (
    37  	b32Chars = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
    38  	b32Enc   = base32.NewEncoding(b32Chars).WithPadding(base32.NoPadding)
    39  	b32Dec   = ""
    40  )
    41  
    42  func init() {
    43  	buf := make([]byte, 128)
    44  	for i := range buf {
    45  		buf[i] = 0xff
    46  	}
    47  	for i, c := range b32Chars {
    48  		buf[c] = byte(i)
    49  		if c >= 'A' && c <= 'Z' {
    50  			buf[c+'a'-'A'] = byte(i)
    51  		}
    52  	}
    53  	b32Dec = string(buf)
    54  }
    55  
    56  func encodeBase32(b []byte, x int64) {
    57  	i := len(b) - 1
    58  	for i >= 0 {
    59  		b[i] = b32Chars[x&31]
    60  		x >>= 5
    61  		i--
    62  	}
    63  }
    64  
    65  func decodeBase32(b string) (x int64, err error) {
    66  	for i, c := range b {
    67  		if b32Dec[c] == 0xff {
    68  			return 0, base32.CorruptInputError(i)
    69  		}
    70  		x = (x << 5) | int64(b32Dec[c])
    71  	}
    72  	return x, nil
    73  }
    74  
    75  const mask50bits = (1 << 50) - 1
    76  
    77  func rand50bits() int64 {
    78  	x := fastrand.Uint64() & mask50bits
    79  	return int64(x)
    80  }
    81  
    82  // strTimeMilli is the time format used in string form of a log ID info.
    83  const strTimeMilli = "20060102.15:04:05.000Z0700"
    84  
    85  func formatTime(t time.Time) string {
    86  	return t.Format(strTimeMilli)
    87  }
    88  
    89  // minLength is the minimum length of a log ID generated by this package.
    90  // Update this constant when adding new generators.
    91  const minLength = v2IPv4Length
    92  
    93  // Decode decodes a log ID string and returns the parsed information.
    94  func Decode(s string) (info Info) {
    95  	if len(s) >= minLength {
    96  		switch s[len(s)-1] {
    97  		case v1Version:
    98  			return decodeV1Info(s)
    99  		case v2Version:
   100  			return decodeV2Info(s)
   101  		}
   102  	}
   103  	return invalidInfo{}
   104  }
   105  
   106  // Info holds parsed information of a log ID string.
   107  type Info interface {
   108  	// Valid tells whether it is a valid log ID generated by this package.
   109  	Valid() bool
   110  
   111  	// Version returns the version of the log ID.
   112  	Version() byte
   113  
   114  	// String returns the human-friendly string representation of the log ID,
   115  	// e.g.
   116  	// "1|20240125.10:07:20.485+0800|M0RY2MKE72XWXGSW|140NFEAD8J"
   117  	// "2|20240125.10:07:20.486+0800|fdbd:dc01:16:16::94|CBDDWZEJH4"
   118  	String() string
   119  }
   120  
   121  type invalidInfo struct{}
   122  
   123  func (invalidInfo) Valid() bool    { return false }
   124  func (invalidInfo) Version() byte  { return '0' }
   125  func (invalidInfo) String() string { return "0|invalid" }