Fundamentos de Solución de Problemas en Producción
10 ejemplos para empezar con la Solución de Problemas en Producción - 7 básicos y 3 intermedios.
Busca en todas las páginas de la documentación
10 ejemplos para empezar con la Solución de Problemas en Producción - 7 básicos y 3 intermedios.
mkdir triagelab && cd triagelab && go mod init example.com/triagelab.go get github.com/prometheus/client_golang/prometheus/promhttp al exportar métricas.Los paquetes de soporte necesitan la versión exacta de Go y los metadatos de compilación durante los incidentes.
package main
import (
"log/slog"
"runtime"
"runtime/debug"
)
func main() {
info, _ := debug.ReadBuildInfo()
slog.Info("starting",
"go", runtime.Version(),
"module", info.Main.Path,
"version", info.Main.Version,
)
}runtime.Version() informa la cadena de herramientas que compiló el binario.debug.ReadBuildInfo() incrusta la versión del módulo desde -ldflags o lanzamientos etiquetados.Relacionado: Respuesta a Incidentes para Servicios Go - modelo mental de triaje
Los logs estructurados vinculan mensajes de tiempo de espera dispersos a una solicitud de cliente.
package main
import (
"log/slog"
"net/http"
)
func withRequestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := r.Header.Get("X-Request-ID")
if id == "" {
id = "generated-" + r.URL.Path
}
logger := slog.With("request_id", id)
ctx := r.Context()
_ = logger
next.ServeHTTP(w, r.WithContext(ctx))
})
}X-Request-ID desde la entrada o genera uno por solicitud.slog.With para que cada línea de log en la cadena de manejadores la comparta.request_id durante el triaje de incidentes.Relacionado: Registro Estructurado con slog - manejadores de producción
Los perfiles en tiempo de ejecución son el camino más rápido de "lento" a "¿qué función?".
package main
import (
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
admin := http.NewServeMux()
admin.Handle("/debug/", http.DefaultServeMux)
log.Println(http.ListenAndServe("127.0.0.1:6060", admin))
}()
select {}
}_ "net/http/pprof" registra los manejadores de perfiles estándar en DefaultServeMux.127.0.0.1 o solo a una red interna.go tool pprof http://127.0.0.1:6060/debug/pprof/profile?seconds=20 bajo carga.Relacionado: Perfilado de CPU y Heap en Vivo Bajo Carga de Incidentes - recolección segura para incidentes
Histogramas de Tasa, Errores y Duración responden "¿es esto un problema de Go o de tráfico?".
package main
import (
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var reqDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Buckets: prometheus.DefBuckets,
}, []string{"route", "code"})
func main() {
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
w.WriteHeader(http.StatusOK)
reqDuration.WithLabelValues("/api", "200").Observe(time.Since(start).Seconds())
})
http.ListenAndServe(":8080", nil)
}http_request_duration_seconds potencia los paneles de control p99.Relacionado: Métricas de Prometheus y Paneles RED-USE - diseño de paneles
Un recuento creciente de goroutines a menudo precede a incidentes de memoria y latencia.
package main
import (
"expvar"
"runtime"
)
func init() {
expvar.Publish("goroutines", expvar.Func(func() any {
return runtime.NumGoroutine()
}))
}expvar expone valores en /debug/vars cuando se monta en tu mux de administración.goroutines junto con el heap y la tasa de solicitudes durante los incidentes.Relacionado: Análisis de Volcado de Goroutines y Perfil de Fugas - lectura de pilas
El agotamiento del pool parece tiempos de espera de la aplicación mientras la base de datos está en buen estado.
package main
import (
"database/sql"
"log"
"time"
_ "github.com/jackc/pgx/v5/stdlib"
)
func logDBStats(db *sql.DB) {
ticker := time.NewTicker(30 * time.Second)
for range ticker.C {
s := db.Stats()
log.Printf("db open=%d inUse=%d idle=%d waitCount=%d waitDur=%s",
s.OpenConnections, s.InUse, s.Idle, s.WaitCount, s.WaitDuration)
}
}WaitCount y WaitDuration en aumento significan que los manejadores se bloquean en db.Conn.Relacionado: Agotamiento del Pool de Conexiones de Base de Datos - manual de saturación
La correlación de despliegues es la hipótesis de incidente más rápida.
package main
import (
"encoding/json"
"net/http"
"os"
)
var buildSHA = os.Getenv("BUILD_SHA")
func health(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
"build": buildSHA,
})
}BUILD_SHA desde CI en el entorno del contenedor.Relacionado: Sondas de Salud, Preparación y Vivacidad - semántica de las sondas
Activa un perfil corto desde un punto final seguro durante un incidente activo.
package main
import (
"net/http"
_ "net/http/pprof"
"os"
"time"
)
func profileHandler(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-On-Call-Token") != os.Getenv("ONCALL_TOKEN") {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
http.Redirect(w, r, "/debug/pprof/profile?seconds=20", http.StatusTemporaryRedirect)
}Relacionado: Perfilado de CPU y Heap con pprof - lectura de grafos de llamas
Reiniciar a mitad de una solicitud causa picos falsos de 5xx que parecen regresiones de código.
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
srv := &http.Server{Addr: ":8080"}
go srv.ListenAndServe()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal(err)
}
}Shutdown deja de aceptar nuevas conexiones y espera las solicitudes en curso.terminationGracePeriodSeconds de Kubernetes.Relacionado: Apagado Elegante y Manejo de Señales - patrones de producción
Los post-mortems necesitan archivos, no solo capturas de pantalla.
mkdir -p /tmp/incident-$(date +%Y%m%d%H%M)
curl -s "http://127.0.0.1:6060/debug/pprof/profile?seconds=20" > /tmp/incident-*/cpu.prof
curl -s "http://127.0.0.1:6060/debug/pprof/heap" > /tmp/incident-*/heap.prof
curl -s "http://127.0.0.1:6060/debug/pprof/goroutine?debug=2" > /tmp/incident-*/goroutine.txt
go tool pprof -top /tmp/incident-*/cpu.prof > /tmp/incident-*/cpu-top.txtRelacionado: Plantilla de Post-Mortem para Incidentes Go - estructura de RCA
Versiones de la pila: Esta página fue escrita para Go 1.26.x (predeterminado Green Tea GC, go fix modernizers - verificar parche en la compilación), chi (última versión - verificar en la compilación), gin (última versión - verificar en la compilación), echo (última versión - verificar en la compilación), google.golang.org/grpc (última versión - verificar en la compilación), sigs.k8s.io/controller-runtime (última versión - verificar en la compilación), kubebuilder (última versión - verificar en la compilación), tinygo (última versión - verificar objetivos de placa en la compilación), wazero (última versión - verificar en la compilación) y golangci-lint (última versión - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 18 jul 2026