Compare commits
6 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
342d39a1a0 | |
|
|
8add85cea7 | |
|
|
95623f3802 | |
|
|
45262d8f88 | |
|
|
f958579981 | |
|
|
737fd041a0 |
|
|
@ -1 +0,0 @@
|
||||||
package log
|
|
||||||
114
log/log.go
114
log/log.go
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -16,8 +17,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logPath string
|
logPath string
|
||||||
sysLog *log.Logger
|
sysLog *log.Logger
|
||||||
|
filePath string
|
||||||
|
currentDate string // 当前日志文件对应的日期
|
||||||
|
fileLogger *lumberjack.Logger
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -59,10 +63,103 @@ func (w *logWriter) Write(p []byte) (n int, err error) {
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
// cleanOldLogs 删除指定天数之前的日志文件(包括主文件和备份文件)
|
||||||
|
func cleanOldLogs(days int) {
|
||||||
|
if !gfile.Exists(logPath) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有日志文件
|
||||||
|
files, err := gfile.DirNames(logPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
for _, file := range files {
|
||||||
|
path := filepath.Join(logPath, file)
|
||||||
|
if gfile.IsDir(path) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var dateStr string
|
||||||
|
var matched bool
|
||||||
|
|
||||||
|
// 匹配主日志文件格式:log-YYYY-MM-DD.log
|
||||||
|
if strings.HasPrefix(file, "log-") && strings.HasSuffix(file, ".log") {
|
||||||
|
// 检查是否是主文件(没有备份时间戳)
|
||||||
|
// 主文件格式:log-2026-04-25.log
|
||||||
|
// 备份文件格式:log-2026-04-25-2026-04-25T10-30-45.123.log
|
||||||
|
parts := strings.Split(strings.TrimSuffix(file, ".log"), "-")
|
||||||
|
if len(parts) == 4 {
|
||||||
|
// 主文件:log-YYYY-MM-DD
|
||||||
|
dateStr = parts[1] + "-" + parts[2] + "-" + parts[3]
|
||||||
|
matched = true
|
||||||
|
} else if len(parts) > 4 {
|
||||||
|
// 备份文件:log-YYYY-MM-DD-YYYY-MM-DDTHH-MM-SS.mmm
|
||||||
|
// 提取主日期部分(第一个日期)
|
||||||
|
dateStr = parts[1] + "-" + parts[2] + "-" + parts[3]
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析日期
|
||||||
|
fileTime, err := time.Parse("2006-01-02", dateStr)
|
||||||
|
if err != nil {
|
||||||
|
continue // 日期格式不正确,跳过
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算文件年龄
|
||||||
|
tage := now.Sub(fileTime)
|
||||||
|
if tage.Hours() > float64(days*24) {
|
||||||
|
// 超过指定天数,删除文件
|
||||||
|
err = os.Remove(path)
|
||||||
|
if err == nil {
|
||||||
|
Info(fmt.Sprintf("已删除过期日志文件:%s", file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkAndRotateLogFile 检查是否需要切换日志文件(跨天时)
|
||||||
|
func checkAndRotateLogFile() {
|
||||||
|
date := gtime.Date()
|
||||||
|
if currentDate != date {
|
||||||
|
// 日期变化,需要重新初始化
|
||||||
|
currentDate = date
|
||||||
|
filePath = gfile.Join(logPath, fmt.Sprintf("log-%s.log", currentDate))
|
||||||
|
fileLogger = &lumberjack.Logger{
|
||||||
|
Filename: filePath,
|
||||||
|
MaxSize: 2, // 单个文件最大 10MB
|
||||||
|
MaxBackups: 5, // 最多保留 5 个备份
|
||||||
|
MaxAge: 30, // 保留 30 天
|
||||||
|
Compress: false, // 启用压缩
|
||||||
|
}
|
||||||
|
// 创建新的 writer
|
||||||
|
multiWriter := &logWriter{
|
||||||
|
console: os.Stdout,
|
||||||
|
file: fileLogger,
|
||||||
|
}
|
||||||
|
sysLog = log.New(multiWriter, "", 0)
|
||||||
|
|
||||||
|
// 清理 30 天前的旧日志
|
||||||
|
cleanOldLogs(30)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
if sysLog != nil {
|
||||||
|
checkAndRotateLogFile() // 检查是否需要切换文件
|
||||||
|
return
|
||||||
|
}
|
||||||
logPath = gfile.Join(gfile.Pwd(), "logs")
|
logPath = gfile.Join(gfile.Pwd(), "logs")
|
||||||
filePath := gfile.Join(logPath, fmt.Sprintf("log-%s.log", gtime.Date()))
|
currentDate = gtime.Date()
|
||||||
fileLogger := &lumberjack.Logger{
|
filePath = gfile.Join(logPath, fmt.Sprintf("log-%s.log", currentDate))
|
||||||
|
fileLogger = &lumberjack.Logger{
|
||||||
Filename: filePath,
|
Filename: filePath,
|
||||||
MaxSize: 2, // 单个文件最大 10MB
|
MaxSize: 2, // 单个文件最大 10MB
|
||||||
MaxBackups: 5, // 最多保留 5 个备份
|
MaxBackups: 5, // 最多保留 5 个备份
|
||||||
|
|
@ -75,22 +172,29 @@ func init() {
|
||||||
file: fileLogger,
|
file: fileLogger,
|
||||||
}
|
}
|
||||||
sysLog = log.New(multiWriter, "", 0)
|
sysLog = log.New(multiWriter, "", 0)
|
||||||
|
|
||||||
|
// 启动时清理 30 天前的旧日志
|
||||||
|
cleanOldLogs(30)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Info(v ...any) {
|
func Info(v ...any) {
|
||||||
|
Init()
|
||||||
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[INFO]%s ", time.Now().Format("2006-01-02 15:04:05"), Green, Reset))
|
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[INFO]%s ", time.Now().Format("2006-01-02 15:04:05"), Green, Reset))
|
||||||
sysLog.Println(fmt.Sprint(v...))
|
sysLog.Println(fmt.Sprint(v...))
|
||||||
}
|
}
|
||||||
func Error(v ...any) {
|
func Error(v ...any) {
|
||||||
|
Init()
|
||||||
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[ERROR]%s ", time.Now().Format("2006-01-02 15:04:05"), Red, Reset))
|
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[ERROR]%s ", time.Now().Format("2006-01-02 15:04:05"), Red, Reset))
|
||||||
msg := fmt.Sprint(v...)
|
msg := fmt.Sprint(v...)
|
||||||
sysLog.Println(msg, strings.TrimSpace(string(debug.Stack())))
|
sysLog.Println(msg, strings.TrimSpace(string(debug.Stack())))
|
||||||
}
|
}
|
||||||
func Warn(v ...any) {
|
func Warn(v ...any) {
|
||||||
|
Init()
|
||||||
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[WARN]%s ", time.Now().Format("2006-01-02 15:04:05"), Yellow, Reset))
|
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[WARN]%s ", time.Now().Format("2006-01-02 15:04:05"), Yellow, Reset))
|
||||||
sysLog.Println(fmt.Sprint(v...))
|
sysLog.Println(fmt.Sprint(v...))
|
||||||
}
|
}
|
||||||
func Debug(v ...any) {
|
func Debug(v ...any) {
|
||||||
|
Init()
|
||||||
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[DEBUG]%s ", time.Now().Format("2006-01-02 15:04:05"), Blue, Reset))
|
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[DEBUG]%s ", time.Now().Format("2006-01-02 15:04:05"), Blue, Reset))
|
||||||
sysLog.Println(fmt.Sprint(v...))
|
sysLog.Println(fmt.Sprint(v...))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,75 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.magicany.cc/black1552/gf-common/log"
|
"git.magicany.cc/black1552/gf-common/log"
|
||||||
|
"github.com/gogf/gf/v2/net/gclient"
|
||||||
"github.com/gogf/gf/v2/net/ghttp"
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
"github.com/gogf/gf/v2/os/gctx"
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/text/gstr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// hasProtocol 检查字符串是否包含协议前缀
|
||||||
|
func hasProtocol(s string) bool {
|
||||||
|
return strings.HasPrefix(s, "http://") ||
|
||||||
|
strings.HasPrefix(s, "https://") ||
|
||||||
|
strings.HasPrefix(s, "ws://") ||
|
||||||
|
strings.HasPrefix(s, "wss://")
|
||||||
|
}
|
||||||
|
|
||||||
// BuildRequest 反向代理请求到指定主机
|
// BuildRequest 反向代理请求到指定主机
|
||||||
// 自动支持所有 HTTP 方法及 WebSocket 连接
|
// 自动支持所有 HTTP 方法及 WebSocket 连接
|
||||||
func BuildRequest(r *ghttp.Request, host string) {
|
func BuildRequest(r *ghttp.Request, host string) {
|
||||||
targetURL, err := url.Parse(host)
|
if gstr.Contains(r.RequestURI, "/ws") {
|
||||||
|
proxyWebSocket(r, host)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := gclient.New()
|
||||||
|
// 构建目标URL而不是直接复制RequestURI
|
||||||
|
targetURL := host + r.URL.Path
|
||||||
|
if r.URL.RawQuery != "" {
|
||||||
|
targetURL += "?" + r.URL.RawQuery
|
||||||
|
}
|
||||||
|
// 复制请求头
|
||||||
|
for key, values := range r.Header {
|
||||||
|
for _, value := range values {
|
||||||
|
client.SetHeader(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response, err := client.DoRequest(gctx.New(), r.Method, targetURL, r.GetBody())
|
||||||
|
if err != nil {
|
||||||
|
log.Error(gctx.New(), "request error:", err)
|
||||||
|
panic(fmt.Sprintf("request error: %v", err))
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
// 读取响应体
|
||||||
|
respBody, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(gctx.New(), "read response body error:", err)
|
||||||
|
panic(fmt.Sprintf("read response body error: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制响应头
|
||||||
|
for key, values := range response.Header {
|
||||||
|
for _, value := range values {
|
||||||
|
r.Response.Header().Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置响应状态码并写入响应体
|
||||||
|
r.Response.Status = response.StatusCode
|
||||||
|
r.Response.Write(respBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxyWebSocket 处理 WebSocket 连接的代理
|
||||||
|
func proxyWebSocket(r *ghttp.Request, targetHost string) {
|
||||||
|
// 解析目标主机 URL
|
||||||
|
targetURL, err := url.Parse(targetHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(gctx.New(), "parse target host error:", err)
|
log.Error(gctx.New(), "parse target host error:", err)
|
||||||
r.Response.WriteStatus(http.StatusInternalServerError)
|
r.Response.WriteStatus(http.StatusInternalServerError)
|
||||||
|
|
@ -21,25 +77,13 @@ func BuildRequest(r *ghttp.Request, host string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建反向代理
|
||||||
proxy := httputil.NewSingleHostReverseProxy(targetURL)
|
proxy := httputil.NewSingleHostReverseProxy(targetURL)
|
||||||
|
|
||||||
// 自定义 Director 来设置目标 URL,保留原始请求的所有参数
|
// 修改请求 URL,保留原始路径和查询参数
|
||||||
proxy.Director = func(req *http.Request) {
|
r.URL.Scheme = targetURL.Scheme
|
||||||
req.URL.Scheme = targetURL.Scheme
|
r.URL.Host = targetURL.Host
|
||||||
req.URL.Host = targetURL.Host
|
log.Info(gctx.New(), r.GetBodyString())
|
||||||
req.URL.Path = r.URL.Path
|
// 处理 WebSocket 连接
|
||||||
req.URL.RawQuery = r.URL.RawQuery
|
|
||||||
// 保留原始 Host 头
|
|
||||||
req.Host = r.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
// 错误处理
|
|
||||||
proxy.ErrorHandler = func(w http.ResponseWriter, req *http.Request, err error) {
|
|
||||||
log.Error(gctx.New(), "proxy error:", err)
|
|
||||||
w.WriteHeader(http.StatusBadGateway)
|
|
||||||
w.Write([]byte("Bad Gateway"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP 会自动处理 WebSocket 升级和所有 HTTP 方法
|
|
||||||
proxy.ServeHTTP(r.Response.Writer, r.Request)
|
proxy.ServeHTTP(r.Response.Writer, r.Request)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
utils/sql.go
21
utils/sql.go
|
|
@ -3,6 +3,7 @@ package utils
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"git.magicany.cc/black1552/gf-common/log"
|
||||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||||
"github.com/gogf/gf/v2/crypto/gmd5"
|
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||||
"github.com/gogf/gf/v2/database/gdb"
|
"github.com/gogf/gf/v2/database/gdb"
|
||||||
|
|
@ -51,51 +52,51 @@ func NewClient[R any](request any, url string, header map[string]string) *SClien
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (w *SClient[R]) Post(ctx context.Context) (res *R, err error) {
|
func (w *SClient[R]) Post(ctx context.Context) (res *R, err error) {
|
||||||
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "post", w.request)
|
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "post", w.request)
|
||||||
resp := w.client.PostVar(ctx, w.url, w.request)
|
resp := w.client.PostVar(ctx, w.url, w.request)
|
||||||
err = gconv.Struct(resp, &res)
|
err = gconv.Struct(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
|
log.Error("请求异常:", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (w *SClient[R]) Get(ctx context.Context) (res *R, err error) {
|
func (w *SClient[R]) Get(ctx context.Context) (res *R, err error) {
|
||||||
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "get", w.request)
|
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "get", w.request)
|
||||||
resp := w.client.GetVar(ctx, w.url, w.request)
|
resp := w.client.GetVar(ctx, w.url, w.request)
|
||||||
err = gconv.Struct(resp, &res)
|
err = gconv.Struct(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
|
log.Error("解析响应体异常:", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (w *SClient[R]) Put(ctx context.Context) (res *R, err error) {
|
func (w *SClient[R]) Put(ctx context.Context) (res *R, err error) {
|
||||||
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "put", w.request)
|
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "put", w.request)
|
||||||
resp := w.client.PutVar(ctx, w.url, w.request)
|
resp := w.client.PutVar(ctx, w.url, w.request)
|
||||||
err = gconv.Struct(resp, &res)
|
err = gconv.Struct(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
|
log.Error("解析响应体异常:", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (w *SClient[R]) Delete(ctx context.Context) (res *R, err error) {
|
func (w *SClient[R]) Delete(ctx context.Context) (res *R, err error) {
|
||||||
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "delete", w.request)
|
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "delete", w.request)
|
||||||
resp := w.client.DeleteVar(ctx, w.url, w.request)
|
resp := w.client.DeleteVar(ctx, w.url, w.request)
|
||||||
err = gconv.Struct(resp, &res)
|
err = gconv.Struct(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
|
log.Error("解析响应体异常:", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (w *SClient[R]) Patch(ctx context.Context) (res *R, err error) {
|
func (w *SClient[R]) Patch(ctx context.Context) (res *R, err error) {
|
||||||
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "patch", w.request)
|
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "patch", w.request)
|
||||||
resp := w.client.PatchVar(ctx, w.url, w.request)
|
resp := w.client.PatchVar(ctx, w.url, w.request)
|
||||||
err = gconv.Struct(resp, &res)
|
err = gconv.Struct(resp, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
|
log.Error("解析响应体异常:", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue