Paquete runtime/debug en Go
El paquete runtime/debug proporciona funcionalidades avanzadas para depurar y controlar el comportamiento del runtime de Go. Este documento explora en detalle sus características y casos de uso prácticos.
1. Funciones de Stack Trace
1.1 Impresión de Stack Trace
// filepath: ejemplos/debug/stack_trace.go
package main
import (
"runtime/debug"
"fmt"
"os"
)
func funcionC() {
debug.PrintStack()
}
func funcionB() {
funcionC()
}
func funcionA() {
funcionB()
}
func main() {
// Ejemplo básico de PrintStack
fmt.Println("=== PrintStack ===")
funcionA()
// Ejemplo de Stack con formato personalizado
fmt.Println("\n=== Stack personalizado ===")
stack := debug.Stack()
os.Stdout.Write(stack)
// Ejemplo con información adicional
fmt.Println("\n=== Stack con metadata ===")
fmt.Printf("Goroutine actual: %d\n", debug.Stack())
}
1.2 Captura de Stack en Panic
// filepath: ejemplos/debug/panic_handler.go
package main
import (
"runtime/debug"
"fmt"
"log"
"os"
)
func recuperadorPanic() {
if r := recover(); r != nil {
// Crear archivo de log
f, err := os.Create("crash.log")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// Escribir información del panic
fmt.Fprintf(f, "Panic: %v\n\n", r)
fmt.Fprintf(f, "Stack Trace:\n")
f.Write(debug.Stack())
}
}
func funcionProblematica() {
defer recuperadorPanic()
// Simular un panic
panic("¡Algo salió mal!")
}
2. Gestión de Memoria
2.1 Monitor de Uso de Memoria
// filepath: ejemplos/debug/memory_monitor.go
package main
import (
"runtime/debug"
"fmt"
"time"
)
type MemoryStats struct {
Alloc uint64
TotalAlloc uint64
Sys uint64
NumGC uint32
PauseTotal time.Duration
}
func monitorearMemoria(intervalo time.Duration) {
ticker := time.NewTicker(intervalo)
defer ticker.Stop()
for range ticker.C {
var stats debug.GCStats
debug.ReadGCStats(&stats)
fmt.Printf("\n=== Estadísticas de Memoria ===\n")
fmt.Printf("Última GC: %v\n", stats.LastGC)
fmt.Printf("Num GC: %d\n", stats.NumGC)
fmt.Printf("Pausa Total GC: %v\n", stats.PauseTotal)
fmt.Printf("Pausas GC: %v\n", stats.Pause)
fmt.Printf("PauseEnd: %v\n", stats.PauseEnd)
}
}
2.2 Optimizador de Memoria
// filepath: ejemplos/debug/memory_optimizer.go
package main
import (
"runtime/debug"
"fmt"
"time"
)
type MemoryOptimizer struct {
gcPercent int
maxStackSize int
cleanupEnabled bool
}
func NewMemoryOptimizer() *MemoryOptimizer {
return &MemoryOptimizer{
gcPercent: 100,
maxStackSize: 1 << 20, // 1MB
cleanupEnabled: true,
}
}
func (mo *MemoryOptimizer) Optimize() {
// Configurar GC
oldGCPercent := debug.SetGCPercent(mo.gcPercent)
fmt.Printf("GC Percent cambiado de %d a %d\n", oldGCPercent, mo.gcPercent)
// Configurar tamaño máximo de stack
oldStackSize := debug.SetMaxStack(mo.maxStackSize)
fmt.Printf("Max Stack Size cambiado de %d a %d\n", oldStackSize, mo.maxStackSize)
// Programar limpieza periódica
if mo.cleanupEnabled {
go mo.scheduleCleanup()
}
}
func (mo *MemoryOptimizer) scheduleCleanup() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
debug.FreeOSMemory()
fmt.Println("Limpieza de memoria realizada")
}
}
3. Configuración Avanzada
3.1 Gestor de Threads
// filepath: ejemplos/debug/thread_manager.go
package main
import (
"runtime/debug"
"fmt"
"sync"
)
type ThreadManager struct {
maxThreads int
mu sync.Mutex
}
func NewThreadManager(maxThreads int) *ThreadManager {
return &ThreadManager{
maxThreads: maxThreads,
}
}
func (tm *ThreadManager) Configure() {
tm.mu.Lock()
defer tm.mu.Unlock()
oldMax := debug.SetMaxThreads(tm.maxThreads)
fmt.Printf("Número máximo de threads cambiado de %d a %d\n", oldMax, tm.maxThreads)
}
func (tm *ThreadManager) RunWithThreadLimit(tasks []func()) {
var wg sync.WaitGroup
semaphore := make(chan struct{}, tm.maxThreads)
for _, task := range tasks {
wg.Add(1)
go func(t func()) {
defer wg.Done()
semaphore <- struct{}{}
defer func() { <-semaphore }()
t()
}(task)
}
wg.Wait()
}
4. Utilidades de Depuración
4.1 Logger Personalizado
// filepath: ejemplos/debug/custom_logger.go
package main
import (
"runtime/debug"
"fmt"
"time"
"os"
"path/filepath"
)
type DebugLogger struct {
logDir string
prefix string
}
func NewDebugLogger(logDir, prefix string) (*DebugLogger, error) {
if err := os.MkdirAll(logDir, 0755); err != nil {
return nil, err
}
return &DebugLogger{
logDir: logDir,
prefix: prefix,
}, nil
}
func (dl *DebugLogger) LogWithStack(message string) error {
filename := filepath.Join(dl.logDir,
fmt.Sprintf("%s_%s.log", dl.prefix, time.Now().Format("20060102_150405")))
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
fmt.Fprintf(f, "=== %s ===\n", message)
fmt.Fprintf(f, "Timestamp: %s\n", time.Now().Format(time.RFC3339))
fmt.Fprintf(f, "\nStack Trace:\n")
f.Write(debug.Stack())
return nil
}
5. Mejores Prácticas
- Gestión de Memoria
- Monitorear el uso de memoria regularmente
- Ajustar parámetros de GC según necesidades
- Implementar limpieza periódica
- Debug en Producción
- Capturar y almacenar stack traces
- Implementar recuperación de panics
- Mantener logs detallados
- Optimización
- Ajustar límites de threads
- Configurar tamaños de stack apropiados
- Monitorear estadísticas de GC