This guide shows how to integrate the structured logger into your Go application.
go get github.com/MohaCodez/structured-loggerpackage main
import (
"github.com/MohaCodez/structured-logger/logger"
)
func main() {
// Create logger
log := logger.New(logger.INFO)
defer log.Close()
// Use throughout application
log.Info("application_started")
// Your application code
processRequests(log)
}
func processRequests(log *logger.Logger) {
log.Debug("processing_started")
// ... business logic
log.Info("request_completed", "duration_ms", 45)
}package main
import (
"os"
"github.com/MohaCodez/structured-logger/formatter"
"github.com/MohaCodez/structured-logger/logger"
"github.com/MohaCodez/structured-logger/sink"
)
func main() {
log := setupLogger()
defer log.Close()
log.Info("server_starting", "port", 8080)
// ... rest of application
}
func setupLogger() *logger.Logger {
// Determine environment
env := os.Getenv("ENV")
// Configure based on environment
config := logger.DefaultConfig()
if env == "production" {
config.Level = logger.INFO
config.EnableCaller = false
config.Async = true
config.BufferSize = 500
// Add file sink
fileSink, err := sink.NewFileSink("/var/log/app/app.log")
if err != nil {
panic(err)
}
config.Sinks = []logger.Sink{
sink.NewConsoleSink(),
fileSink,
}
} else {
// Development settings
config.Level = logger.DEBUG
config.EnableCaller = true
config.Async = false
}
config.Formatter = formatter.NewJSONFormatter()
return logger.NewWithConfig(config)
}package main
import (
"net/http"
"time"
"github.com/MohaCodez/structured-logger/logger"
)
type Server struct {
log *logger.Logger
}
func main() {
log := logger.New(logger.INFO)
defer log.Close()
server := &Server{log: log}
http.HandleFunc("/api/users", server.loggingMiddleware(server.handleUsers))
log.Info("server_listening", "port", 8080)
http.ListenAndServe(":8080", nil)
}
func (s *Server) loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
s.log.Info("request_started",
"method", r.Method,
"path", r.URL.Path,
"remote_addr", r.RemoteAddr,
)
next(w, r)
s.log.Info("request_completed",
"method", r.Method,
"path", r.URL.Path,
"duration_ms", time.Since(start).Milliseconds(),
)
}
}
func (s *Server) handleUsers(w http.ResponseWriter, r *http.Request) {
s.log.Debug("fetching_users")
// Business logic
users := fetchUsers()
if len(users) == 0 {
s.log.Warn("no_users_found")
}
s.log.Info("users_fetched", "count", len(users))
// Send response
w.Write([]byte("users"))
}
func fetchUsers() []string {
return []string{"alice", "bob"}
}package main
import (
"github.com/MohaCodez/structured-logger/logger"
)
type App struct {
log *logger.Logger
service *UserService
}
type UserService struct {
log *logger.Logger
}
func main() {
log := logger.New(logger.INFO)
defer log.Close()
app := NewApp(log)
app.Run()
}
func NewApp(log *logger.Logger) *App {
return &App{
log: log,
service: NewUserService(log),
}
}
func (a *App) Run() {
a.log.Info("app_started")
a.service.CreateUser("alice")
}
func NewUserService(log *logger.Logger) *UserService {
return &UserService{log: log}
}
func (s *UserService) CreateUser(name string) {
s.log.Info("creating_user", "name", name)
// Business logic
if err := validateUser(name); err != nil {
s.log.Error("user_validation_failed",
"name", name,
"error", err.Error(),
)
return
}
s.log.Info("user_created", "name", name)
}
func validateUser(name string) error {
return nil
}package main
import (
"errors"
"github.com/MohaCodez/structured-logger/logger"
)
func main() {
log := logger.New(logger.INFO)
defer log.Close()
if err := processPayment(log, "txn_123", 99.99); err != nil {
log.Error("payment_failed",
"transaction_id", "txn_123",
"error", err.Error(),
)
}
}
func processPayment(log *logger.Logger, txnID string, amount float64) error {
log.Info("payment_processing",
"transaction_id", txnID,
"amount", amount,
)
// Simulate error
if amount > 100 {
log.Warn("high_value_transaction",
"transaction_id", txnID,
"amount", amount,
)
}
// Business logic
if err := chargeCard(amount); err != nil {
log.Error("card_charge_failed",
"transaction_id", txnID,
"amount", amount,
"error", err.Error(),
)
return err
}
log.Info("payment_completed",
"transaction_id", txnID,
"amount", amount,
)
return nil
}
func chargeCard(amount float64) error {
return errors.New("insufficient funds")
}package main
import (
"sync"
"time"
"github.com/MohaCodez/structured-logger/logger"
)
func main() {
log := logger.New(logger.INFO)
defer log.Close()
jobs := make(chan int, 100)
// Start workers
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(log, i, jobs, &wg)
}
// Send jobs
for j := 0; j < 20; j++ {
jobs <- j
}
close(jobs)
wg.Wait()
log.Info("all_workers_completed")
}
func worker(log *logger.Logger, id int, jobs <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
log.Info("worker_started", "worker_id", id)
for job := range jobs {
log.Debug("processing_job",
"worker_id", id,
"job_id", job,
)
time.Sleep(100 * time.Millisecond)
log.Info("job_completed",
"worker_id", id,
"job_id", job,
)
}
log.Info("worker_stopped", "worker_id", id)
}package main
import (
"testing"
"github.com/MohaCodez/structured-logger/logger"
)
// Mock sink for testing
type testSink struct {
logs []string
}
func (t *testSink) Write(data []byte) error {
t.logs = append(t.logs, string(data))
return nil
}
func (t *testSink) Close() error {
return nil
}
func TestUserService(t *testing.T) {
// Create logger with test sink
sink := &testSink{}
config := logger.DefaultConfig()
config.Level = logger.DEBUG
config.Sinks = []logger.Sink{sink}
log := logger.NewWithConfig(config)
service := NewUserService(log)
service.CreateUser("alice")
// Verify logs
if len(sink.logs) == 0 {
t.Error("expected logs to be written")
}
}- Create logger at application startup
- Pass logger to components via dependency injection
- Always defer
log.Close()in main
- DEBUG: Development details, verbose
- INFO: Normal operations, user actions
- WARN: Unexpected but handled conditions
- ERROR: Errors that need attention
- FATAL: Critical errors, application exits
- Use consistent field names across application
- Include request IDs for tracing
- Add user context when available
- Include timing information
- Use async mode for high-throughput applications
- Disable caller tracing in production
- Set appropriate log level (INFO or WARN in prod)
- Always include error message in fields
- Add relevant context (IDs, values)
- Log at appropriate level
func handleRequest(log *logger.Logger, requestID string) {
log.Info("request_started", "request_id", requestID)
// ... processing
log.Info("request_completed", "request_id", requestID)
}start := time.Now()
// ... operation
log.Info("operation_completed", "duration_ms", time.Since(start).Milliseconds())if result.Count > threshold {
log.Warn("threshold_exceeded", "count", result.Count, "threshold", threshold)
}- Check log level configuration
- Verify sinks are properly initialized
- Ensure
Close()is called (flushes async queue)
- Enable async mode
- Reduce log volume (increase level threshold)
- Disable caller tracing
- Check file permissions
- Verify directory exists
- Ensure disk space available