Fluent Syntax
ByteHide Logger's fluent syntax allows you to chain multiple configuration methods together for expressive, readable logging. This Go-idiomatic approach provides a natural way to build complex log entries.
Fluent API Overview
The fluent API uses method chaining, where each method returns a logger builder that can accept further method calls:
Go
import "github.com/bytehide/bytehide-logs-go"
// Chain methods for expressive logging
logs.WithContext("userId", "user_123").
WithContext("action", "login").
WithTags("auth", "success").
Info("User authenticated")
// Multiple tags
logs.WithTags("payment", "processing", "stripe").
WithContext("amount", 99.99).
WithContext("currency", "USD").
Info("Processing payment")
// Exception handling
err := someOperation()
if err != nil {
logs.WithException(err).
WithTags("error", "critical").
WithContext("operation", "dataSync").
Error("Operation failed", err)
}import "github.com/bytehide/bytehide-logs-go"
// Chain methods for expressive logging
logs.WithContext("userId", "user_123").
WithContext("action", "login").
WithTags("auth", "success").
Info("User authenticated")
// Multiple tags
logs.WithTags("payment", "processing", "stripe").
WithContext("amount", 99.99).
WithContext("currency", "USD").
Info("Processing payment")
// Exception handling
err := someOperation()
if err != nil {
logs.WithException(err).
WithTags("error", "critical").
WithContext("operation", "dataSync").
Error("Operation failed", err)
}Fluent Methods
WithContext()
Add key-value pairs to the log:
Go
logs.WithContext("key1", "value1").
WithContext("key2", "value2").
WithContext("key3", 123).
Info("Log message")logs.WithContext("key1", "value1").
WithContext("key2", "value2").
WithContext("key3", 123).
Info("Log message")WithTags()
Add categorization tags (multiple tags supported):
Go
logs.WithTags("tag1").
WithTags("tag2", "tag3").
Info("Log message")
// Or pass multiple tags at once
logs.WithTags("payment", "processing", "critical").
Info("Log message")logs.WithTags("tag1").
WithTags("tag2", "tag3").
Info("Log message")
// Or pass multiple tags at once
logs.WithTags("payment", "processing", "critical").
Info("Log message")WithCorrelationID()
Set request tracking ID:
Go
logs.WithCorrelationID("req_abc123").
Info("Request processing started")logs.WithCorrelationID("req_abc123").
Info("Request processing started")WithException()
Add error/exception context:
Go
logs.WithException(err).
Error("Operation failed", err)logs.WithException(err).
Error("Operation failed", err)WithUser()
Set user context:
Go
user := &logs.AuthUser{
ID: "user_123",
Email: "john@example.com",
}
logs.WithUser(user).
Info("User action")user := &logs.AuthUser{
ID: "user_123",
Email: "john@example.com",
}
logs.WithUser(user).
Info("User action")Method Chaining Patterns
Complete Request Logging
Go
func (h *Handler) ProcessRequest(w http.ResponseWriter, r *http.Request) {
correlationId := extractOrGenerateCorrelationID(r)
logs.WithCorrelationID(correlationId).
WithContext("method", r.Method).
WithContext("path", r.URL.Path).
WithTags("api", "incoming").
Debug("Request received")
// Parse request
var req OrderRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("path", r.URL.Path).
WithTags("api", "parsing").
WithException(err).
Error("Failed to parse request", err)
return
}
// Process request
order, err := h.service.CreateOrder(&req)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("customerId", req.CustomerID).
WithContext("itemCount", len(req.Items)).
WithTags("order", "creation", "error").
WithException(err).
Error("Order creation failed", err)
return
}
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithContext("total", order.Total).
WithTags("order", "creation", "success").
Info("Order created successfully")
}func (h *Handler) ProcessRequest(w http.ResponseWriter, r *http.Request) {
correlationId := extractOrGenerateCorrelationID(r)
logs.WithCorrelationID(correlationId).
WithContext("method", r.Method).
WithContext("path", r.URL.Path).
WithTags("api", "incoming").
Debug("Request received")
// Parse request
var req OrderRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("path", r.URL.Path).
WithTags("api", "parsing").
WithException(err).
Error("Failed to parse request", err)
return
}
// Process request
order, err := h.service.CreateOrder(&req)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("customerId", req.CustomerID).
WithContext("itemCount", len(req.Items)).
WithTags("order", "creation", "error").
WithException(err).
Error("Order creation failed", err)
return
}
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithContext("total", order.Total).
WithTags("order", "creation", "success").
Info("Order created successfully")
}Service Layer Pattern
Go
type PaymentService struct {
gateway PaymentGateway
}
func (s *PaymentService) ProcessPayment(ctx context.Context, request *PaymentRequest) (string, error) {
correlationId := ctx.Value("correlationId").(string)
logs.WithCorrelationID(correlationId).
WithContext("amount", request.Amount).
WithContext("currency", request.Currency).
WithTags("payment", "processing").
Info("Starting payment processing")
// Validate
if request.Amount <= 0 {
logs.WithCorrelationID(correlationId).
WithContext("amount", request.Amount).
WithTags("payment", "validation").
Warn("Invalid payment amount")
return "", fmt.Errorf("invalid amount")
}
// Process
start := time.Now()
transactionId, err := s.gateway.Charge(ctx, request)
duration := time.Since(start)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("amount", request.Amount).
WithContext("duration_ms", duration.Milliseconds()).
WithTags("payment", "gateway", "error").
WithException(err).
Error("Payment gateway request failed", err)
return "", err
}
logs.WithCorrelationID(correlationId).
WithContext("transactionId", transactionId).
WithContext("amount", request.Amount).
WithContext("duration_ms", duration.Milliseconds()).
WithTags("payment", "gateway", "success").
Info("Payment processed successfully")
return transactionId, nil
}type PaymentService struct {
gateway PaymentGateway
}
func (s *PaymentService) ProcessPayment(ctx context.Context, request *PaymentRequest) (string, error) {
correlationId := ctx.Value("correlationId").(string)
logs.WithCorrelationID(correlationId).
WithContext("amount", request.Amount).
WithContext("currency", request.Currency).
WithTags("payment", "processing").
Info("Starting payment processing")
// Validate
if request.Amount <= 0 {
logs.WithCorrelationID(correlationId).
WithContext("amount", request.Amount).
WithTags("payment", "validation").
Warn("Invalid payment amount")
return "", fmt.Errorf("invalid amount")
}
// Process
start := time.Now()
transactionId, err := s.gateway.Charge(ctx, request)
duration := time.Since(start)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("amount", request.Amount).
WithContext("duration_ms", duration.Milliseconds()).
WithTags("payment", "gateway", "error").
WithException(err).
Error("Payment gateway request failed", err)
return "", err
}
logs.WithCorrelationID(correlationId).
WithContext("transactionId", transactionId).
WithContext("amount", request.Amount).
WithContext("duration_ms", duration.Milliseconds()).
WithTags("payment", "gateway", "success").
Info("Payment processed successfully")
return transactionId, nil
}Database Operations Pattern
Go
type OrderRepository struct {
db *sql.DB
}
func (r *OrderRepository) CreateOrder(ctx context.Context, order *Order) error {
correlationId := ctx.Value("correlationId").(string)
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithTags("database", "insert").
Trace("Inserting order into database")
query := `INSERT INTO orders (id, customer_id, total, created_at)
VALUES ($1, $2, $3, $4)`
_, err := r.db.ExecContext(ctx, query,
order.ID,
order.CustomerID,
order.Total,
order.CreatedAt)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithTags("database", "insert", "error").
WithException(err).
Error("Failed to insert order", err)
return err
}
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithTags("database", "insert", "success").
Info("Order inserted successfully")
return nil
}type OrderRepository struct {
db *sql.DB
}
func (r *OrderRepository) CreateOrder(ctx context.Context, order *Order) error {
correlationId := ctx.Value("correlationId").(string)
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithTags("database", "insert").
Trace("Inserting order into database")
query := `INSERT INTO orders (id, customer_id, total, created_at)
VALUES ($1, $2, $3, $4)`
_, err := r.db.ExecContext(ctx, query,
order.ID,
order.CustomerID,
order.Total,
order.CreatedAt)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithTags("database", "insert", "error").
WithException(err).
Error("Failed to insert order", err)
return err
}
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithTags("database", "insert", "success").
Info("Order inserted successfully")
return nil
}Error Handling with Fluent API
Go
func (s *Service) SafeExecute(ctx context.Context, operationName string, fn func() error) error {
correlationId := ctx.Value("correlationId").(string)
start := time.Now()
logs.WithCorrelationID(correlationId).
WithContext("operation", operationName).
WithTags("operation", "start").
Trace("Operation starting")
defer func() {
if r := recover(); r != nil {
duration := time.Since(start)
logs.WithCorrelationID(correlationId).
WithContext("operation", operationName).
WithContext("duration_ms", duration.Milliseconds()).
WithContext("panic", fmt.Sprintf("%v", r)).
WithTags("operation", "panic", "critical").
Critical("Panic occurred during operation")
}
}()
err := fn()
duration := time.Since(start)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("operation", operationName).
WithContext("duration_ms", duration.Milliseconds()).
WithTags("operation", "error").
WithException(err).
Error("Operation failed", err)
return err
}
logs.WithCorrelationID(correlationId).
WithContext("operation", operationName).
WithContext("duration_ms", duration.Milliseconds()).
WithTags("operation", "success").
Info("Operation completed successfully")
return nil
}func (s *Service) SafeExecute(ctx context.Context, operationName string, fn func() error) error {
correlationId := ctx.Value("correlationId").(string)
start := time.Now()
logs.WithCorrelationID(correlationId).
WithContext("operation", operationName).
WithTags("operation", "start").
Trace("Operation starting")
defer func() {
if r := recover(); r != nil {
duration := time.Since(start)
logs.WithCorrelationID(correlationId).
WithContext("operation", operationName).
WithContext("duration_ms", duration.Milliseconds()).
WithContext("panic", fmt.Sprintf("%v", r)).
WithTags("operation", "panic", "critical").
Critical("Panic occurred during operation")
}
}()
err := fn()
duration := time.Since(start)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("operation", operationName).
WithContext("duration_ms", duration.Milliseconds()).
WithTags("operation", "error").
WithException(err).
Error("Operation failed", err)
return err
}
logs.WithCorrelationID(correlationId).
WithContext("operation", operationName).
WithContext("duration_ms", duration.Milliseconds()).
WithTags("operation", "success").
Info("Operation completed successfully")
return nil
}Real-World Scenario
Complete API Endpoint
Go
func (h *OrderHandler) CreateOrder(w http.ResponseWriter, r *http.Request) {
correlationId := extractOrGenerateCorrelationID(r)
user := extractUserFromRequest(r)
// Request received
logs.WithCorrelationID(correlationId).
WithContext("method", r.Method).
WithContext("path", r.URL.Path).
WithContext("userId", user.ID).
WithTags("api", "order", "create", "request").
Debug("Order creation request received")
// Parse request
var req CreateOrderRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("userId", user.ID).
WithTags("api", "parsing", "error").
WithException(err).
Error("Failed to decode request body", err)
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
logs.WithCorrelationID(correlationId).
WithContext("userId", user.ID).
WithContext("itemCount", len(req.Items)).
WithTags("api", "parsing", "success").
Debug("Request decoded successfully")
// Create order
ctx := r.Context()
ctx = context.WithValue(ctx, "correlationId", correlationId)
order, err := h.service.CreateOrder(ctx, user, &req)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("userId", user.ID).
WithContext("itemCount", len(req.Items)).
WithTags("service", "order", "creation", "error").
WithException(err).
Error("Order creation failed", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
logs.WithCorrelationID(correlationId).
WithContext("userId", user.ID).
WithContext("orderId", order.ID).
WithContext("total", order.Total).
WithTags("api", "order", "create", "success").
Info("Order created successfully")
// Return response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(order)
}func (h *OrderHandler) CreateOrder(w http.ResponseWriter, r *http.Request) {
correlationId := extractOrGenerateCorrelationID(r)
user := extractUserFromRequest(r)
// Request received
logs.WithCorrelationID(correlationId).
WithContext("method", r.Method).
WithContext("path", r.URL.Path).
WithContext("userId", user.ID).
WithTags("api", "order", "create", "request").
Debug("Order creation request received")
// Parse request
var req CreateOrderRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("userId", user.ID).
WithTags("api", "parsing", "error").
WithException(err).
Error("Failed to decode request body", err)
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
logs.WithCorrelationID(correlationId).
WithContext("userId", user.ID).
WithContext("itemCount", len(req.Items)).
WithTags("api", "parsing", "success").
Debug("Request decoded successfully")
// Create order
ctx := r.Context()
ctx = context.WithValue(ctx, "correlationId", correlationId)
order, err := h.service.CreateOrder(ctx, user, &req)
if err != nil {
logs.WithCorrelationID(correlationId).
WithContext("userId", user.ID).
WithContext("itemCount", len(req.Items)).
WithTags("service", "order", "creation", "error").
WithException(err).
Error("Order creation failed", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
logs.WithCorrelationID(correlationId).
WithContext("userId", user.ID).
WithContext("orderId", order.ID).
WithContext("total", order.Total).
WithTags("api", "order", "create", "success").
Info("Order created successfully")
// Return response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(order)
}Go Idiomatic Patterns
Prefer Multi-Line Chains
Go
// Good: readability with line breaks
logs.WithContext("userId", userID).
WithContext("action", "login").
WithTags("auth", "success").
Info("User logged in")
// Avoid: single line is hard to read
logs.WithContext("userId", userID).WithContext("action", "login").WithTags("auth", "success").Info("User logged in")// Good: readability with line breaks
logs.WithContext("userId", userID).
WithContext("action", "login").
WithTags("auth", "success").
Info("User logged in")
// Avoid: single line is hard to read
logs.WithContext("userId", userID).WithContext("action", "login").WithTags("auth", "success").Info("User logged in")Consistent Indentation
Go
// Good: clear hierarchy
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("total", order.Total).
WithTags("order", "processing").
Info("Processing order")
// Avoid: inconsistent indentation
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("total", order.Total).
WithTags("order", "processing").
Info("Processing order")// Good: clear hierarchy
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("total", order.Total).
WithTags("order", "processing").
Info("Processing order")
// Avoid: inconsistent indentation
logs.WithCorrelationID(correlationId).
WithContext("orderId", order.ID).
WithContext("total", order.Total).
WithTags("order", "processing").
Info("Processing order")Logical Grouping
Go
// Good: group related context
logs.WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithContext("total", order.Total).
WithTags("order", "processing").
WithTags("payment", "pending").
Info("Order waiting for payment")
// Good: separate concerns logically
logs.WithTags("database", "query").
WithContext("table", "orders").
WithContext("duration_ms", 150).
Debug("Query executed")// Good: group related context
logs.WithContext("orderId", order.ID).
WithContext("customerId", order.CustomerID).
WithContext("total", order.Total).
WithTags("order", "processing").
WithTags("payment", "pending").
Info("Order waiting for payment")
// Good: separate concerns logically
logs.WithTags("database", "query").
WithContext("table", "orders").
WithContext("duration_ms", 150).
Debug("Query executed")Best Practices
Fluent Syntax Best Practices
- Chain methods logically - group related calls together
- Use line breaks - each method on its own line for readability
- Consistent formatting - maintain alignment and indentation
- Order methods consistently - context, then tags, then exception, then log level
- Prefer fluent API - for complex logging with multiple attributes
- Use direct calls - for simple single-message logging
- Document patterns - establish team conventions for fluent chains
Fluent API Ordering
Suggested order for method chaining:
WithCorrelationID()- request trackingWithContext()- structured data (call multiple times)WithTags()- categorization (call multiple times)WithException()/WithUser()- special contexts- Log method -
Info(),Error(), etc.