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
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)
|
|
}
|