feat(frontend):增加前端错误捕获和后端panic处理

- 在前端 App.vue、settings.vue 和 stock.vue 中添加 window.onerror 事件处理器,捕获前端错误并发送给后端
- 在后端 app.go 和 openai_api.go 中添加 panic 处理逻辑,捕获并记录 panic错误
- 在 main.go 中添加 PanicHandler 函数,用于捕获和处理全局 panic
This commit is contained in:
spark 2025-02-10 17:46:42 +08:00
parent 22111411c3
commit b4c55ce233
6 changed files with 80 additions and 7 deletions

8
app.go
View File

@ -41,6 +41,10 @@ func NewApp() *App {
// startup is called at application startup
func (a *App) startup(ctx context.Context) {
defer PanicHandler()
runtime.EventsOn(ctx, "frontendError", func(optionalData ...interface{}) {
logger.SugaredLogger.Errorf("Frontend error: %v\n", optionalData)
})
logger.SugaredLogger.Infof("Version:%s", Version)
// Perform your setup here
a.ctx = ctx
@ -86,6 +90,8 @@ func checkUpdate(a *App) {
// domReady is called after front-end resources have been loaded
func (a *App) domReady(ctx context.Context) {
defer PanicHandler()
// Add your action here
//定时更新数据
go func() {
@ -321,6 +327,7 @@ func addStockFollowData(follow data.FollowedStock, stockData *data.StockInfo) {
// either by clicking the window close button or calling runtime.Quit.
// Returning true will cause the application to continue, false will continue shutdown as normal.
func (a *App) beforeClose(ctx context.Context) (prevent bool) {
defer PanicHandler()
dialog, err := runtime.MessageDialog(ctx, runtime.MessageDialogOptions{
Type: runtime.QuestionDialog,
@ -344,6 +351,7 @@ func (a *App) beforeClose(ctx context.Context) (prevent bool) {
// shutdown is called at application termination
func (a *App) shutdown(ctx context.Context) {
defer PanicHandler()
// Perform your teardown here
systray.Quit()
}

View File

@ -76,7 +76,19 @@ type AiResponse struct {
func (o OpenAi) NewChatStream(stock, stockCode string) <-chan string {
ch := make(chan string, 512)
defer func() {
if err := recover(); err != nil {
logger.SugaredLogger.Error("NewChatStream panic", err)
}
}()
go func() {
defer func() {
if err := recover(); err != nil {
logger.SugaredLogger.Error("NewChatStream goroutine panic", err)
}
}()
defer close(ch)
msg := []map[string]interface{}{
{
@ -174,7 +186,7 @@ func (o OpenAi) NewChatStream(stock, stockCode string) <-chan string {
client.SetBaseURL(o.BaseUrl)
client.SetHeader("Authorization", "Bearer "+o.ApiKey)
client.SetHeader("Content-Type", "application/json")
client.SetRetryCount(3)
//client.SetRetryCount(3)
if o.TimeOut <= 0 {
o.TimeOut = 300
}
@ -190,14 +202,15 @@ func (o OpenAi) NewChatStream(stock, stockCode string) <-chan string {
}).
Post("/chat/completions")
defer resp.RawBody().Close()
body := resp.RawBody()
defer body.Close()
if err != nil {
logger.SugaredLogger.Infof("Stream error : %s", err.Error())
ch <- err.Error()
return
}
scanner := bufio.NewScanner(resp.RawBody())
scanner := bufio.NewScanner(body)
for scanner.Scan() {
line := scanner.Text()
logger.SugaredLogger.Infof("Received data: %s", line)
@ -232,8 +245,13 @@ func (o OpenAi) NewChatStream(stock, stockCode string) <-chan string {
}
}
} else {
logger.SugaredLogger.Infof("Stream data error : %s", err.Error())
ch <- err.Error()
if err != nil {
logger.SugaredLogger.Infof("Stream data error : %s", err.Error())
ch <- err.Error()
} else {
logger.SugaredLogger.Infof("Stream data error : %s", data)
ch <- data
}
}
} else {
ch <- line

View File

@ -1,6 +1,7 @@
<script setup>
import stockInfo from './components/stock.vue'
import {
EventsEmit,
EventsOn,
Quit,
WindowFullscreen, WindowGetPosition,
@ -157,6 +158,18 @@ EventsOn("realtime_profit",(data)=>{
EventsOn("telegraph",(data)=>{
telegraph.value=data
})
window.onerror = function (message, source, lineno, colno, error) {
//
EventsEmit("frontendError", {
message: message,
source: source,
lineno: lineno,
colno: colno,
error: error ? error.stack : null
});
return true;
};
</script>
<template>

View File

@ -4,6 +4,7 @@ import {onMounted, ref, watch} from "vue";
import {ExportConfig, GetConfig, SendDingDingMessageByType, UpdateConfig} from "../../wailsjs/go/main/App";
import {useMessage} from "naive-ui";
import {data} from "../../wailsjs/go/models";
import {EventsEmit} from "../../wailsjs/runtime";
const message = useMessage()
const formRef = ref(null)
@ -150,6 +151,19 @@ function importConfig(){
};
input.click();
}
window.onerror = function (message, source, lineno, colno, error) {
//
EventsEmit("frontendError", {
message: message,
source: source,
lineno: lineno,
colno: colno,
error: error ? error.stack : null
});
return true;
};
</script>
<template>

View File

@ -22,7 +22,7 @@ import {
useModal,
useNotification
} from 'naive-ui'
import {EventsOn, WindowFullscreen, WindowReload, WindowUnfullscreen} from '../../wailsjs/runtime'
import {EventsEmit, EventsOn, WindowFullscreen, WindowReload, WindowUnfullscreen} from '../../wailsjs/runtime'
import {Add, Search,StarOutline} from '@vicons/ionicons5'
import { MdPreview } from 'md-editor-v3';
// preview.cssstyle.css
@ -546,7 +546,17 @@ function getHeight() {
return document.documentElement.clientHeight
}
window.onerror = function (message, source, lineno, colno, error) {
//
EventsEmit("frontendError", {
message: message,
source: source,
lineno: lineno,
colno: colno,
error: error ? error.stack : null
});
return true;
};
</script>
<template>

10
main.go
View File

@ -3,6 +3,7 @@ package main
import (
"embed"
"encoding/json"
"fmt"
"github.com/duke-git/lancet/v2/convertor"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/logger"
@ -18,6 +19,7 @@ import (
"log"
"os"
goruntime "runtime"
"runtime/debug"
"time"
)
@ -206,3 +208,11 @@ func checkDir(dir string) {
logger.NewDefaultLogger().Info("create dir: " + dir)
}
}
// PanicHandler 捕获 panic 的包装函数
func PanicHandler() {
if r := recover(); r != nil {
fmt.Printf("Recovered from panic: %v\n", r)
debug.PrintStack()
}
}