* feat: support zap.Object in zapai
   set caller in traceTelemetry if caller is defined
This commit is contained in:
ZetaoZhuang 2022-09-01 22:28:39 -07:00 коммит произвёл GitHub
Родитель e11b452096
Коммит bd236e72f6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 193 добавлений и 15 удалений

Просмотреть файл

@ -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,
}
}

Просмотреть файл

@ -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 {
@ -34,9 +37,125 @@ type gobber struct {
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}