implement ObjectEncoder (#1528)
* feat: support zap.Object in zapai set caller in traceTelemetry if caller is defined
This commit is contained in:
Родитель
e11b452096
Коммит
bd236e72f6
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/microsoft/ApplicationInsights-Go/appinsights/contracts"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var levelToSev = map[zapcore.Level]contracts.SeverityLevel{
|
||||
|
@ -31,6 +32,7 @@ type Core struct {
|
|||
fieldMappers map[string]fieldTagMapper
|
||||
fields []zapcore.Field
|
||||
out zapcore.WriteSyncer
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// NewCore creates a new appinsights zap core. Should only be initialized using an appinsights Sink as the
|
||||
|
@ -76,21 +78,33 @@ func (c *Core) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcor
|
|||
// Write implements zapcore.Core
|
||||
//nolint:gocritic // ignore hugeparam in interface impl
|
||||
func (c *Core) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
t := appinsights.NewTraceTelemetry(entry.Message, levelToSev[entry.Level])
|
||||
|
||||
// add fields from core
|
||||
fields = append(c.fields, fields...)
|
||||
|
||||
// reset the traceTelemetry in encoder
|
||||
c.enc.setTraceTelemetry(t)
|
||||
|
||||
// set caller
|
||||
if entry.Caller.Defined {
|
||||
t.Properties["caller"] = entry.Caller.String()
|
||||
}
|
||||
|
||||
// set fields
|
||||
for i := range fields {
|
||||
// check mapped fields first
|
||||
if mapper, ok := c.fieldMappers[fields[i].Key]; ok {
|
||||
// handle zap object first
|
||||
if fields[i].Type == zapcore.ObjectMarshalerType {
|
||||
fields[i].AddTo(c.enc)
|
||||
} else if mapper, ok := c.fieldMappers[fields[i].Key]; ok {
|
||||
// check mapped fields
|
||||
mapper(t, fieldStringer(&fields[i]))
|
||||
} else {
|
||||
t.Properties[fields[i].Key] = fieldStringer(&fields[i])
|
||||
}
|
||||
}
|
||||
|
||||
b, err := c.enc.encode(t)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "core failed to encode trace")
|
||||
|
@ -121,5 +135,6 @@ func (c *Core) clone() *Core {
|
|||
fieldMappers: fieldMappers,
|
||||
fields: fields,
|
||||
out: c.out,
|
||||
lock: c.lock,
|
||||
}
|
||||
}
|
||||
|
|
125
zapai/encoder.go
125
zapai/encoder.go
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/microsoft/ApplicationInsights-Go/appinsights"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -13,7 +14,9 @@ import (
|
|||
)
|
||||
|
||||
type traceEncoder interface {
|
||||
zapcore.ObjectEncoder
|
||||
encode(*appinsights.TraceTelemetry) ([]byte, error)
|
||||
setTraceTelemetry(*appinsights.TraceTelemetry)
|
||||
}
|
||||
|
||||
type traceDecoder interface {
|
||||
|
@ -31,12 +34,128 @@ type traceDecoder interface {
|
|||
// Encoders and Decoders also need to be matched up 1:1, as the first thing an Encoder sends (once!) is type data, and
|
||||
// it is an error for a Decoder to receive the same type data from its stream more than once.
|
||||
type gobber struct {
|
||||
encoder *gob.Encoder
|
||||
decoder *gob.Decoder
|
||||
buffer *bytes.Buffer
|
||||
encoder *gob.Encoder
|
||||
decoder *gob.Decoder
|
||||
buffer *bytes.Buffer
|
||||
traceTelemetry *appinsights.TraceTelemetry
|
||||
keyPrefix string
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (g *gobber) AddObject(key string, marshaler zapcore.ObjectMarshaler) error {
|
||||
curPrefix := g.keyPrefix
|
||||
if len(g.keyPrefix) == 0 {
|
||||
g.keyPrefix = key
|
||||
} else {
|
||||
g.keyPrefix = g.keyPrefix + "_" + key
|
||||
}
|
||||
marshaler.MarshalLogObject(g)
|
||||
g.keyPrefix = curPrefix
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gobber) AddString(key, value string) {
|
||||
g.traceTelemetry.Properties[g.keyPrefix+"_"+key] = value
|
||||
}
|
||||
|
||||
func (g *gobber) AddBool(key string, value bool) {
|
||||
g.traceTelemetry.Properties[g.keyPrefix+"_"+key] = strconv.FormatBool(value)
|
||||
}
|
||||
|
||||
func (g *gobber) AddInt(key string, value int) {
|
||||
g.traceTelemetry.Properties[g.keyPrefix+"_"+key] = strconv.Itoa(value)
|
||||
}
|
||||
|
||||
func (g *gobber) AddInt64(key string, value int64) {
|
||||
g.traceTelemetry.Properties[g.keyPrefix+"_"+key] = strconv.FormatInt(value, 10)
|
||||
}
|
||||
|
||||
func (g *gobber) AddUint16(key string, value uint16) {
|
||||
g.traceTelemetry.Properties[g.keyPrefix+"_"+key] = strconv.FormatUint(uint64(value), 10)
|
||||
}
|
||||
|
||||
func (g *gobber) AddArray(_ string, _ zapcore.ArrayMarshaler) error {
|
||||
// TODO to be implemented
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gobber) AddBinary(_ string, _ []byte) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddByteString(_ string, _ []byte) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddComplex128(_ string, _ complex128) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddComplex64(_ string, _ complex64) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddDuration(_ string, _ time.Duration) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddFloat64(_ string, _ float64) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddFloat32(_ string, _ float32) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddInt32(_ string, _ int32) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddInt16(_ string, _ int16) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddInt8(_ string, _ int8) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddTime(_ string, _ time.Time) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddUint(_ string, _ uint) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddUint64(_ string, _ uint64) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddUint32(_ string, _ uint32) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddUint8(_ string, _ uint8) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddUintptr(_ string, _ uintptr) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) AddReflected(_ string, _ interface{}) error {
|
||||
// TODO to be implemented
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gobber) OpenNamespace(_ string) {
|
||||
// TODO to be implemented
|
||||
}
|
||||
|
||||
func (g *gobber) setTraceTelemetry(traceTelemetry *appinsights.TraceTelemetry) {
|
||||
g.traceTelemetry = traceTelemetry
|
||||
}
|
||||
|
||||
// newTraceEncoder creates a gobber that can only encode.
|
||||
func newTraceEncoder() traceEncoder {
|
||||
buf := &bytes.Buffer{}
|
||||
|
|
|
@ -2,20 +2,44 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-container-networking/zapai"
|
||||
logfmt "github.com/jsternberg/zap-logfmt"
|
||||
"github.com/microsoft/ApplicationInsights-Go/appinsights"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
const version = "1.2.3"
|
||||
|
||||
type Example struct {
|
||||
NetworkContainerID string
|
||||
NetworkID string
|
||||
ReservationID string
|
||||
Sub Sub
|
||||
}
|
||||
|
||||
type Sub struct {
|
||||
subnet string
|
||||
num int
|
||||
}
|
||||
|
||||
func (s Sub) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
|
||||
encoder.AddString("subnet", s.subnet)
|
||||
encoder.AddInt("num", s.num)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Example) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
|
||||
encoder.AddString("ncId", e.NetworkContainerID)
|
||||
encoder.AddString("vnetId", e.NetworkID)
|
||||
encoder.AddObject("sub", e.Sub)
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// stdoutcore logs to stdout with a default JSON encoding
|
||||
stdoutcore := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), os.Stdout, zapcore.DebugLevel)
|
||||
|
@ -26,7 +50,7 @@ func main() {
|
|||
logfmtcore := zapcore.NewCore(logfmt.NewEncoder(zap.NewProductionEncoderConfig()), os.Stdout, zapcore.DebugLevel)
|
||||
log = zap.New(logfmtcore) // reassign log
|
||||
log.Error("subnet failed to join", zap.String("subnet", "podnet"), zap.String("prefix", "10.0.0.0/8"))
|
||||
|
||||
jsoncore := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), os.Stdout, zapcore.DebugLevel)
|
||||
// build the AI config
|
||||
sinkcfg := zapai.SinkConfig{
|
||||
GracePeriod: 30 * time.Second, //nolint:gomnd // ignore
|
||||
|
@ -49,12 +73,12 @@ func main() {
|
|||
aicore = aicore.WithFieldMappers(zapai.DefaultMappers)
|
||||
|
||||
// compose the logfmt and aicore in to a virtual tee core so they both receive all log events
|
||||
teecore := zapcore.NewTee(logfmtcore, aicore)
|
||||
teecore := zapcore.NewTee(logfmtcore, jsoncore, aicore)
|
||||
|
||||
// reassign log using the teecore
|
||||
log = zap.New(teecore)
|
||||
log = zap.New(teecore, zap.AddCaller())
|
||||
|
||||
// (optional): add normalized fields for the built-in AI Tags
|
||||
//(optional): add normalized fields for the built-in AI Tags
|
||||
log = log.With(
|
||||
zap.String("user_id", runtime.GOOS),
|
||||
zap.String("operation_id", ""),
|
||||
|
@ -71,6 +95,26 @@ func main() {
|
|||
zap.String("VMID", "VMID"),
|
||||
)
|
||||
|
||||
subn := Sub{
|
||||
subnet: "123.222.222",
|
||||
num: 123,
|
||||
}
|
||||
ex1 := Example{
|
||||
NetworkID: "vetId-1",
|
||||
NetworkContainerID: "nc-1",
|
||||
Sub: subn,
|
||||
}
|
||||
|
||||
ex2 := Example{
|
||||
NetworkID: "vetId-2",
|
||||
NetworkContainerID: "nc-2",
|
||||
Sub: subn,
|
||||
}
|
||||
|
||||
// log with zap.Object
|
||||
log.Debug("testing message-1", zap.Object("ex1", &ex1))
|
||||
log.Debug("testing message-2", zap.Object("ex2", &ex2))
|
||||
|
||||
// muxlog adds a component=mux field to every log that it writes
|
||||
muxlog := log.With(zap.String("component", "mux"))
|
||||
m := &mux{log: muxlog}
|
||||
|
|
Загрузка…
Ссылка в новой задаче