mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-21 20:56:04 +03:00
stream id generation and middleware
This commit is contained in:
commit
2be8669393
@ -17,7 +17,7 @@ import (
|
|||||||
type logRecord struct {
|
type logRecord struct {
|
||||||
LevelName string `json:"level_name"`
|
LevelName string `json:"level_name"`
|
||||||
DateTime null.Time `json:"datetime"`
|
DateTime null.Time `json:"datetime"`
|
||||||
Caller string `json:"caller"`
|
StreamID string `json:"streamId"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Handler string `json:"handler,omitempty"`
|
Handler string `json:"handler,omitempty"`
|
||||||
Connection string `json:"connection,omitempty"`
|
Connection string `json:"connection,omitempty"`
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GinMiddleware will construct Gin middleware which will log requests.
|
// GinMiddleware will construct Gin middleware which will log requests and provide logger with unique request ID.
|
||||||
func GinMiddleware(log Logger) gin.HandlerFunc {
|
func GinMiddleware(log Logger) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// Start timer
|
// Start timer
|
||||||
@ -15,6 +15,11 @@ func GinMiddleware(log Logger) gin.HandlerFunc {
|
|||||||
path := c.Request.URL.Path
|
path := c.Request.URL.Path
|
||||||
raw := c.Request.URL.RawQuery
|
raw := c.Request.URL.RawQuery
|
||||||
|
|
||||||
|
streamID := generateStreamID()
|
||||||
|
log := log.With(StreamID(streamID))
|
||||||
|
c.Set(StreamIDAttr, streamID)
|
||||||
|
c.Set("logger", log)
|
||||||
|
|
||||||
// Process request
|
// Process request
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|
||||||
@ -35,3 +40,7 @@ func GinMiddleware(log Logger) gin.HandlerFunc {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustGet(c *gin.Context) Logger {
|
||||||
|
return c.MustGet("logger").(Logger)
|
||||||
|
}
|
||||||
|
@ -16,6 +16,8 @@ func TestGinMiddleware(t *testing.T) {
|
|||||||
r := gin.New()
|
r := gin.New()
|
||||||
r.Use(GinMiddleware(log))
|
r.Use(GinMiddleware(log))
|
||||||
r.GET("/mine", func(c *gin.Context) {
|
r.GET("/mine", func(c *gin.Context) {
|
||||||
|
log := MustGet(c)
|
||||||
|
log.Info("some very important message")
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
})
|
})
|
||||||
r.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/mine", nil))
|
r.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/mine", nil))
|
||||||
@ -23,16 +25,19 @@ func TestGinMiddleware(t *testing.T) {
|
|||||||
require.Equal(t, http.StatusOK, rr.Code)
|
require.Equal(t, http.StatusOK, rr.Code)
|
||||||
items, err := newJSONBufferedLogger(log).ScanAll()
|
items, err := newJSONBufferedLogger(log).ScanAll()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, items, 1)
|
require.Len(t, items, 2)
|
||||||
require.NotEmpty(t, items[0].Context)
|
require.NotEmpty(t, items[0].StreamID)
|
||||||
assert.NotEmpty(t, items[0].Context["startTime"])
|
require.NotEmpty(t, items[1].StreamID)
|
||||||
assert.NotEmpty(t, items[0].Context["endTime"])
|
assert.Equal(t, "some very important message", items[0].Message)
|
||||||
|
require.NotEmpty(t, items[1].Context)
|
||||||
|
assert.NotEmpty(t, items[1].Context["startTime"])
|
||||||
|
assert.NotEmpty(t, items[1].Context["endTime"])
|
||||||
assert.True(t, func() bool {
|
assert.True(t, func() bool {
|
||||||
_, ok := items[0].Context["latency"]
|
_, ok := items[1].Context["latency"]
|
||||||
return ok
|
return ok
|
||||||
}())
|
}())
|
||||||
assert.NotEmpty(t, items[0].Context["remoteAddress"])
|
assert.NotEmpty(t, items[1].Context["remoteAddress"])
|
||||||
assert.NotEmpty(t, items[0].Context[HTTPMethodAttr])
|
assert.NotEmpty(t, items[1].Context[HTTPMethodAttr])
|
||||||
assert.NotEmpty(t, items[0].Context["path"])
|
assert.NotEmpty(t, items[1].Context["path"])
|
||||||
assert.NotEmpty(t, items[0].Context["bodySize"])
|
assert.NotEmpty(t, items[1].Context["bodySize"])
|
||||||
}
|
}
|
||||||
|
44
core/logger/stream_id.go
Normal file
44
core/logger/stream_id.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var streamIDCounter = uint64(time.Now().Unix())
|
||||||
|
|
||||||
|
func hexEncode(dst *[32]byte, src *[8]byte, cnt uint64) int {
|
||||||
|
const hexDigits = "0123456789abcdef"
|
||||||
|
for i, b := range src[:] {
|
||||||
|
dst[i*2] = hexDigits[b>>4]
|
||||||
|
dst[i*2+1] = hexDigits[b&0x0F]
|
||||||
|
}
|
||||||
|
idx := 16
|
||||||
|
for cnt > 0 {
|
||||||
|
dst[idx] = hexDigits[cnt&0xF]
|
||||||
|
cnt >>= 4
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
for i, j := 16, idx-1; i < j; i, j = i+1, j-1 {
|
||||||
|
dst[i], dst[j] = dst[j], dst[i]
|
||||||
|
}
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
func bytesToString(b *[32]byte, length int) string {
|
||||||
|
return unsafe.String(&b[0], length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateStreamID() string {
|
||||||
|
var id [8]byte
|
||||||
|
var hexID [32]byte
|
||||||
|
|
||||||
|
cnt := atomic.AddUint64(&streamIDCounter, 1)
|
||||||
|
timestamp := time.Now().UnixNano()
|
||||||
|
binary.BigEndian.PutUint64(id[:], uint64(timestamp))
|
||||||
|
length := hexEncode(&hexID, &id, cnt)
|
||||||
|
|
||||||
|
return bytesToString(&hexID, length)
|
||||||
|
}
|
19
core/logger/stream_id_test.go
Normal file
19
core/logger/stream_id_test.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerateStreamID(t *testing.T) {
|
||||||
|
id1 := generateStreamID()
|
||||||
|
id2 := generateStreamID()
|
||||||
|
|
||||||
|
assert.NotEqual(t, id1, id2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkGenerateStreamID(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = generateStreamID()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user