diff --git a/common/zlog/zlog.go b/common/zlog/zlog.go index 1ac36e6..7fb7993 100644 --- a/common/zlog/zlog.go +++ b/common/zlog/zlog.go @@ -19,13 +19,19 @@ import ( // zlog.Warn("hello", zap.String("name", "Kevin"), zap.Any("arbitraryObj", dummyObject)) var logger *zap.Logger -func InitZLog(releaseFlag string) { +// InitZLog 初始化zlog +func InitZLog(releaseFlag string, outputFormat string) { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder - encoder := zapcore.NewJSONEncoder(encoderConfig) - - //file, _ := os.OpenFile("/tmp/test.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 644) - //fileWriteSyncer = zapcore.AddSync(file) + var encoder zapcore.Encoder + if outputFormat == "json" { + encoder = zapcore.NewJSONEncoder(encoderConfig) + } else { // 默认使用控制台格式 + consoleConfig := zap.NewDevelopmentEncoderConfig() + consoleConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + consoleConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05.000") + encoder = zapcore.NewConsoleEncoder(consoleConfig) + } fileWriteSyncer := getFileLogWriter() if releaseFlag == "false" { @@ -51,8 +57,8 @@ func getFileLogWriter() (writeSyncer zapcore.WriteSyncer) { exeDir := "" // 检测环境变量是否存在 envVar := "SamWafIDE" - if value, exists := os.LookupEnv(envVar); exists { - fmt.Println("当前在IDE,环境变量" + value) + if _, exists := os.LookupEnv(envVar); exists { + //fmt.Println("当前在IDE,环境变量" + value) exeDir = "." } else { exePath, err := os.Executable() diff --git a/conf/config.yml b/conf/config.yml index 08d28b8..8b03087 100644 --- a/conf/config.yml +++ b/conf/config.yml @@ -5,3 +5,5 @@ notice: isenable: false soft_id: SamWafCom user_code: 8ad2bca0-4fd0-46aa-afe6-833132a05eee +zlog: + outputformat: console diff --git a/global/global.go b/global/global.go index e96c42b..c778b79 100644 --- a/global/global.go +++ b/global/global.go @@ -52,26 +52,29 @@ var ( */ GWAF_MEASURE_PROCESS_DEQUEENGINE cache.WafOnlyLockWriteData //遥测数据-队列处理时间 - GWAF_GLOBAL_HOST_NAME string = "全局网站:0" //全局网站 - GWAF_GLOBAL_HOST_CODE string = "0" //全局网站代码 - GWAF_LOCAL_DB *gorm.DB //通用本地数据库,尊重用户隐私 - GWAF_LOCAL_LOG_DB *gorm.DB //通用本地数据库存日志数据,尊重用户隐私 - GWAF_LOCAL_STATS_DB *gorm.DB //通用本地数据库存放统计数据,尊重用户隐私 - GWAF_REMOTE_DB *gorm.DB //仅当用户使用云数据库 - GWAF_LOCAL_SERVER_PORT int = 26666 // 本地local端口 - GWAF_LOCAL_INDEX_HTML string //本地首页HTML信息 - GWAF_USER_CODE string // 当前识别号 - GWAF_CUSTOM_SERVER_NAME string // 当前服务器自定义名称 - GWAF_TENANT_ID string = "SamWafCom" // 当前租户ID - GWAF_RELEASE string = "false" // 当前是否为发行版 - GWAF_RELEASE_VERSION_NAME string = "20241028" // 发行版的版本号名称 - GWAF_RELEASE_VERSION string = "v1.0.0" // 发行版的版本号 - GWAF_LAST_UPDATE_TIME time.Time // 上次时间 - GWAF_LAST_TIME_UNIX int64 = 0 // 上次时间戳 - GWAF_NOTICE_ENABLE bool = false // 是否开启通知 - GWAF_CAN_EXPORT_DOWNLOAD_LOG bool = false //是否可以导出下载日志 - GWAF_DLP dlpheader.EngineAPI // 脱敏引擎 - GWAF_DLP_CONFIG string // 脱敏引擎配置数据 + GWAF_GLOBAL_HOST_NAME string = "全局网站:0" //全局网站 + GWAF_GLOBAL_HOST_CODE string = "0" //全局网站代码 + GWAF_LOCAL_DB *gorm.DB //通用本地数据库,尊重用户隐私 + GWAF_LOCAL_LOG_DB *gorm.DB //通用本地数据库存日志数据,尊重用户隐私 + GWAF_LOCAL_STATS_DB *gorm.DB //通用本地数据库存放统计数据,尊重用户隐私 + GWAF_REMOTE_DB *gorm.DB //仅当用户使用云数据库 + GWAF_LOCAL_SERVER_PORT int = 26666 // 本地local端口 + GWAF_LOCAL_INDEX_HTML string //本地首页HTML信息 + GWAF_USER_CODE string // 当前识别号 + GWAF_CUSTOM_SERVER_NAME string // 当前服务器自定义名称 + GWAF_TENANT_ID string = "SamWafCom" // 当前租户ID + + //zlog 日志相关信息 + GWAF_LOG_OUTPUT_FORMAT string = "console" //zlog输出格式 控制台格式console,json格式 + GWAF_RELEASE string = "false" // 当前是否为发行版 + GWAF_RELEASE_VERSION_NAME string = "20241028" // 发行版的版本号名称 + GWAF_RELEASE_VERSION string = "v1.0.0" // 发行版的版本号 + GWAF_LAST_UPDATE_TIME time.Time // 上次时间 + GWAF_LAST_TIME_UNIX int64 = 0 // 上次时间戳 + GWAF_NOTICE_ENABLE bool = false // 是否开启通知 + GWAF_CAN_EXPORT_DOWNLOAD_LOG bool = false //是否可以导出下载日志 + GWAF_DLP dlpheader.EngineAPI // 脱敏引擎 + GWAF_DLP_CONFIG string // 脱敏引擎配置数据 GWAF_OWASP *wafowasp.WafOWASP //owasp引擎 /**链聚合**/ diff --git a/main.go b/main.go index 4da0af0..591e6f7 100644 --- a/main.go +++ b/main.go @@ -107,8 +107,6 @@ func NeverExit(name string, f func()) { // run 是服务的主要逻辑 func (m *wafSystenService) run() { - //加载配置 - wafconfig.LoadAndInitConfig() // 获取当前执行文件的路径 executablePath, err := os.Executable() if err != nil { @@ -167,29 +165,6 @@ func (m *wafSystenService) run() { //TODO 准备释放最新spider bot - /*// 启动一个 goroutine 来处理信号 - go func() { - // 创建一个通道来接收信号 - signalCh := make(chan os.Signal, 1) - - // 根据操作系统设置不同的信号监听 - if runtime.GOOS == "windows" { - signal.Notify(signalCh, syscall.SIGINT,os.Interrupt, syscall.SIGTERM,os.Kill) - } else { // Linux 系统 - signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM) - } - for { - select { - case sig := <-signalCh: - fmt.Println("接收到信号:", sig) - wafsafeclear.SafeClear() - // 在这里执行你的清理操作或退出应用程序的代码 - os.Exit(0) - } - } - }()*/ - - // 在这里编写你的服务逻辑代码 //初始化cache global.GCACHE_WAFCACHE = cache.InitWafCache() //初始化锁写不锁度 @@ -336,13 +311,6 @@ func (m *wafSystenService) run() { }) } - - /*withEncrypt, err :=wafreg.GenClientMachineInfoWithEncrypt() - if err != nil { - fmt.Println("获取机器码失败") - } else { - fmt.Println("机器码: ", withEncrypt) - }*/ //加载授权信息 verifyResult, info, err := wafreg.VerifyServerRegByDefaultFile() if verifyResult { @@ -621,10 +589,18 @@ func (m *wafSystenService) Graceful() { } func main() { + fmt.Println(` +========================================== + SamWaf Web Application Firewall ` + global.GWAF_RELEASE_VERSION + ` + Version Name: ` + global.GWAF_RELEASE_VERSION_NAME + ` +========================================== +`) + //加载配置 + wafconfig.LoadAndInitConfig() //初始化日志 - zlog.InitZLog(global.GWAF_RELEASE) - if v := recover(); v != nil { // 侦测到一个恐慌 - zlog.Info("主流程上被异常了") + zlog.InitZLog(global.GWAF_RELEASE, global.GWAF_LOG_OUTPUT_FORMAT) + if v := recover(); v != nil { + zlog.Error("主流程上被异常了") } pid := os.Getpid() zlog.Debug("SamWaf Current PID:" + strconv.Itoa(pid)) @@ -659,11 +635,6 @@ func main() { sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM) - /*_, stop := signal.NotifyContext(context.Background(), os.Interrupt) - defer stop()*/ - - //doWork(ctx) - if len(os.Args) > 1 { command := os.Args[1] switch command { @@ -676,13 +647,9 @@ func main() { fmt.Printf("Samwaf has successfully executed the '%s' command.\n", command) break case "resetpwd": //重制密码 - //加载配置 - wafconfig.LoadAndInitConfig() wafdb.InitCoreDb("") wafdb.ResetAdminPwd() case "resetotp": //重置安全码 - //加载配置 - wafconfig.LoadAndInitConfig() wafdb.InitCoreDb("") wafdb.ResetAdminOTP() default: @@ -698,7 +665,6 @@ func main() { zlog.Info("main server not under service manager") global.GWAF_RUNTIME_SERVER_TYPE = service.Interactive() } - //defer wafsafeclear.SafeClear() // 以常规方式运行 err = s.Run() if err != nil { diff --git a/utils/common.go b/utils/common.go index afdf6e7..7013549 100644 --- a/utils/common.go +++ b/utils/common.go @@ -35,17 +35,15 @@ func GetExternalIp() string { func GetCurrentDir() string { // 检测环境变量是否存在 envVar := "SamWafIDE" - if value, exists := os.LookupEnv(envVar); exists { - zlog.Info("当前在IDE,环境变量", value) + if _, exists := os.LookupEnv(envVar); exists { return "." } exePath, err := os.Executable() if err != nil { - zlog.Error("Failed to get executable path:", err) + fmt.Println(fmt.Sprintf("Failed to get executable path:%v", err)) return "" } - zlog.Debug("当前程序所在文件位置", exePath) exeDir := filepath.Dir(exePath) return exeDir } diff --git a/wafconfig/wafconfig.go b/wafconfig/wafconfig.go index 9cf96da..05703f7 100644 --- a/wafconfig/wafconfig.go +++ b/wafconfig/wafconfig.go @@ -1,18 +1,21 @@ package wafconfig import ( - "SamWaf/common/zlog" "SamWaf/global" "SamWaf/utils" + "fmt" "github.com/denisbrodbeck/machineid" uuid "github.com/satori/go.uuid" "github.com/spf13/viper" "os" + "time" ) // 加载配置并初始化 func LoadAndInitConfig() { - zlog.Info("load config") + // 格式化当前时间为指定格式 + currentTime := time.Now().Format("2006-01-02 15:04:05.000") + fmt.Printf("%s\tINFO\tload config\n", currentTime) /** 1.如果user_code存在就使用本地的user_code 2. @@ -21,7 +24,7 @@ func LoadAndInitConfig() { configDir := utils.GetCurrentDir() + "/conf/" if _, err := os.Stat(configDir); os.IsNotExist(err) { if err := os.MkdirAll(configDir, os.ModePerm); err != nil { - zlog.Error("创建config目录失败:", err) + fmt.Printf("%s\tERROR\t创建config目录失败:%v\n", currentTime, err) return } } @@ -32,11 +35,11 @@ func LoadAndInitConfig() { if err := config.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { - zlog.Error("找不到配置文件..") + fmt.Printf("%s\tWARN\t找不到配置文件..\n", currentTime) config.Set("local_port", global.GWAF_LOCAL_SERVER_PORT) err = config.SafeWriteConfig() } else { - zlog.Error("配置文件出错..") + fmt.Printf("%s\tERROR\t配置文件出错..\n", currentTime) } } if config.IsSet("user_code") == false { @@ -84,10 +87,18 @@ func LoadAndInitConfig() { global.GWAF_CAN_EXPORT_DOWNLOAD_LOG = config.GetBool("export_download") } + if config.IsSet("zlog.outputformat") { + global.GWAF_LOG_OUTPUT_FORMAT = config.GetString("zlog.outputformat") + } else { + config.Set("zlog.outputformat", global.GWAF_LOG_OUTPUT_FORMAT) + } + err := config.WriteConfig() if err != nil { - zlog.Error("write config failed: ", err) + fmt.Printf("%s\tERROR\twrite config failed:%v\n", currentTime, err) + return } - zlog.Info("user_code:", global.GWAF_USER_CODE) - zlog.Info("sof_id:", global.GWAF_TENANT_ID) + + fmt.Printf("%s\tINFO\tuser_code:%s ,soft_id:%s\n", + currentTime, global.GWAF_USER_CODE, global.GWAF_TENANT_ID) } diff --git a/wafdb/gormlogger.go b/wafdb/gormlogger.go new file mode 100644 index 0000000..2fa8e98 --- /dev/null +++ b/wafdb/gormlogger.go @@ -0,0 +1,86 @@ +package wafdb + +import ( + "SamWaf/common/zlog" + "context" + "fmt" + "time" + + "gorm.io/gorm/logger" +) + +// GormZLogger 是一个实现了 gorm.Logger 接口的自定义日志记录器 +type GormZLogger struct { + SlowThreshold time.Duration + IgnoreRecordNotFound bool + LogLevel logger.LogLevel + ParameterizedQueries bool +} + +// NewGormZLogger 创建一个新的 GormZLogger 实例 +func NewGormZLogger() *GormZLogger { + return &GormZLogger{ + SlowThreshold: time.Second, // 慢查询阈值 + LogLevel: logger.Info, // 默认日志级别 + IgnoreRecordNotFound: true, // 忽略记录未找到错误 + } +} + +// LogMode 设置日志级别 +func (l *GormZLogger) LogMode(level logger.LogLevel) logger.Interface { + newLogger := *l + newLogger.LogLevel = level + return &newLogger +} + +// Info 打印信息日志 +func (l *GormZLogger) Info(ctx context.Context, msg string, data ...interface{}) { + if l.LogLevel >= logger.Info { + zlog.Info(fmt.Sprintf(msg, data...)) + } +} + +// Warn 打印警告日志 +func (l *GormZLogger) Warn(ctx context.Context, msg string, data ...interface{}) { + if l.LogLevel >= logger.Warn { + zlog.Warn(fmt.Sprintf(msg, data...)) + } +} + +// Error 打印错误日志 +func (l *GormZLogger) Error(ctx context.Context, msg string, data ...interface{}) { + if l.LogLevel >= logger.Error { + zlog.Error(fmt.Sprintf(msg, data...)) + } +} + +// Trace 记录 SQL 执行情况 +func (l *GormZLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { + if l.LogLevel <= logger.Silent { + return + } + + elapsed := time.Since(begin) + sql, rows := fc() + + // 构建日志消息,简化格式使其更加整洁 + logMsg := fmt.Sprintf("SQL执行 [%.3fms] [rows:%v] %s", float64(elapsed.Nanoseconds())/1e6, rows, sql) + + // 根据不同情况记录不同级别的日志 + switch { + case err != nil && l.LogLevel >= logger.Error && (!l.IgnoreRecordNotFound || !isRecordNotFoundError(err)): + zlog.Error(fmt.Sprintf("SQL错误 %v %s", err, logMsg)) + case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= logger.Warn: + zlog.Warn(fmt.Sprintf("慢SQL %s", logMsg)) + case l.LogLevel >= logger.Info: + zlog.Debug(logMsg) + } +} + +// isRecordNotFoundError 检查错误是否为记录未找到错误 +func isRecordNotFoundError(err error) bool { + if err == nil { + return false + } + return err.Error() == "record not found" +} diff --git a/wafdb/localdb.go b/wafdb/localdb.go index 28f0b25..42dd6eb 100644 --- a/wafdb/localdb.go +++ b/wafdb/localdb.go @@ -85,10 +85,13 @@ func InitCoreDb(currentDir string) { // 启用 WAL 模式 _ = db.Exec("PRAGMA journal_mode=WAL;") + // 创建自定义日志记录器 + gormLogger := NewGormZLogger() if global.GWAF_RELEASE == "false" && utils.CheckDebugEnvInfo() { + gormLogger = gormLogger.LogMode(logger.Info).(*GormZLogger) // 启用调试模式 db = db.Session(&gorm.Session{ - Logger: logger.Default.LogMode(logger.Info), // 设置为Info表示启用调试模式 + Logger: gormLogger, }) } global.GWAF_LOCAL_DB = db @@ -188,7 +191,10 @@ func InitLogDb(currentDir string) { } // 启用 WAL 模式 _ = db.Exec("PRAGMA journal_mode=WAL;") + // 创建自定义日志记录器 + gormLogger := NewGormZLogger() if global.GWAF_RELEASE == "false" && utils.CheckDebugEnvInfo() { + gormLogger = gormLogger.LogMode(logger.Info).(*GormZLogger) // 启用调试模式 db = db.Session(&gorm.Session{ Logger: logger.Default.LogMode(logger.Info), // 设置为Info表示启用调试模式 @@ -250,7 +256,10 @@ func InitManaulLogDb(currentDir string, custFileName string) { } // 启用 WAL 模式 _ = db.Exec("PRAGMA journal_mode=WAL;") - if global.GWAF_RELEASE == "false" { + // 创建自定义日志记录器 + gormLogger := NewGormZLogger() + if global.GWAF_RELEASE == "false" && utils.CheckDebugEnvInfo() { + gormLogger = gormLogger.LogMode(logger.Info).(*GormZLogger) // 启用调试模式 db = db.Session(&gorm.Session{ Logger: logger.Default.LogMode(logger.Info), // 设置为Info表示启用调试模式 @@ -289,7 +298,10 @@ func InitStatsDb(currentDir string) { } // 启用 WAL 模式 _ = db.Exec("PRAGMA journal_mode=WAL;") + // 创建自定义日志记录器 + gormLogger := NewGormZLogger() if global.GWAF_RELEASE == "false" && utils.CheckDebugEnvInfo() { + gormLogger = gormLogger.LogMode(logger.Info).(*GormZLogger) // 启用调试模式 db = db.Session(&gorm.Session{ Logger: logger.Default.LogMode(logger.Info), // 设置为Info表示启用调试模式 diff --git a/wafmangeweb/localserver.go b/wafmangeweb/localserver.go index 7da13a9..6883aa4 100644 --- a/wafmangeweb/localserver.go +++ b/wafmangeweb/localserver.go @@ -10,6 +10,7 @@ import ( "errors" "github.com/gin-contrib/pprof" "github.com/gin-gonic/gin" + "io" "log" "net/http" "strconv" @@ -119,10 +120,10 @@ func (web *WafWebManager) cors() gin.HandlerFunc { func (web *WafWebManager) StartLocalServer() { if global.GWAF_RELEASE == "true" { gin.SetMode(gin.ReleaseMode) + gin.DefaultWriter = io.Discard } r := gin.Default() r.Use(web.cors()) //解决跨域 - web.initRouter(r) web.R = r