Back to all articles
GoGoLoggingClean CodeDesign Patterns

Logging Field Builder in Go

How to structure and enforce consistent structured logging across microservices in Go using custom field builder patterns.

YS
Yavuz Selim Yesilyurt
Senior Software Engineer
4 min read
Read on Medium

Logging Field Builder in Go

Structured logging is essential when running distributed systems in production. In modern observability stacks (like Elasticsearch, Grafana Loki, or Datadog), logs are queried as key-value pairs rather than arbitrary text strings.

However, in large codebases with multiple engineers, logging field keys often become inconsistent:

  • Developer A logs user_id: 123
  • Developer B logs userId: 123
  • Developer C logs uid: 123

This inconsistency degrades queryability and makes dashboard alerts fragile.

The Field Builder Pattern

To solve this, we can encapsulate structured logging fields inside a dedicated package with strongly typed builder functions:

package fields

import "go.uber.org/zap"

const (
    KeyUserID    = "user_id"
    KeyRequestID = "request_id"
    KeyDuration  = "duration_ms"
)

func UserID(id string) zap.Field {
    return zap.String(KeyUserID, id)
}

func RequestID(id string) zap.Field {
    return zap.String(KeyRequestID, id)
}

func DurationMS(ms int64) zap.Field {
    return zap.Int64(KeyDuration, ms)
}

Usage in Services

With strongly-typed fields, call sites become clean, readable, and refactor-safe:

func HandleUserRequest(ctx context.Context, userID string) error {
    start := time.Now()
    
    logger.Info("processing user request",
        fields.UserID(userID),
        fields.RequestID(ctx.Value("request_id").(string)),
    )
    
    // Perform business logic...
    
    logger.Info("user request completed",
        fields.UserID(userID),
        fields.DurationMS(time.Since(start).Milliseconds()),
    )
    return nil
}

Benefits

  1. Standardization: A single source of truth for key naming conventions.
  2. Type Safety: Prevents passing invalid types or forgetting required fields.
  3. Refactorability: Renaming a field key in production requires updating only one constant.