From 2732b14eae9c7df8db88f51123dda36e0769806a Mon Sep 17 00:00:00 2001 From: zicla Date: Sat, 27 Apr 2019 23:24:09 +0800 Subject: [PATCH] Try to abstract the main part. --- code/config/config.go | 26 ++-- code/rest/alien_service.go | 2 +- code/rest/bean.go | 7 +- code/rest/context.go | 12 +- code/rest/dashboard_service.go | 2 +- code/rest/footprint_service.go | 2 +- code/rest/router.go | 8 +- .../logger.go => support/tank_logger.go} | 116 +++++++++--------- code/test/main_test.go | 3 + code/tool/cache/cache.go | 2 +- code/tool/inter/logger.go | 17 +++ code/tool/util/{util_path.go => util_file.go} | 11 +- code/tool/util/util_framework.go | 16 ++- main.go | 11 +- 14 files changed, 130 insertions(+), 105 deletions(-) rename code/{logger/logger.go => support/tank_logger.go} (52%) create mode 100644 code/tool/inter/logger.go rename code/tool/util/{util_path.go => util_file.go} (96%) diff --git a/code/config/config.go b/code/config/config.go index dc5225e..ee1760d 100644 --- a/code/config/config.go +++ b/code/config/config.go @@ -1,7 +1,7 @@ package config import ( - "github.com/eyebluecn/tank/code/logger" + "github.com/eyebluecn/tank/code/tool/inter" "github.com/eyebluecn/tank/code/tool/util" "github.com/json-iterator/go" "io/ioutil" @@ -65,7 +65,7 @@ type ConfigItem struct { func (this *ConfigItem) validate() bool { if this.ServerPort == 0 { - logger.LOGGER.Error("ServerPort 未配置") + inter.LOGGER.Error("ServerPort 未配置") return false } else { //只要配置文件中有配置端口,就使用。 @@ -73,27 +73,27 @@ func (this *ConfigItem) validate() bool { } if this.MysqlUsername == "" { - logger.LOGGER.Error("MysqlUsername 未配置") + inter.LOGGER.Error("MysqlUsername 未配置") return false } if this.MysqlPassword == "" { - logger.LOGGER.Error("MysqlPassword 未配置") + inter.LOGGER.Error("MysqlPassword 未配置") return false } if this.MysqlHost == "" { - logger.LOGGER.Error("MysqlHost 未配置") + inter.LOGGER.Error("MysqlHost 未配置") return false } if this.MysqlPort == 0 { - logger.LOGGER.Error("MysqlPort 未配置") + inter.LOGGER.Error("MysqlPort 未配置") return false } if this.MysqlSchema == "" { - logger.LOGGER.Error("MysqlSchema 未配置") + inter.LOGGER.Error("MysqlSchema 未配置") return false } @@ -135,14 +135,14 @@ func (this *Config) ReadFromConfigFile() { filePath := util.GetConfPath() + "/tank.json" content, err := ioutil.ReadFile(filePath) if err != nil { - logger.LOGGER.Warn("无法找到配置文件:%s 即将进入安装过程!", filePath) + inter.LOGGER.Warn("无法找到配置文件:%s 即将进入安装过程!", filePath) this.Installed = false } else { this.Item = &ConfigItem{} - logger.LOGGER.Warn("读取配置文件:%s", filePath) + inter.LOGGER.Warn("读取配置文件:%s", filePath) err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(content, this.Item) if err != nil { - logger.LOGGER.Error("配置文件格式错误! 即将进入安装过程!") + inter.LOGGER.Error("配置文件格式错误! 即将进入安装过程!") this.Installed = false return } @@ -150,7 +150,7 @@ func (this *Config) ReadFromConfigFile() { //验证项是否齐全 itemValidate := this.Item.validate() if !itemValidate { - logger.LOGGER.Error("配置文件信息不齐全! 即将进入安装过程!") + inter.LOGGER.Error("配置文件信息不齐全! 即将进入安装过程!") this.Installed = false return } @@ -171,8 +171,8 @@ func (this *Config) ReadFromConfigFile() { this.MysqlUrl = util.GetMysqlUrl(this.Item.MysqlPort, this.Item.MysqlHost, this.Item.MysqlSchema, this.Item.MysqlUsername, this.Item.MysqlPassword) this.Installed = true - logger.LOGGER.Info("使用配置文件:%s", filePath) - logger.LOGGER.Info("上传文件存放路径:%s", this.MatterPath) + inter.LOGGER.Info("使用配置文件:%s", filePath) + inter.LOGGER.Info("上传文件存放路径:%s", this.MatterPath) } } diff --git a/code/rest/alien_service.go b/code/rest/alien_service.go index 8ab0ccc..804776f 100644 --- a/code/rest/alien_service.go +++ b/code/rest/alien_service.go @@ -134,7 +134,7 @@ func (this *AlienService) PreviewOrDownload( } //文件下载次数加一,为了加快访问速度,异步进行 - go util.SafeMethod(func() { + go util.RunWithRecovery(func() { this.matterDao.TimesIncrement(uuid) }) diff --git a/code/rest/bean.go b/code/rest/bean.go index 4598e2a..6409125 100644 --- a/code/rest/bean.go +++ b/code/rest/bean.go @@ -2,7 +2,7 @@ package rest import ( "github.com/eyebluecn/tank/code/config" - "github.com/eyebluecn/tank/code/logger" + "github.com/eyebluecn/tank/code/tool/inter" "github.com/eyebluecn/tank/code/tool/result" "github.com/eyebluecn/tank/code/tool/util" "net/http" @@ -20,11 +20,11 @@ type IBean interface { } type Bean struct { - logger *logger.Logger + logger inter.Logger } func (this *Bean) Init() { - this.logger = logger.LOGGER + this.logger = inter.LOGGER } func (this *Bean) Bootstrap() { @@ -59,6 +59,7 @@ func (this *Bean) findUser(writer http.ResponseWriter, request *http.Request) *U } if cacheItem == nil || cacheItem.Data() == nil { + this.logger.Warn("cache item中已经不存在了 ") return nil } diff --git a/code/rest/context.go b/code/rest/context.go index f327e09..40b1ee0 100644 --- a/code/rest/context.go +++ b/code/rest/context.go @@ -3,8 +3,8 @@ package rest import ( "fmt" "github.com/eyebluecn/tank/code/config" - "github.com/eyebluecn/tank/code/logger" cache2 "github.com/eyebluecn/tank/code/tool/cache" + "github.com/eyebluecn/tank/code/tool/inter" "github.com/jinzhu/gorm" "reflect" ) @@ -56,7 +56,7 @@ func (this *Context) OpenDb() { this.DB, err = gorm.Open("mysql", config.CONFIG.MysqlUrl) if err != nil { - logger.LOGGER.Panic("failed to connect mysql database") + inter.LOGGER.Panic("failed to connect mysql database") } //是否打开sql日志(在调试阶段可以打开,以方便查看执行的SQL) @@ -68,7 +68,7 @@ func (this *Context) CloseDb() { if this.DB != nil { err := this.DB.Close() if err != nil { - fmt.Println("关闭数据库连接出错", err) + inter.LOGGER.Error("关闭数据库连接出错 %s", err.Error()) } } } @@ -83,7 +83,7 @@ func (this *Context) registerBean(bean IBean) { err := fmt.Sprintf("【%s】已经被注册了,跳过。", typeName) if _, ok := this.BeanMap[typeName]; ok { - logger.LOGGER.Error(fmt.Sprintf(err)) + inter.LOGGER.Error(fmt.Sprintf(err)) } else { this.BeanMap[typeName] = element @@ -95,7 +95,7 @@ func (this *Context) registerBean(bean IBean) { } } else { - logger.LOGGER.Panic("注册的【%s】不是Bean类型。", typeName) + inter.LOGGER.Panic("注册的【%s】不是Bean类型。", typeName) } } @@ -165,7 +165,7 @@ func (this *Context) GetBean(bean IBean) IBean { if val, ok := this.BeanMap[typeName]; ok { return val } else { - logger.LOGGER.Panic("【%s】没有注册。", typeName) + inter.LOGGER.Panic("【%s】没有注册。", typeName) return nil } } diff --git a/code/rest/dashboard_service.go b/code/rest/dashboard_service.go index 951d67b..321e87c 100644 --- a/code/rest/dashboard_service.go +++ b/code/rest/dashboard_service.go @@ -60,7 +60,7 @@ func (this *DashboardService) Bootstrap() { this.logger.Info("[cron job] 每日00:05清洗离线数据") //立即执行一次数据清洗任务 - go util.SafeMethod(this.etl) + go util.RunWithRecovery(this.etl) } diff --git a/code/rest/footprint_service.go b/code/rest/footprint_service.go index 524f49f..271490f 100644 --- a/code/rest/footprint_service.go +++ b/code/rest/footprint_service.go @@ -102,7 +102,7 @@ func (this *FootprintService) Bootstrap() { this.logger.Info("[cron job] 每日00:10 删除8日之前的访问数据") //立即执行一次数据清洗任务 - go util.SafeMethod(this.cleanOldData) + go util.RunWithRecovery(this.cleanOldData) } diff --git a/code/rest/router.go b/code/rest/router.go index fe7a0e3..6de999e 100644 --- a/code/rest/router.go +++ b/code/rest/router.go @@ -3,7 +3,7 @@ package rest import ( "fmt" "github.com/eyebluecn/tank/code/config" - "github.com/eyebluecn/tank/code/logger" + "github.com/eyebluecn/tank/code/tool/inter" "github.com/eyebluecn/tank/code/tool/result" "github.com/eyebluecn/tank/code/tool/util" "github.com/json-iterator/go" @@ -72,7 +72,7 @@ func NewRouter() *Router { func (this *Router) GlobalPanicHandler(writer http.ResponseWriter, request *http.Request, startTime time.Time) { if err := recover(); err != nil { - logger.LOGGER.Error("错误: %v", err) + inter.LOGGER.Error("错误: %v", err) var webResult *result.WebResult = nil if value, ok := err.(string); ok { @@ -108,7 +108,7 @@ func (this *Router) GlobalPanicHandler(writer http.ResponseWriter, request *http } //错误情况记录。 - go util.SafeMethod(func() { + go util.RunWithRecovery(func() { this.footprintService.Trace(writer, request, time.Now().Sub(startTime), false) }) } @@ -156,7 +156,7 @@ func (this *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) } //正常的访问记录会落到这里。 - go util.SafeMethod(func() { + go util.RunWithRecovery(func() { this.footprintService.Trace(writer, request, time.Now().Sub(startTime), true) }) diff --git a/code/logger/logger.go b/code/support/tank_logger.go similarity index 52% rename from code/logger/logger.go rename to code/support/tank_logger.go index e8d58dc..1ef0a1a 100644 --- a/code/logger/logger.go +++ b/code/support/tank_logger.go @@ -1,8 +1,9 @@ -package logger +package support import ( "fmt" "github.com/eyebluecn/tank/code/tool/util" + "github.com/robfig/cron" "log" "os" "runtime" @@ -10,12 +11,8 @@ import ( "time" ) -//日志系统必须高保 -//全局唯一的日志对象(在main函数中初始化) -var LOGGER = &Logger{} - //在Logger的基础上包装一个全新的Logger. -type Logger struct { +type TankLogger struct { //加锁,在维护日志期间,禁止写入日志。 sync.RWMutex @@ -23,12 +20,28 @@ type Logger struct { goLogger *log.Logger //日志记录所在的文件 file *os.File - //每天凌晨定时整理器 - maintainTimer *time.Timer +} + +func (this *TankLogger) Init() { + + this.openFile() + + //每日00:00整理日志。 + expression := "0 0 0 * * ?" + cronJob := cron.New() + err := cronJob.AddFunc(expression, this.maintain) + util.PanicError(err) + cronJob.Start() + this.Info("[cron job] 每日00:00维护日志") + +} + +func (this *TankLogger) Destroy() { + this.closeFile() } //处理日志的统一方法。 -func (this *Logger) log(prefix string, format string, v ...interface{}) { +func (this *TankLogger) Log(prefix string, format string, v ...interface{}) { content := fmt.Sprintf(format+"\r\n", v...) @@ -52,44 +65,29 @@ func (this *Logger) log(prefix string, format string, v ...interface{}) { } //处理日志的统一方法。 -func (this *Logger) Debug(format string, v ...interface{}) { - this.log("[DEBUG]", format, v...) +func (this *TankLogger) Debug(format string, v ...interface{}) { + this.Log("[DEBUG]", format, v...) } -func (this *Logger) Info(format string, v ...interface{}) { - this.log("[INFO ]", format, v...) +func (this *TankLogger) Info(format string, v ...interface{}) { + this.Log("[INFO ]", format, v...) } -func (this *Logger) Warn(format string, v ...interface{}) { - this.log("[WARN ]", format, v...) +func (this *TankLogger) Warn(format string, v ...interface{}) { + this.Log("[WARN ]", format, v...) } -func (this *Logger) Error(format string, v ...interface{}) { - this.log("[ERROR]", format, v...) +func (this *TankLogger) Error(format string, v ...interface{}) { + this.Log("[ERROR]", format, v...) } -func (this *Logger) Panic(format string, v ...interface{}) { - this.log("[PANIC]", format, v...) +func (this *TankLogger) Panic(format string, v ...interface{}) { + this.Log("[PANIC]", format, v...) panic(fmt.Sprintf(format, v...)) } -func (this *Logger) Init() { - - this.openFile() - - //日志需要自我备份,自我维护。明天第一秒触发 - nextTime := util.FirstSecondOfDay(util.Tomorrow()) - duration := nextTime.Sub(time.Now()) - - this.Info("下一次日志维护时间%s 距当前 %ds ", util.ConvertTimeToDateTimeString(nextTime), duration/time.Second) - this.maintainTimer = time.AfterFunc(duration, func() { - go util.SafeMethod(this.maintain) - }) - -} - //将日志写入到今天的日期中(该方法内必须使用异步方法记录日志,否则会引发死锁) -func (this *Logger) maintain() { +func (this *TankLogger) maintain() { this.Info("每日维护日志") @@ -100,7 +98,7 @@ func (this *Logger) maintain() { this.closeFile() //日志归类到昨天 - destPath := util.GetLogPath() + "/tank-" + util.Yesterday().Local().Format("2006-01-02") + ".log" + destPath := util.GetLogPath() + "/tank-" + util.ConvertTimeToDateString(util.Yesterday()) + ".log" //直接重命名文件 err := os.Rename(this.fileName(), destPath) @@ -111,23 +109,33 @@ func (this *Logger) maintain() { //再次打开文件 this.openFile() - //准备好下次维护日志的时间。 - now := time.Now() - nextTime := util.FirstSecondOfDay(util.Tomorrow()) - duration := nextTime.Sub(now) - this.Info("下次维护时间:%s ", util.ConvertTimeToDateTimeString(nextTime)) - this.maintainTimer = time.AfterFunc(duration, func() { - go util.SafeMethod(this.maintain) - }) + //删除一个月之前的日志文件。 + monthAgo := time.Now() + monthAgo = monthAgo.AddDate(0, -1, 0) + oldDestPath := util.GetLogPath() + "/tank-" + util.ConvertTimeToDateString(monthAgo) + ".log" + this.Log("删除日志文件 %s", oldDestPath) + + //删除文件 + exists, err := util.PathExists(oldDestPath) + util.PanicError(err) + if exists { + err = os.Remove(oldDestPath) + if err != nil { + this.Error("删除磁盘上的文件%s 出错 %s", oldDestPath, err.Error()) + } + } else { + this.Error("日志文件 %s 不存在,无需删除", oldDestPath) + } + } //日志名称 -func (this *Logger) fileName() string { +func (this *TankLogger) fileName() string { return util.GetLogPath() + "/tank.log" } //打开日志文件 -func (this *Logger) openFile() { +func (this *TankLogger) openFile() { //日志输出到文件中 文件打开后暂时不关闭 fmt.Printf("使用日志文件 %s\r\n", this.fileName()) f, err := os.OpenFile(this.fileName(), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) @@ -137,11 +145,15 @@ func (this *Logger) openFile() { this.goLogger = log.New(f, "", log.Ltime|log.Lshortfile) + if this.goLogger == nil { + fmt.Printf("Error: cannot create goLogger \r\n") + } + this.file = f } //关闭日志文件 -func (this *Logger) closeFile() { +func (this *TankLogger) closeFile() { if this.file != nil { err := this.file.Close() if err != nil { @@ -149,13 +161,3 @@ func (this *Logger) closeFile() { } } } - -func (this *Logger) Destroy() { - - this.closeFile() - - if this.maintainTimer != nil { - this.maintainTimer.Stop() - } - -} diff --git a/code/test/main_test.go b/code/test/main_test.go index 677871c..d00eec6 100644 --- a/code/test/main_test.go +++ b/code/test/main_test.go @@ -26,6 +26,9 @@ func TestCron(t *testing.T) { err := c.AddFunc(spec, func() { i++ log.Println("cron running:", i) + if i == 2 { + panic("intent to panic.") + } }) util.PanicError(err) diff --git a/code/tool/cache/cache.go b/code/tool/cache/cache.go index e58c4ef..6809796 100644 --- a/code/tool/cache/cache.go +++ b/code/tool/cache/cache.go @@ -197,7 +197,7 @@ func (table *Table) checkExpire() { table.cleanupInterval = smallestDuration if smallestDuration > 0 { table.cleanupTimer = time.AfterFunc(smallestDuration, func() { - go util.SafeMethod(table.checkExpire) + go util.RunWithRecovery(table.checkExpire) }) } table.Unlock() diff --git a/code/tool/inter/logger.go b/code/tool/inter/logger.go new file mode 100644 index 0000000..0a4b1d3 --- /dev/null +++ b/code/tool/inter/logger.go @@ -0,0 +1,17 @@ +package inter + +//日志系统必须高保 +//全局唯一的日志对象(在main函数中初始化) +var LOGGER Logger + +type Logger interface { + //处理日志的统一方法。 + Log(prefix string, format string, v ...interface{}) + + //不同级别的日志处理 + Debug(format string, v ...interface{}) + Info(format string, v ...interface{}) + Warn(format string, v ...interface{}) + Error(format string, v ...interface{}) + Panic(format string, v ...interface{}) +} diff --git a/code/tool/util/util_path.go b/code/tool/util/util_file.go similarity index 96% rename from code/tool/util/util_path.go rename to code/tool/util/util_file.go index d0ea7a6..746169b 100644 --- a/code/tool/util/util_path.go +++ b/code/tool/util/util_file.go @@ -40,8 +40,7 @@ func GetDevHomePath() string { panic("cannot get dev home path.") } - fmt.Println(file) - + //$DevHomePath/code/tool/util/util_file.go dir := GetDirOfPath(file) dir = GetDirOfPath(dir) dir = GetDirOfPath(dir) @@ -69,9 +68,11 @@ func GetHomePath() string { //如果exPath中包含了 \\AppData\\Local\\Temp 我们认为是在Win的开发环境中 systemUser, err := user.Current() - winDev := strings.HasPrefix(exPath, systemUser.HomeDir+"\\AppData\\Local\\Temp") - if winDev { - exPath = GetDevHomePath() + "/tmp" + if systemUser != nil { + winDev := strings.HasPrefix(exPath, systemUser.HomeDir+"\\AppData\\Local\\Temp") + if winDev { + exPath = GetDevHomePath() + "/tmp" + } } return exPath diff --git a/code/tool/util/util_framework.go b/code/tool/util/util_framework.go index 3257dad..4040a0f 100644 --- a/code/tool/util/util_framework.go +++ b/code/tool/util/util_framework.go @@ -1,16 +1,14 @@ package util //带有panic恢复的方法 -func PanicHandler() { - if err := recover(); err != nil { - //TODO 全局日志记录 - //LOGGER.Error("异步任务错误: %v", err) - } -} +func RunWithRecovery(f func()) { + defer func() { + if err := recover(); err != nil { + //TODO 全局日志记录 + //LOGGER.Error("异步任务错误: %v", err) + } + }() -//带有panic恢复的方法 -func SafeMethod(f func()) { - defer PanicHandler() //执行函数 f() } diff --git a/main.go b/main.go index e246427..cc5f1de 100644 --- a/main.go +++ b/main.go @@ -3,8 +3,9 @@ package main import ( "fmt" "github.com/eyebluecn/tank/code/config" - "github.com/eyebluecn/tank/code/logger" "github.com/eyebluecn/tank/code/rest" + "github.com/eyebluecn/tank/code/support" + "github.com/eyebluecn/tank/code/tool/inter" _ "github.com/go-sql-driver/mysql" "log" "net/http" @@ -13,8 +14,10 @@ import ( func main() { //日志第一优先级保障 - logger.LOGGER.Init() - defer logger.LOGGER.Destroy() + tankLogger := &support.TankLogger{} + tankLogger.Init() + defer tankLogger.Destroy() + inter.LOGGER = tankLogger //装载配置文件,这个决定了是否需要执行安装过程 config.CONFIG.Init() @@ -25,7 +28,7 @@ func main() { http.Handle("/", rest.CONTEXT.Router) - logger.LOGGER.Info("App started at http://localhost:%v", config.CONFIG.ServerPort) + inter.LOGGER.Info("App started at http://localhost:%v", config.CONFIG.ServerPort) dotPort := fmt.Sprintf(":%v", config.CONFIG.ServerPort) err1 := http.ListenAndServe(dotPort, nil)