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 MediumLogging 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
- Standardization: A single source of truth for key naming conventions.
- Type Safety: Prevents passing invalid types or forgetting required fields.
- Refactorability: Renaming a field key in production requires updating only one constant.
