diff --git a/core/errs/dispatcher.go b/core/errs/dispatcher.go new file mode 100644 index 0000000..ce57abb --- /dev/null +++ b/core/errs/dispatcher.go @@ -0,0 +1,92 @@ +// ============================================================================= +// Active-GO Framework +// Copyright (c) 2025 ActiveThing (https://activething.com) +// Author: Juan V. Navarro juanvnl@activething.com +// ============================================================================= +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// ============================================================================= +// Date Create 19/05/2025 +// ============================================================================= + +package errs + +import ( + "fmt" + "sync" +) + +type ( + + Dispatcher struct { + mu sync.RWMutex + processors map[string]Processor + formatters map[string]Formatter + bufferSize int + + cacheMu sync.Mutex + cacheEnable map[string]struct{} + formatCache map[string]map[uintptr][]byte + } + + DispatcherFnc func (dispatcher *Dispatcher) error +) + + +func NewDispatcher(options ...DispatcherFnc) (*Dispatcher,error) { + dp := &Dispatcher{ + processors : make(map[string]Processor), + formatters : make(map[string]Formatter), + formatCache: make(map[string]map[uintptr][]byte), + cacheEnable: make(map[string]struct{}), + } + for _,o := range options { + if e := o(dp); e != nil { + return nil,e + } + } + return dp,nil +} + + +func (d *Dispatcher) RegisterProcessor(processor Processor, options ...ProcessorEntryFnc) error { + d.mu.Lock() + defer d.mu.Unlock() + + if processor == nil { + return CreateParamNilErr("processor") + } + if _,ex := d.formatters[processor.FormatterID()]; !ex { + // + //todo create newerror formatter_not_registered + } + if _,ex := d.processors[processor.ID()]; ex { + // + //todo create newerror processor_registered + } + + pe,er := NewProcessorEntry(options...) + if er != nil { + return er + } + d.processors[processor.ID()] = pe + + return nil +} \ No newline at end of file diff --git a/core/errs/formatter.go b/core/errs/formatter.go new file mode 100644 index 0000000..0cc14c8 --- /dev/null +++ b/core/errs/formatter.go @@ -0,0 +1,47 @@ +// ============================================================================= +// Active-GO Framework +// Copyright (c) 2025 ActiveThing (https://activething.com) +// Author: Juan V. Navarro juanvnl@activething.com +// ============================================================================= +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// ============================================================================= +// Date Create 20/05/2025 +// ============================================================================= + +package errs + + +type ( + + Formatter interface { + Format (err error) ([]byte,error) + } + + + FormatFnc func (error) ([]byte,error) +) + + + +func (f FormatFnc) Format (err error) ([]byte,error) { return f(err) } + + + diff --git a/core/errs/processor.go b/core/errs/processor.go new file mode 100644 index 0000000..5adf298 --- /dev/null +++ b/core/errs/processor.go @@ -0,0 +1,43 @@ +// ============================================================================= +// Active-GO Framework +// Copyright (c) 2025 ActiveThing (https://activething.com) +// Author: Juan V. Navarro juanvnl@activething.com +// ============================================================================= +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// ============================================================================= +// Date Create 19/05/2025 +// ============================================================================= + +package errs + + +type ( + + Processor interface { + ID () string + FormatterID () string + CanProcess (err error) bool + Process (data []byte) error + State () ProcessorState + } + +) + diff --git a/core/errs/processor_entry.go b/core/errs/processor_entry.go new file mode 100644 index 0000000..8cc758e --- /dev/null +++ b/core/errs/processor_entry.go @@ -0,0 +1,55 @@ +// ============================================================================= +// Active-GO Framework +// Copyright (c) 2025 ActiveThing (https://activething.com) +// Author: Juan V. Navarro juanvnl@activething.com +// ============================================================================= +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// ============================================================================= +// Date Create 20/05/2025 +// ============================================================================= + +package errs + +import "time" + +type ( + + ProcessorEntry struct { + processor Processor + channel chan error + state ProcessorState + failures int + maxFailures int + blockedTime time.Time + blockTime time.Time + errors []error + } + + ProcessorEntryFnc func(*ProcessorEntry) error +) + + +func NewProcessorEntry (options...ProcessorEntryFnc) (*ProcessorEntry,error) { + pe := &ProcessorEntry { + + } + return pe,nil +} \ No newline at end of file diff --git a/core/errs/processor_state.go b/core/errs/processor_state.go new file mode 100644 index 0000000..3991650 --- /dev/null +++ b/core/errs/processor_state.go @@ -0,0 +1,36 @@ +// ============================================================================= +// Active-GO Framework +// Copyright (c) 2025 ActiveThing (https://activething.com) +// Author: Juan V. Navarro juanvnl@activething.com +// ============================================================================= +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// 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 NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// ============================================================================= +// Date Create 19/05/2025 +// ============================================================================= + +package errs + + + +type ProcessorState uint8 + + + diff --git a/core/errs/readme.md b/core/errs/readme.md index f0877e3..0983063 100644 --- a/core/errs/readme.md +++ b/core/errs/readme.md @@ -169,7 +169,7 @@ if errsList.Any() { --- **Repositorio oficial:** [Active-Go Core](https://github.com/active-go/core) -**Documentación:** [https://docs.active-go.com/errors](https://docs.active-go.com/errors) +**Documentación:** [https://docs.active-go.com/errors](https://g.activething.com/errors) --- diff --git a/core/excs/stack.go b/core/excs/stack.go index b18dfa9..b6ad84f 100644 --- a/core/excs/stack.go +++ b/core/excs/stack.go @@ -1,3 +1,4 @@ + // ============================================================================= // Active-GO Framework // Copyright (c) 2025 ActiveThing (https://activething.com) @@ -26,51 +27,64 @@ // Date Create 13/05/2025 // ============================================================================= +// Package excs provides utilities for capturing and working with execution contexts, +// primarily focused on stack traces. This package is used by the errs package +// to include stack trace information in structured errors. package excs import ( "runtime" ) -// Constantes utilizadas para la configuración y manejo de stack traces +// Constants used for stack trace configuration and handling. const ( - // stackSkipFrames define cuántos frames iniciales se omiten al capturar el stack - // Se omiten 2 frames para excluir las llamadas internas del sistema + // stackSkipFrames defines how many initial frames are skipped when capturing the stack. + // 2 frames are skipped to exclude internal system calls (runtime.Callers and this function). stackSkipFrames = 2 - // stackMaxFrames define el número máximo de frames que se capturarán - // Limita la profundidad del stack trace a 32 frames + // stackMaxFrames defines the maximum number of frames that will be captured. + // Limits the depth of the stack trace to 32 frames by default. stackMaxFrames = 32 ) +// Stack is a slice of program counters, representing a captured stack trace. type Stack []uintptr +// CreateStack captures the current execution stack trace. +// It takes an offset (number of frames to skip in addition to the default) +// and maxFrames (maximum number of frames to capture). +// Returns a Stack slice containing the captured program counters. func CreateStack(offset, maxFrames int) Stack { - // Create a slice to store program counters with maximum capacity + // Determine the effective maximum number of frames to capture. mx := stackMaxFrames if maxFrames > 0 { mx = maxFrames } + // Create a slice to store program counters with maximum capacity. ls := make([]uintptr, mx) - // Ensure offset is not negative + // Ensure offset is not negative. if offset < 0 { offset = 0 } - // Capture the stack trace using runtime.Callers - // cn will contain the actual number of frames captured + // Capture the stack trace using runtime.Callers. + // runtime.Callers fills the slice 'ls' with program counters, + // skipping 'offset + stackSkipFrames' frames. + // 'cn' will contain the actual number of frames captured. cn := runtime.Callers(offset+stackSkipFrames, ls) - // Return only the slice containing valid program counters + // Return only the slice containing valid program counters (up to 'cn'). return ls[:cn] } +// Frames converts the Stack (slice of program counters) into a slice of StackFrame objects. +// Each StackFrame provides detailed information about a single frame in the stack. func (s Stack) Frames() []StackFrame { - // Create a slice to store Frame objects + // Create a slice to store StackFrame objects with the same length as the stack. f := make([]StackFrame, len(s)) - // Convert each program counter to a Frame object + // Convert each program counter to a StackFrame object. for i, p := range s { f[i] = CreateStackFrame(p) } diff --git a/core/excs/stack_frame.go b/core/excs/stack_frame.go index e221191..c321b7d 100644 --- a/core/excs/stack_frame.go +++ b/core/excs/stack_frame.go @@ -37,79 +37,108 @@ import ( ) const ( - // stackUnknownInfo es el valor por defecto cuando no se puede obtener - // información sobre un frame del stack + // 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 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 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 + // with any receiver types removed. FuncName string - // Package is the full package path where this frame was created + // Package is the full package path where this frame was created. Package string - // Counter is the program counter address for this frame + // Counter is the program counter address for this frame. Counter uintptr - // files is an internal cache of file contents + // files is an internal cache of file contents. + // Note: This field is present but not used in the current implementation of SourceLine. files map[string][]byte } ) +// 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 + // Get runtime.Func for the program counter. fn := runtime.FuncForPC(counter) + // Extract package and function name. sf.Package, sf.FuncName = packageAndName(fn) + // Get file and line number. Note: counter - 1 is used to get the call site. sf.File, sf.LineNumber = fn.FileLine(counter - 1) return sf } +// SourceLine attempts to retrieve the source code 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, error) { + // Return unknown info if line number is not valid. if f.LineNumber <= 0 { return stackUnknownInfo, nil } + // Open the source file. file, err := os.Open(f.File) if err != nil { - return "", err + return "", err // Return error if file cannot be opened. } + defer file.Close() // Ensure the file is closed. + scanner := bufio.NewScanner(file) currentLine := 1 + // Scan the file line by line. for scanner.Scan() { + // If the current line matches the frame's line number. if currentLine == f.LineNumber { + // Return the trimmed line content as a string. return string(bytes.Trim(scanner.Bytes(), " \t")), nil } currentLine++ } - _ = file.Close() + + // Check for scanner errors. if err := scanner.Err(); err != nil { - return "", err + return "", err // Return scanner error. } + + // If the line number was not found (e.g., file shorter than expected). return stackUnknownInfo, nil } +// 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() - pg := "" + 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] + "/" - nm = nm[ls+1:] + 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] - nm = nm[pd+1:] + 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) }