Skip to content

Commit

Permalink
Adding more struct to use reflect fields for optional logging (#123)
Browse files Browse the repository at this point in the history
* new logger

* add more tags and fields

* update nullify context

* fix

* update

---------

Co-authored-by: Vikranth Subramanian <[email protected]>
  • Loading branch information
vik-nullify and Vikranth Subramanian authored Jan 28, 2025
1 parent 285415b commit 8fff950
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 14 deletions.
11 changes: 0 additions & 11 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main

import (
"context"
"errors"

"github.com/nullify-platform/logger/pkg/logger"
"github.com/nullify-platform/logger/pkg/logger/tracer"
Expand All @@ -22,14 +21,4 @@ func main() {
logger.L(ctx).Error("error configuring logger", logger.Err(err))
panic(err)
}

anotherFunctionRenamed(ctx)
}

func anotherFunctionRenamed(ctx context.Context) {
ctx, span := tracer.FromContext(ctx).Start(ctx, "extended feature")
defer span.End()

logger.L(ctx).Info("another function started")
logger.L(ctx).Error("something terbl happened", logger.Err(errors.New("test error 2 in dev")))
}
9 changes: 6 additions & 3 deletions pkg/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,24 +105,27 @@ func (l *logger) Info(msg string, fields ...Field) {
// Warn logs a message with the warn level
func (l *logger) Warn(msg string, fields ...Field) {
l.captureExceptions(fields)
l.underlyingLogger.Warn(msg, fields...)
updateFields := l.getContextMetadataAsFields(LogConfig{}, fields)
l.underlyingLogger.Warn(msg, updateFields...)
}

// Error logs a message with the error level
func (l *logger) Error(msg string, fields ...Field) {
trace.SpanFromContext(l.attachedContext).RecordError(errors.New(msg))
trace.SpanFromContext(l.attachedContext).SetStatus(codes.Error, msg)
l.captureExceptions(fields)
l.underlyingLogger.Error(msg, fields...)
updateFields := l.getContextMetadataAsFields(LogConfig{}, fields)
l.underlyingLogger.Error(msg, updateFields...)
}

// Fatal logs a message with the fatal level and then calls os.Exit(1)
func (l *logger) Fatal(msg string, fields ...Field) {
trace.SpanFromContext(l.attachedContext).SetStatus(codes.Error, msg)
l.captureExceptions(fields)
updateFields := l.getContextMetadataAsFields(LogConfig{}, fields)
l.Sync()

l.underlyingLogger.Fatal(msg, fields...)
l.underlyingLogger.Fatal(msg, updateFields...)
}

// captureExceptions captures exceptions from fields and sends them to sentry
Expand Down
126 changes: 126 additions & 0 deletions pkg/logger/nullify_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package logger

import (
"context"

"reflect"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

type LogConfig struct {
Repository *repository
Service *service
Tool *tool
}

type repository struct {
Name *string `json:"repository_name"`
Owner *string `json:"repository_owner"`
Platform *string `json:"platform"`
ID *string `json:"repository_id"`
CommitID *string `json:"commit_id"` //head commit id
PRNumber *string `json:"pr_number"`
Component *string `json:"component"`
BranchID *string `json:"branch_id"`
BranchName *string `json:"branch_name"`
InstallationID *string `json:"installation_id"`
AppID *string `json:"app_id"`
Action *string `json:"action"`
ProjectID *string `json:"project_id"`
OrganizationID *string `json:"organization_id"`
}

type service struct {
Name *string `json:"service_name"`
ServiceCategory *string `json:"service_category"`
Event *string `json:"service_event"`
}

type tool struct {
Name *string `json:"tool_name"`
Status *string `json:"tool_status"`
}

type contextKey string

func SetMetadata(ctx context.Context, metadata map[string]string) context.Context {
for key, value := range metadata {
ctx = setContextMetadata(ctx, contextKey(key), value)
}
return ctx
}

func setContextMetadata(ctx context.Context, inputKey contextKey, inputValue string) context.Context {
return context.WithValue(ctx, contextKey(inputKey), inputValue)
}

func SetTraceAttributes(trace trace.Span, metadata map[string]string) trace.Span {
for key, value := range metadata {
trace.SetAttributes(attribute.String(key, value))
}
return trace
}

func GetAWSConfig(ctx context.Context) (aws.Config, error) {
awsConfig, err := config.LoadDefaultConfig(ctx)
if err != nil {
return aws.Config{}, err
}
return awsConfig, nil
}

func SetMetdataForLogsAndTraces(ctx context.Context, trace trace.Span, metadata map[string]string) (context.Context, trace.Span) {
mctx := SetMetadata(ctx, metadata)
mtrace := SetTraceAttributes(trace, metadata)
return mctx, mtrace
}

func (l *logger) getContextMetadataAsFields(logConfig LogConfig, fields []zapcore.Field) []zapcore.Field {
return extractFieldsFromStruct(l.attachedContext, reflect.ValueOf(logConfig), fields)
}

func extractFieldsFromStruct(ctx context.Context, v reflect.Value, fields []zapcore.Field) []zapcore.Field {
if v.Kind() == reflect.Ptr {
v = v.Elem()
}

if v.Kind() != reflect.Struct {
return fields
}

t := v.Type()

// Iterate through all struct fields
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
fieldValue := v.Field(i)

// If the field is a struct, recurse into it
if fieldValue.Kind() == reflect.Struct || (fieldValue.Kind() == reflect.Ptr && fieldValue.Elem().Kind() == reflect.Struct) {
fields = extractFieldsFromStruct(ctx, fieldValue, fields)
continue
}

jsonKey := field.Tag.Get("json")

// Skip fields without a JSON tag
if jsonKey == "" || jsonKey == "-" {
continue
}

// Retrieve the value from context using the field name as the key
key := contextKey(field.Name)
if value, ok := ctx.Value(key).(string); ok {
// Append zap field
fields = append(fields, zap.String(jsonKey, value))
}
}

return fields
}

0 comments on commit 8fff950

Please sign in to comment.