Cómo crear un sistema de logs en Go

Clase 27 de 30Curso de Ciberseguridad para Desarrollo Web

Resumen

Crear logs útiles no es opcional: es la base de la observabilidad. Con un sistema de logs consistente en Go, podrás medir con precisión en CloudWatch (y herramientas como New Relic, Datadog o Kibana) lo que de verdad importa. Aquí verás cómo estructurar niveles de log, mensajes claros y atributos ricos en JSON para monitorear tu servicio con confianza.

¿Por qué crear un sistema de logs escalable en Go?

Los logs permiten saber qué sucede en el sistema y, sobre todo, medir. Porque lo que no se loguea no se puede medir. El objetivo: logs consistentes, claros, encontrables y accionables.

  • Consistentes: mismo formato y campos en todos los servicios.
  • Claros: sin ambigüedad en el mensaje.
  • Encontrables: con event name estable y service name explícito.
  • Accionables: suficientes datos en attributes para tomar decisiones.

Este enfoque facilita filtrar y analizar en plataformas como CloudWatch.

¿Cómo estructurar el logger con niveles y JSON?

Se crea una carpeta Logger con un paquete logger que expone una estructura Logger, un constructor y métodos por nivel de log. El mensaje se serializa a JSON para que sea encontrable y filtrable en diferentes plataformas.

¿Qué niveles de log usar?

Se define un "enum" (constantes de tipo string) llamado LogLevel con: fatal, error, warning e info. Cada nivel sirve para comunicar gravedad y guiar acciones.

package logger

import (
    "context"
    "encoding/json"
    "fmt"
)

type LogLevel string

const (
    LevelFatal   LogLevel = "fatal"
    LevelError   LogLevel = "error"
    LevelWarning LogLevel = "warning"
    LevelInfo    LogLevel = "info"
)

type Logger struct {
    serviceName string
}

func NewLogger(serviceName string) *Logger {
    return &Logger{serviceName: serviceName}
}

func (l *Logger) log(ctx context.Context, event string, level LogLevel, attrs map[string]interface{}) {
    out := map[string]interface{}{
        "event":      event,
        "level":      level,
        "service":    l.serviceName,
        "properties": attrs,
    }

    b, _ := json.Marshal(out)
    fmt.Println(string(b))
}

func (l *Logger) Info(ctx context.Context, event string, attrs map[string]interface{}) {
    l.log(ctx, event, LevelInfo, attrs)
}

func (l *Logger) Warning(ctx context.Context, event string, attrs map[string]interface{}) {
    l.log(ctx, event, LevelWarning, attrs)
}

func (l *Logger) Error(ctx context.Context, event string, err error, attrs map[string]interface{}) {
    if attrs == nil {
        attrs = map[string]interface{}{}
    }
    attrs["error"] = err.Error()
    l.log(ctx, event, LevelError, attrs)
}

func (l *Logger) Fatal(ctx context.Context, event string, attrs map[string]interface{}) {
    l.log(ctx, event, LevelFatal, attrs)
}
  • Logger: incluye el service name para identificar el origen del log.
  • log: método base que imprime un objeto JSON con event, level, service y properties.
  • Info/Warning/Error/Fatal: métodos convenientes que estandarizan el level. En Error, el error se añade a attributes.

¿Cómo definir el event name y los atributos?

  • Usa un event name estable y buscable: no pongas el detalle dinámico del error ahí.
  • Enriquécelo con attributes: datos clave como head_commit para contexto.
  • Beneficio: rápido filtrado y métricas confiables por evento.

¿Cómo integrarlo en una Lambda y monitorear en CloudWatch?

Se inicializa el logger al inicio de la lambda handleGitHubWebhook con su service name. Luego se reemplazan los fmt.Print por logger.Info usando un event name estándar y un mapa de attributes.

// Dentro de la Lambda handleGitHubWebhook
log := logger.NewLogger("handleGitHubWebhook")
attrs := map[string]interface{}{
    "head_commit": /* dato del evento */ nil,
}

log.Info(ctx, "validGitHubWebhookReceived", attrs)
log.Info(ctx, "commitCreated", attrs)
  • validGitHubWebhookReceived: permite medir cuántos webhooks válidos llegan.
  • commitCreated: confirma la creación del commit y su traza asociada.

¿Cómo desplegar y verificar en CloudWatch?

  • Publica el artefacto en S3: make publish.
  • Revisa el plan: terraform plan.
  • Aplica cambios: terraform apply y confirma con yes.
  • Genera tráfico: git commit, push y usa “redeliver” en el webhook de GitHub.
  • Observa en AWS Lambda > Monitor > CloudWatch > log stream: verás logs con event, level, properties y service en formato JSON legible.

¿Qué habilidades y keywords refuerzas?

  • Diseño de observabilidad con logs consistentes y accionables.
  • Modelado de LogLevel y mensajes en JSON.
  • Uso de event name estable y attributes enriquecidos.
  • Implementación de Logger reutilizable en Go con NewLogger y métodos Info/Warning/Error/Fatal.
  • Integración en AWS Lambda y lectura en CloudWatch.
  • Despliegue con Terraform y prueba con GitHub Webhook y “redeliver”.

¿Te gustaría comentar cómo nombrarías tus eventos y qué atributos añadirías para mejorar la observabilidad de tu servicio?