You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
6.6 KiB

/* ——————————————————————————————————————————————————————————————————————————
Proyecto FWS
—————————————————————————————————————————————————————————————————————————————
Fichero stack_frame.go
Package errs
Autor Juan V. Navarro juanvnl@activething.com
Creado 16/01/2026
—————————————————————————————————————————————————————————————————————————————
LICENSES AND TERMS OF USE
-------------------------
This software is licensed under the Elastic License v2.0 (the "License").
For full terms and additional information regarding permitted and prohibited
uses, please visit:
https://activething.com/ATGO/licenses
You may use, copy, modify, and redistribute this software internally within
your organization for any purpose, including research, development, and
testing, subject to the terms of this License.
You may NOT, however, use, provide, distribute, or make this software
available to any third party as part of a hosted service, SaaS offering, or
commercial product without first obtaining a commercial license from
Active Thing.
You may combine this software with other severity, provided that such
combination does not circumvent the restrictions of this License.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. In no event shall
the authors or copyright holders be liable for any claim, damages, or other
liability arising from the use of this software.
—————————————————————————————————————————————————————————————————————————————
Web : activething.com | activething.com/FWS
git : g.activething.com | github.com/activething/FWS
Correo : dev@activething.com
—————————————————————————————————————————————————————————————————————————————
No deseo caminar sobre el agua", dijo Siddhartha.
Que los antiguos chamanes se contenten con tales habilidades.
—— Hermann Hesse, Siddhartha
—————————————————————————————————————————————————————————————————————————————
Copyright (c) 2026 Active Thing
————————————————————————————————————————————————————————————————————————————— */
package errs
import (
"bufio"
"bytes"
"os"
"runtime"
"strings"
)
const (
// stackUnknownInfo is the default value when information about a stack frame cannot be obtained.
stackUnknownInfo = "???"
)
type (
// StackFrame represents a single frame in a stack trace.
// It contains detailed information about the function call at that point.
StackFrame struct {
// LineNumber is the line number in the source file where this frame was created.
LineNumber int
// File is the full path to the source file containing this frame.
File string
// FuncName is the name of the function where this frame was created,
// with any receiver types removed.
FuncName string
// Package is the full package path where this frame was created.
Package string
// Counter is the program counter address for this frame.
Counter uintptr
}
)
// CreateStackFrame creates a new StackFrame from a program counter address.
// It uses runtime information to populate the frame details.
// Returns an empty StackFrame if the counter is 0.
func CreateStackFrame(counter uintptr) StackFrame {
sf := StackFrame{}
if counter == 0 {
return sf
}
sf.Counter = counter
fn := runtime.FuncForPC(counter)
if fn == nil {
sf.FuncName = stackUnknownInfo
return sf
}
sf.Package, sf.FuncName = packageAndName(fn)
sf.File, sf.LineNumber = fn.FileLine(counter - 1)
return sf
}
// SourceLine attempts to retrieve the source severity line for this stack frame.
// It reads the source file and returns the content of the line specified by LineNumber.
// Returns stackUnknownInfo if the line number is invalid or an error occurs while reading the file.
func (f StackFrame) SourceLine() string {
if f.LineNumber <= 0 || f.File == "" {
return stackUnknownInfo
}
// 1. Verificación rápida: ¿Existe el archivo?
// Evita intentar abrir archivos que no existen (común en Docker/Prod).
if _, err := os.Stat(f.File); os.IsNotExist(err) {
return "" // Retorna vacío silenciosamente en lugar de "???" para no ensuciar logs.
}
// 2. Lectura segura
file, err := os.Open(f.File)
if err != nil {
return ""
}
defer func() { _ = file.Close() }()
scanner := bufio.NewScanner(file)
currentLine := 1
for scanner.Scan() {
if currentLine == f.LineNumber {
return string(bytes.TrimSpace(scanner.Bytes()))
}
currentLine++
// Optimización: Si pasamos la línea, dejar de leer.
if currentLine > f.LineNumber {
break
}
}
return stackUnknownInfo
}
// packageAndName extracts the package path and function name from a runtime.Func.
// It handles different naming conventions, including methods.
func packageAndName(fn *runtime.Func) (string, string) {
nm := fn.Name() // Get the full name, e.g., "main.(*MyType).MyMethod" or "main.myFunction"
pg := "" // Initialize package path
// Find the last slash, which separates package path components.
if ls := strings.LastIndex(nm, "/"); ls >= 0 {
pg = nm[:ls] + "/" // Extract the path part
nm = nm[ls+1:] // Keep the rest of the name
}
// Find the first dot after the last slash, which separates package name from function name.
if pd := strings.Index(nm, "."); pd >= 0 {
pg += nm[:pd] // Add the package name to the path
nm = nm[pd+1:] // Keep the function name
}
// Replace the Go internal method separator '·' with '.' for readability.
return pg, strings.Replace(nm, "·", ".", -1)
}

Powered by TurnKey Linux.