Go SDK

GitHub

Official Go SDK for LogWard with automatic batching, retry logic, circuit breaker pattern, and native OpenTelemetry integration.

Installation

bash
go get github.com/logward-dev/logward-sdk-go

Quick Start

go
package main

import (
    "context"
    "github.com/logward-dev/logward-sdk-go"
)

func main() {
    client, err := logward.New(
        logward.WithAPIKey("lp_your_api_key"),
        logward.WithService("my-service"),
    )
    if err != nil {
        panic(err)
    }
    defer client.Close()

    ctx := context.Background()

    // Send logs
    client.Info(ctx, "Server started", map[string]any{"port": 8080})
    client.Error(ctx, "Connection failed", map[string]any{"error": "timeout"})
}

Features

  • ✅ Automatic batching with configurable size and interval
  • ✅ Retry logic with exponential backoff
  • ✅ Circuit breaker pattern for fault tolerance
  • ✅ Goroutine-safe logging
  • ✅ Context support for request-scoped logging
  • ✅ Native OpenTelemetry integration
  • ✅ Graceful shutdown with flush
  • ✅ Structured metadata support
  • ✅ 87% test coverage

Configuration

go
client, err := logward.New(
    // Required
    logward.WithAPIKey("lp_your_api_key"),
    logward.WithService("my-service"),

    // Optional - API
    logward.WithBaseURL("https://api.logward.dev"),

    // Optional - Performance
    logward.WithBatchSize(100),              // Max logs per batch (default: 100)
    logward.WithFlushInterval(5*time.Second), // Flush interval (default: 5s)
    logward.WithTimeout(30*time.Second),      // HTTP timeout (default: 30s)

    // Optional - Reliability
    logward.WithRetry(3, 1*time.Second, 60*time.Second), // maxRetries, delay, maxDelay
    logward.WithCircuitBreaker(5, 30*time.Second),       // threshold, timeout

    // Optional - Metadata
    logward.WithGlobalMetadata(map[string]any{
        "environment": "production",
        "version":     "1.0.0",
    }),
)

Logging Methods

Basic Logging

go
ctx := context.Background()

// Log levels: Debug, Info, Warn, Error, Critical
client.Debug(ctx, "Debug message", map[string]any{"detail": "value"})
client.Info(ctx, "Request received", map[string]any{"method": "GET", "path": "/users"})
client.Warn(ctx, "Cache miss", map[string]any{"key": "user:123"})
client.Error(ctx, "Query failed", map[string]any{"query": "SELECT *"})
client.Critical(ctx, "Out of memory", map[string]any{"used": "95%"})

With Trace ID

go
// Add trace ID to context
ctx := logward.WithTraceID(context.Background(), "550e8400-e29b-41d4-a716-446655440000")

// All logs will include the trace ID
client.Info(ctx, "Processing request", nil)
client.Info(ctx, "Query executed", nil)

Error Handling

go
err := client.Info(ctx, "message", nil)
if err != nil {
    switch {
    case errors.Is(err, logward.ErrClientClosed):
        // Client was closed, logs won't be sent
    case errors.Is(err, logward.ErrCircuitOpen):
        // Circuit breaker is open, too many failures
    case errors.Is(err, logward.ErrInvalidAPIKey):
        // Invalid API key configuration
    default:
        // Other error
        log.Printf("Failed to send log: %v", err)
    }
}

OpenTelemetry Integration

go
import (
    "go.opentelemetry.io/otel"
)

tracer := otel.Tracer("my-service")

func handleRequest(ctx context.Context) {
    ctx, span := tracer.Start(ctx, "handle-request")
    defer span.End()

    // Trace ID and Span ID are automatically extracted from context
    client.Info(ctx, "Processing request", map[string]any{
        "user_id": 123,
    })

    // Nested spans work too
    ctx, dbSpan := tracer.Start(ctx, "database-query")
    client.Debug(ctx, "Executing query", nil)
    dbSpan.End()
}

HTTP Middleware

Standard Library

go
func LoggingMiddleware(client *logward.Client) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            start := time.Now()

            // Create wrapped response writer to capture status
            wrapped := &responseWriter{ResponseWriter: w, statusCode: 200}

            next.ServeHTTP(wrapped, r)

            client.Info(r.Context(), "HTTP request", map[string]any{
                "method":      r.Method,
                "path":        r.URL.Path,
                "status":      wrapped.statusCode,
                "duration_ms": time.Since(start).Milliseconds(),
            })
        })
    }
}

// Usage
mux := http.NewServeMux()
handler := LoggingMiddleware(client)(mux)
http.ListenAndServe(":8080", handler)

Gin Framework

go
func GinLoggingMiddleware(client *logward.Client) gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()

        c.Next()

        client.Info(c.Request.Context(), "HTTP request", map[string]any{
            "method":      c.Request.Method,
            "path":        c.Request.URL.Path,
            "status":      c.Writer.Status(),
            "duration_ms": time.Since(start).Milliseconds(),
        })
    }
}

// Usage
r := gin.Default()
r.Use(GinLoggingMiddleware(client))

Best Practices

1. Always Defer Close
Use defer client.Close() immediately after creating the client to ensure all buffered logs are flushed on shutdown.
2. Pass Context
Always pass the request context to logging methods. This enables trace correlation and allows logs to be cancelled with the request.
3. Use Global Metadata
Add environment, version, and hostname as global metadata instead of repeating them in every log call.
4. Handle Errors Appropriately
Check for specific errors like ErrCircuitOpen to implement fallback logging strategies when LogWard is unavailable.