I wrote a code for integrating Azure app insights with Zerolog, one thing I'm not sure about is how to get the event fields inside the hook function so I can publish the data fields into the Azure app insights custom properties field or is there any other cleaner way to achieve this.
Does anyone think zerolog should expose a function to get the fields?
package zeroappinsights
import (
"context"
"encoding/json"
"github.com/gin-gonic/gin"
"net/http"
"os"
"time"
"github.com/microsoft/ApplicationInsights-Go/appinsights"
"github.com/microsoft/ApplicationInsights-Go/appinsights/contracts"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/org/package/constants"
"github.com/org/package/models"
)
var levelMap = map[zerolog.Level]contracts.SeverityLevel{
zerolog.ErrorLevel: contracts.Error,
zerolog.InfoLevel: contracts.Information,
zerolog.DebugLevel: contracts.Verbose,
zerolog.FatalLevel: contracts.Critical,
zerolog.WarnLevel: contracts.Warning,
}
var appInsightsProperties = []string{
contracts.OperationId,
contracts.OperationParentId,
contracts.UserId,
contracts.SessionId,
}
type TracingHook struct {
env constants.Environment
telemetryClient appinsights.TelemetryClient
file *os.File
eventsMap map[string]string
}
func NewAITracing(serviceName, instrumentationKey string, env constants.Environment, eventsMap map[string]string) *TracingHook {
client := appinsights.NewTelemetryClient(instrumentationKey)
client.Context().Tags.Cloud().SetRole(serviceName)
if env.IsLocal() {
file, err := os.OpenFile(serviceName+".log", os.O_CREATE, 0644)
if err != nil {
return nil
}
return &TracingHook{
telemetryClient: client,
file: file,
env: constants.Local,
}
}
return &TracingHook{
telemetryClient: client,
file: nil,
env: env,
eventsMap: eventsMap,
}
}
func (h *TracingHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
ctx := e.GetCtx()
if appinsightsLevel, ok := levelMap[level]; ok {
trace := appinsights.NewTraceTelemetry(msg, appinsightsLevel)
for _, property := range appInsightsProperties {
trace.Tags[property] = ctx.Value(property).(string)
}
//I want to add more properties from the event to trace.Properties
trace.Timestamp = time.Now().UTC()
if h.env.IsLocal() {
// Marshal the request into a JSON byte slice
traceJSON, err := json.Marshal(trace)
if err != nil {
log.Printf("error marshaling request: %v", err)
return
}
if _, err = h.file.Write(append(traceJSON, '\n')); err != nil {
log.Printf("error occurred while writing to file: %v", err)
return
}
} else {
go h.telemetryClient.Track(trace)
}
}
}
func (h *TracingHook) LoggingMiddleware(logger zerolog.Logger) func(c *gin.Context) {
return func(c *gin.Context) {
startTime := time.Now().UTC()
telemetry := appinsights.NewEventTelemetry(h.eventsMap[c.Request.URL.Path])
h.telemetryClient.Track(telemetry)
operationID := telemetry.Tags[contracts.OperationId]
operationParentID := telemetry.Tags[contracts.OperationParentId]
userID := telemetry.Tags[contracts.UserId]
sessionID := telemetry.Tags[contracts.SessionId]
if value, exists := c.Get("userProfile"); exists {
profile := value.(models.UserProfile)
userID = profile.ID
sessionID = c.Request.Header.Get(constants.Session)
}
values := []string{operationID, operationParentID, userID, sessionID}
ctx := c.Request.Context()
for i, key := range appInsightsProperties {
ctx = context.WithValue(ctx, key, values[i])
}
loggerContext := logger.WithContext(ctx)
c.Set("loggerCtx", loggerContext)
c.Next()
duration := time.Since(startTime)
request := appinsights.NewRequestTelemetry(c.Request.Method, c.Request.URL.Path, duration, http.StatusText(c.Writer.Status()))
request.Timestamp = time.Now().UTC()
tags := request.Tags
tags[contracts.OperationId] = operationID
tags[contracts.OperationParentId] = operationParentID
tags[contracts.UserId] = userID
tags[contracts.SessionId] = sessionID
request.Tags = tags
if h.env.IsLocal() {
// Marshal the request into a JSON byte slice
requestJSON, err := json.Marshal(request)
if err != nil {
log.Printf("error marshaling request: %v", err)
return
}
if _, err = h.file.Write(append(requestJSON, '\n')); err != nil {
log.Printf("error occurred while writing to file: %v", err)
return
}
return
}
h.telemetryClient.Track(request)
}
}
Below you can see the hook function that extracts the relevant information from the
Zerologevent and add it to the Application Insights telemetry properties.Code:
By this
ApplicationInsightsHookstruct is created and it implementing thezerolog.Hookinterface.The
Firemethod is called when a log event is fired, and it extracts relevant information from the Zerolog event, including custom fields, and adds them to the Application Insights telemetry.