Merge pull request #1918 from cytown/panic
Merging - approved after code review with Codex adversarial check. All CI checks passed.
This commit is contained in:
commit
c3285625b0
10 changed files with 163 additions and 7 deletions
|
|
@ -34,7 +34,7 @@ func NewGatewayCommand() *cobra.Command {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
RunE: func(_ *cobra.Command, _ []string) error {
|
RunE: func(_ *cobra.Command, _ []string) error {
|
||||||
return gateway.Run(debug, internal.GetConfigPath(), allowEmpty)
|
return gateway.Run(debug, internal.GetPicoclawHome(), internal.GetConfigPath(), allowEmpty)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@ const (
|
||||||
serviceShutdownTimeout = 30 * time.Second
|
serviceShutdownTimeout = 30 * time.Second
|
||||||
providerReloadTimeout = 30 * time.Second
|
providerReloadTimeout = 30 * time.Second
|
||||||
gracefulShutdownTimeout = 15 * time.Second
|
gracefulShutdownTimeout = 15 * time.Second
|
||||||
|
|
||||||
|
logPath = "logs"
|
||||||
|
panicFile = "gateway_panic.log"
|
||||||
|
logFile = "gateway.log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type services struct {
|
type services struct {
|
||||||
|
|
@ -79,7 +83,19 @@ func (p *startupBlockedProvider) GetDefaultModel() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts the gateway runtime using the configuration loaded from configPath.
|
// Run starts the gateway runtime using the configuration loaded from configPath.
|
||||||
func Run(debug bool, configPath string, allowEmptyStartup bool) error {
|
func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) error {
|
||||||
|
panicPath := filepath.Join(homePath, logPath, panicFile)
|
||||||
|
panicFunc, err := logger.InitPanic(panicPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error initializing panic log: %w", err)
|
||||||
|
}
|
||||||
|
defer panicFunc()
|
||||||
|
|
||||||
|
if err = logger.EnableFileLogging(filepath.Join(homePath, logPath, logFile)); err != nil {
|
||||||
|
panic(fmt.Sprintf("error enabling file logging: %v", err))
|
||||||
|
}
|
||||||
|
defer logger.DisableFileLogging()
|
||||||
|
|
||||||
cfg, err := config.LoadConfig(configPath)
|
cfg, err := config.LoadConfig(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error loading config: %w", err)
|
return fmt.Errorf("error loading config: %w", err)
|
||||||
|
|
|
||||||
36
pkg/logger/panic.go
Normal file
36
pkg/logger/panic.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime/debug"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitPanic(filePath string) (func(), error) {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create log directory: %w", err)
|
||||||
|
}
|
||||||
|
writer := initPanicFile(filePath)
|
||||||
|
if writer == nil {
|
||||||
|
return nil, fmt.Errorf("failed to create log file: %s", filePath)
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
defer writer.Close()
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
now := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
stack := debug.Stack()
|
||||||
|
logMsg := "\n\n====================\n[" + now + "] PANIC OCCURRED: " + fmt.Sprintf(
|
||||||
|
"%v",
|
||||||
|
err,
|
||||||
|
) + "\n" + string(
|
||||||
|
stack,
|
||||||
|
)
|
||||||
|
|
||||||
|
writer.Write([]byte(logMsg))
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
21
pkg/logger/panic_unix.go
Normal file
21
pkg/logger/panic_unix.go
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initPanicFile(panicFile string) io.WriteCloser {
|
||||||
|
file, err := os.OpenFile(panicFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND|os.O_SYNC, 0o600)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error in open panic: %v", err))
|
||||||
|
}
|
||||||
|
if err = Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {
|
||||||
|
panic(fmt.Sprintf("error in syscall.Dup2: %v", err))
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
25
pkg/logger/panic_win.go
Normal file
25
pkg/logger/panic_win.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initPanicFile(panicFile string) io.WriteCloser {
|
||||||
|
file, err := os.OpenFile(panicFile, os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0600)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error in open panic: %v", err))
|
||||||
|
}
|
||||||
|
err = windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(file.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Failed to redirect stderr to file: %v", err))
|
||||||
|
}
|
||||||
|
os.Stderr = file
|
||||||
|
return file
|
||||||
|
}
|
||||||
12
pkg/logger/syscall_amd64.go
Normal file
12
pkg/logger/syscall_amd64.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build linux && amd64
|
||||||
|
// +build linux,amd64
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dup2(oldfd int, newfd int) error {
|
||||||
|
return syscall.Dup2(oldfd, newfd)
|
||||||
|
}
|
||||||
12
pkg/logger/syscall_arm64.go
Normal file
12
pkg/logger/syscall_arm64.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build linux && arm64
|
||||||
|
// +build linux,arm64
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dup2(oldfd int, newfd int) error {
|
||||||
|
return syscall.Dup3(oldfd, newfd, 0)
|
||||||
|
}
|
||||||
12
pkg/logger/syscall_darwin.go
Normal file
12
pkg/logger/syscall_darwin.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build darwin
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dup2(oldfd int, newfd int) error {
|
||||||
|
return syscall.Dup2(oldfd, newfd)
|
||||||
|
}
|
||||||
12
pkg/logger/syscall_loong64.go
Normal file
12
pkg/logger/syscall_loong64.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build linux && loong64
|
||||||
|
// +build linux,loong64
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dup2(oldfd int, newfd int) error {
|
||||||
|
return syscall.Dup3(oldfd, newfd, 0)
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,10 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
appName = "PicoClaw"
|
appName = "PicoClaw"
|
||||||
|
|
||||||
|
logPath = "logs"
|
||||||
|
panicFile = "launcher_panic.log"
|
||||||
|
logFile = "launcher.log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -72,6 +76,14 @@ func main() {
|
||||||
|
|
||||||
// Initialize logger
|
// Initialize logger
|
||||||
picoHome := utils.GetPicoclawHome()
|
picoHome := utils.GetPicoclawHome()
|
||||||
|
|
||||||
|
f := filepath.Join(picoHome, logPath, panicFile)
|
||||||
|
panicFunc, err := logger.InitPanic(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error initializing panic log: %v", err))
|
||||||
|
}
|
||||||
|
defer panicFunc()
|
||||||
|
|
||||||
// By default, detect terminal to decide console log behavior
|
// By default, detect terminal to decide console log behavior
|
||||||
// If -console-logs flag is explicitly set, it overrides the detection
|
// If -console-logs flag is explicitly set, it overrides the detection
|
||||||
enableConsole := *console
|
enableConsole := *console
|
||||||
|
|
@ -79,11 +91,9 @@ func main() {
|
||||||
// Disable console logging by setting level to Fatal (no output)
|
// Disable console logging by setting level to Fatal (no output)
|
||||||
logger.SetConsoleLevel(logger.FATAL)
|
logger.SetConsoleLevel(logger.FATAL)
|
||||||
|
|
||||||
logPath := filepath.Join(picoHome, "logs", "web.log")
|
f := filepath.Join(picoHome, logPath, logFile)
|
||||||
if err := logger.EnableFileLogging(logPath); err != nil {
|
if err = logger.EnableFileLogging(f); err != nil {
|
||||||
// FIXME: https://github.com/sipeed/picoclaw/issues/1734
|
panic(fmt.Sprintf("error enabling file logging: %v", err))
|
||||||
fmt.Fprintf(os.Stderr, "Failed to initialize logger: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
defer logger.DisableFileLogging()
|
defer logger.DisableFileLogging()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue