From 69b25f28744ed122bf5a163086f2fa60a5153fca Mon Sep 17 00:00:00 2001 From: Hugo-X Date: Fri, 28 Jul 2023 22:38:30 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=8A=9F=E8=83=BD=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修正当前工作目录 2. 添加命令行选项-dironly,用于只分析目录避免解压文件包以加快部分场景下的分析速度 3. 添加命令行选项-log,用于指定日志文件位置 4. 支持sqlite报告输出,当sqlite文件不存在时新建,当文件已存在时增量插入(暂时只盘点组件清单) 5. 支持csv报告输出(暂时只盘点组件清单) 6. 调整默认配置文件$HOME/opensca_config.json --- analyzer/engine/archive.go | 4 ++ cli/go.mod | 13 +++++ cli/go.sum | 22 ++++++++ cli/main.go | 48 ++++++++++------ go.work.sum | 9 +++ util/args/args.go | 32 +++++++---- util/logs/log.go | 17 +++++- util/report/csv.go | 35 ++++++++++++ util/report/json.go | 4 +- util/report/sqlite.go | 107 ++++++++++++++++++++++++++++++++++++ util/report/sqlite_init.sql | 38 +++++++++++++ util/report/statis.go | 2 +- util/temp/temp.go | 18 +++++- 13 files changed, 312 insertions(+), 37 deletions(-) create mode 100644 util/report/csv.go create mode 100644 util/report/sqlite.go create mode 100644 util/report/sqlite_init.sql diff --git a/analyzer/engine/archive.go b/analyzer/engine/archive.go index d4882ca..4e05c2a 100644 --- a/analyzer/engine/archive.go +++ b/analyzer/engine/archive.go @@ -13,6 +13,7 @@ import ( "os" "path" "strings" + "util/args" "util/bar" "util/filter" "util/logs" @@ -134,6 +135,9 @@ func (e Engine) opendir(dirpath string) (dir *model.DirTree) { dir.SubDir[filename] = e.opendir(filepath) } else { if filter.AllPkg(filename) { + if args.Config.DirOnly { + continue + } dir.DirList = append(dir.DirList, filename) dir.SubDir[filename] = e.unArchiveFile(filepath) } else if e.checkFile(filename) { diff --git a/cli/go.mod b/cli/go.mod index 9ad835e..1104952 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -1,3 +1,16 @@ module cli go 1.18 + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/sys v0.7.0 // indirect + modernc.org/libc v1.22.5 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.5.0 // indirect + modernc.org/sqlite v1.23.1 // indirect +) diff --git a/cli/go.sum b/cli/go.sum index e69de29..5366fb2 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -0,0 +1,22 @@ +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= +modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= +modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= +modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= diff --git a/cli/main.go b/cli/main.go index 2e3beda..8a52d6a 100644 --- a/cli/main.go +++ b/cli/main.go @@ -43,7 +43,8 @@ func output(depRoot *model.DepTree, taskInfo report.TaskInfo) { var reportFunc func(*model.DepTree, report.TaskInfo) []byte var reportByWriterFunc func(io.Writer, *model.DepTree, report.TaskInfo) out := args.Config.Out - switch path.Ext(out) { + ext := path.Ext(out) + switch ext { case ".html": reportFunc = report.Html case ".json": @@ -70,25 +71,38 @@ func output(depRoot *model.DepTree, taskInfo report.TaskInfo) { } else { reportFunc = report.Xml } + case ".csv": + reportFunc = report.Csv + case ".sqlite": + reportFunc = report.Sqlite default: reportFunc = report.Json } - fmt.Println(report.Statis(depRoot, taskInfo)) - if out != "" { - // 尝试创建导出文件目录 - if err := os.MkdirAll(filepath.Dir(out), fs.ModePerm); err != nil { - logs.Warn(err) - fmt.Println(err) - return - } - if reportFunc != nil { - report.Save(reportFunc(depRoot, taskInfo), out) - } else if reportByWriterFunc != nil { - report.SaveByWriter(func(w io.Writer) { - reportByWriterFunc(w, depRoot, taskInfo) - }, out) - } + + if ext == ".sqlite" { + result := reportFunc(depRoot, taskInfo) + logs.Debug(string(result)) } else { - fmt.Println(string(reportFunc(depRoot, taskInfo))) + if out != "" { + pwd, _ := os.Getwd() + fmt.Printf("Working directory: %s, Output file: %s\n", pwd, out) + // 尝试创建导出文件目录 + if err := os.MkdirAll(filepath.Dir(out), fs.ModePerm); err != nil { + logs.Warn(err) + fmt.Println(err) + return + } + if reportFunc != nil { + report.Save(reportFunc(depRoot, taskInfo), out) + } else if reportByWriterFunc != nil { + report.SaveByWriter(func(w io.Writer) { + reportByWriterFunc(w, depRoot, taskInfo) + }, out) + } + } else { + fmt.Println(string(reportFunc(depRoot, taskInfo))) + } } + + fmt.Println(report.Statis(depRoot, taskInfo)) } diff --git a/go.work.sum b/go.work.sum index 615958f..06f51c8 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1 +1,10 @@ +github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= +github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780 h1:tFh1tRc4CA31yP6qDcu+Trax5wW5GuMxvkIba07qVLY= +github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E= +github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= diff --git a/util/args/args.go b/util/args/args.go index 60f9751..61ee4fe 100644 --- a/util/args/args.go +++ b/util/args/args.go @@ -10,22 +10,25 @@ import ( "flag" "fmt" "io/ioutil" - "path" + "log" + "os/user" + "path/filepath" "strings" - "util/temp" ) var ( ConfigPath string Config = struct { // detect option - Path string `json:"path"` - Out string `json:"out"` + Path string `json:"path"` + Out string `json:"out"` + Logfile string `json:"log"` + DirOnly bool `json:dironly` // 永远开启 - Cache bool `json:"-"` - Bar bool `json:"progress"` - OnlyVuln bool `json:"vuln"` - Dedup bool `json:"dedup"` + Cache bool `json:"-"` + Bar bool `json:"progress"` + OnlyVuln bool `json:"vuln"` + Dedup bool `json:"dedup"` // remote vuldb Url string `json:"url"` Token string `json:"token"` @@ -45,10 +48,12 @@ func init() { flag.StringVar(&Config.Token, "token", Config.Token, "(可选,与url需一起使用) 云服务验证token,需要在云服务平台申请") flag.BoolVar(&Config.Cache, "cache", Config.Cache, "(已弃用/永远开启) 缓存下载的文件(例如pom文件),重复检测相同组件时会节省时间,下载的文件会保存到工具所在目录的.cache目录下") flag.BoolVar(&Config.OnlyVuln, "vuln", Config.OnlyVuln, "(可选) 结果仅保留有漏洞信息的组件,使用该参数不会保留组件层级结构") - flag.StringVar(&Config.Out, "out", Config.Out, "(可选) 将检测结果保存到指定文件,根据后缀生成不同格式的文件,默认为json格式,例: -out output.json") + flag.StringVar(&Config.Out, "out", Config.Out, "(可选) 将检测结果保存到指定文件,根据后缀生成不同格式的文件,支持的后缀有:.html, .json, .spdx.json, .spdx.xml, .csv, .sqlite, 默认为json格式,例: -out output.json") flag.StringVar(&Config.VulnDB, "db", Config.VulnDB, "(可选) 指定本地漏洞库文件,希望使用自己漏洞库时可用,漏洞库文件为json格式,具体格式会在开源项目文档中给出;若同时使用云端漏洞库与本地漏洞库,漏洞查询结果取并集,例: -db db.json") flag.BoolVar(&Config.Bar, "progress", Config.Bar, "(可选) 显示进度条") flag.BoolVar(&Config.Dedup, "dedup", Config.Dedup, "(可选) 相同组件去重") + flag.BoolVar(&Config.DirOnly, "dironly", Config.DirOnly, "(可选) 仅检测目录,忽略压缩包,加速基于源码的检测") + flag.StringVar(&Config.Logfile, "log", Config.Logfile, "(可选) 指定日志文件路径") } func Parse() { @@ -62,8 +67,13 @@ func Parse() { } } } else { - // 默认读取目录下的config.json文件 - if data, err := ioutil.ReadFile(path.Join(temp.GetPwd(), "config.json")); err == nil { + // 默认读取HOME目录下的opensca_config.json + user, err := user.Current() + if err != nil { + log.Fatalf(err.Error()) + } + + if data, err := ioutil.ReadFile(filepath.Join(user.HomeDir, "opensca_config.json")); err == nil { // 不处理错误 json.Unmarshal(data, &Config) } diff --git a/util/logs/log.go b/util/logs/log.go index 2a6abd7..fd65f04 100644 --- a/util/logs/log.go +++ b/util/logs/log.go @@ -11,6 +11,7 @@ import ( "os" "path" "strings" + "util/args" "github.com/pkg/errors" ) @@ -38,8 +39,14 @@ var ( func init() { // 创建日志文件 var err error - dir, _ := os.Executable() - filepath := path.Join(path.Dir(strings.ReplaceAll(dir, `\`, `/`)), "opensca.log") + cwd, _ := os.Getwd() //获取当前工作目录 + filepath := args.Config.Logfile + + if filepath == "" { + filepath = path.Join(strings.ReplaceAll(cwd, `\`, `/`), "opensca.log") + } + + fmt.Printf("log file: %s\n", filepath) logFile, err = os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0777) if err != nil { fmt.Println("log file create fail!") @@ -58,7 +65,11 @@ func out(level logLevel, v interface{}) { return } logger.SetPrefix(fmt.Sprintf("[%s] ", prefixs[level])) - logger.Output(3, fmt.Sprint(v)) + err := logger.Output(3, fmt.Sprint(v)) + if err != nil { + fmt.Println(errors.WithStack(err).Error()) + return + } } func Debug(v interface{}) { diff --git a/util/report/csv.go b/util/report/csv.go new file mode 100644 index 0000000..375e8be --- /dev/null +++ b/util/report/csv.go @@ -0,0 +1,35 @@ +package report + +import ( + "fmt" + "util/model" +) + +// Csv csv格式报告数据 +func Csv(dep *model.DepTree, taskInfo TaskInfo) []byte { + + if taskInfo.Error != nil { + taskInfo.ErrorString = taskInfo.Error.Error() + } + + table := "Name, Version, Vendor, License, Langauge, PURL\n" + + // 遍历所有组件树,提取关键字段 + q := []*model.DepTree{dep} + for len(q) > 0 { + n := q[0] + q = append(q[1:], n.Children...) + + licenseTxt := "" + if len(n.Licenses) > 0 { + licenseTxt = n.Licenses[0].ShortName + } + + if n.Name != "" { + table = table + fmt.Sprintf("%s,%s,%s,%s,%s,%s\n", n.Name, n.VersionStr, n.Vendor, licenseTxt, n.LanguageStr, n.Purl()) + } + + } + + return []byte(table) +} diff --git a/util/report/json.go b/util/report/json.go index 3e707bd..65d75fe 100644 --- a/util/report/json.go +++ b/util/report/json.go @@ -11,13 +11,13 @@ func Json(dep *model.DepTree, taskInfo TaskInfo) []byte { if taskInfo.Error != nil { taskInfo.ErrorString = taskInfo.Error.Error() } - if data, err := json.Marshal(struct { + if data, err := json.MarshalIndent(struct { TaskInfo TaskInfo `json:"task_info"` *model.DepTree }{ TaskInfo: taskInfo, DepTree: dep, - }); err != nil { + }, "", " "); err != nil { logs.Error(err) } else { return data diff --git a/util/report/sqlite.go b/util/report/sqlite.go new file mode 100644 index 0000000..0051b2b --- /dev/null +++ b/util/report/sqlite.go @@ -0,0 +1,107 @@ +package report + +import ( + "database/sql" + _ "embed" + "fmt" + "log" + "os" + "os/user" + "path/filepath" + "strings" + "util/args" + "util/logs" + "util/model" + + _ "github.com/glebarez/go-sqlite" +) + +func getHomeDir() string { + user, err := user.Current() + if err != nil { + log.Fatalf(err.Error()) + } + return user.HomeDir +} + +//go:embed sqlite_init.sql +var initSql string + +// Sqlite sql格式报告数据 +func Sqlite(dep *model.DepTree, taskInfo TaskInfo) []byte { + + dbFile := filepath.Clean(args.Config.Out) + initRequired := false + + if dbFile == "" { + dbFile = filepath.Join(getHomeDir(), ".opensca/opensca.db") + } + + logs.Info("sqlite location: " + dbFile) + + _, err := os.Stat(dbFile) + + if os.IsNotExist(err) { + initRequired = true + } + + db, err := sql.Open("sqlite", dbFile) + if err != nil { + logs.Error(err) + } + + if initRequired { + logs.Info("initing database: " + dbFile) + db.Exec(initSql) + } + + if taskInfo.Error != nil { + taskInfo.ErrorString = taskInfo.Error.Error() + } + + absPath, _ := filepath.Abs(filepath.Clean(args.Config.Path)) + moduleName := filepath.Base(absPath) + + if dep.Name != "" { + moduleName = dep.Name + } + + result := fmt.Sprintf("\n---- sql report of %s\n", moduleName) + insertFmt := "insert or ignore into component (name, version, vendor, language, purl) values ('%s','%s','%s','%s','%s');\n" + insertRef := "insert or ignore into reference (module_name, purl) values ('%s','%s');\n" + + // 遍历所有组件树,提取需求字段,输出SQL语句 + q := []*model.DepTree{dep} + for len(q) > 0 { + n := q[0] + q = append(q[1:], n.Children...) + + if n.Name != "" { + db.Exec("insert or ignore into component (name, version, vendor, language, purl) values (?,?,?,?,?)", n.Name, n.VersionStr, n.Vendor, n.LanguageStr, n.Purl()) + result = result + fmt.Sprintf(insertFmt, quoteEscape(n.Name), quoteEscape(n.VersionStr), quoteEscape(n.Vendor), quoteEscape(n.LanguageStr), quoteEscape(n.Purl())) + } + + } + + q = []*model.DepTree{dep} + for len(q) > 0 { + n := q[0] + q = append(q[1:], n.Children...) + + if n.Name != "" { + result = result + fmt.Sprintf(insertRef, moduleName, quoteEscape(n.Purl())) + db.Exec("insert or ignore into reference (module_name, purl) values (?, ?)", moduleName, n.Purl()) + } + + } + + result = result + fmt.Sprintf("---- sql report of %s\n", moduleName) + + return []byte(result) +} + +// quoteEscape 转义单引号 +func quoteEscape(src string) (out string) { + out = strings.ReplaceAll(src, `'`, "\\'") + return out +} diff --git a/util/report/sqlite_init.sql b/util/report/sqlite_init.sql new file mode 100644 index 0000000..e56c5c3 --- /dev/null +++ b/util/report/sqlite_init.sql @@ -0,0 +1,38 @@ +-- +-- File generated with SQLiteStudio v3.4.3 on 22:18:37 2023-07-28 +-- +-- Text encoding used: System +-- +PRAGMA foreign_keys = off; +BEGIN TRANSACTION; + +-- Table: component +CREATE TABLE IF NOT EXISTS component ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR (50) NOT NULL, + version VARCHAR (50) NOT NULL, + vendor VARCHAR (50) + DEFAULT 'N.A.', + language VARCHAR (50) NOT NULL, + purl VARCHAR (256) NOT NULL + UNIQUE +); + + +-- Table: reference +CREATE TABLE IF NOT EXISTS reference ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + module_name VARCHAR (100) NOT NULL, + purl VARCHAR (256) NOT NULL +); + + +-- Index: uk_ref +CREATE UNIQUE INDEX IF NOT EXISTS uk_ref ON reference ( + module_name, + purl +); + + +COMMIT TRANSACTION; +PRAGMA foreign_keys = on; diff --git a/util/report/statis.go b/util/report/statis.go index 0bc41b4..fc30bd3 100644 --- a/util/report/statis.go +++ b/util/report/statis.go @@ -40,7 +40,7 @@ func Statis(depRoot *model.DepTree, taskInfo TaskInfo) string { } q = append(q[1:], n.Children...) } - return fmt.Sprintf("\nComplete!"+ + return fmt.Sprintf("Complete!"+ "\nComponents:%d C:%d H:%d M:%d L:%d"+ "\nVulnerabilities:%d C:%d H:%d M:%d L:%d", coms[0], coms[1], coms[2], coms[3], coms[4], diff --git a/util/temp/temp.go b/util/temp/temp.go index 765a187..ed5b4da 100644 --- a/util/temp/temp.go +++ b/util/temp/temp.go @@ -1,28 +1,40 @@ package temp import ( + "fmt" "os" "path" "strconv" - "strings" "time" "util/logs" ) const tempdir = ".temp" +func printpwd(s string) { + cwd, _ := os.Getwd() + fmt.Println(s, "cwd:", cwd) +} + func init() { os.RemoveAll(path.Join(GetPwd(), tempdir)) } // GetPwd 获取当前目录 func GetPwd() string { - filepath, err := os.Executable() + // filepath, err := os.Executable() + pwd, err := os.Getwd() + // printpwd("temp.28") if err != nil { logs.Error(err) return "" } - return path.Dir(strings.ReplaceAll(filepath, `\`, `/`)) + // fmt.Println(filepath, "replaced=>", strings.ReplaceAll(filepath, `\`, `/`)) + // pwd := path.Dir(strings.ReplaceAll(filepath, `\`, `/`)) + // pwd = strings.ReplaceAll(pwd, `\`, `/`) + // fmt.Println("temp.GetPwd:", pwd) + + return pwd } // DoInTempDir 在临时目录中执行 -- Gitee From cda950cefe2d462bd560ed3941ae17a36b5dc8d2 Mon Sep 17 00:00:00 2001 From: Hugo-X Date: Mon, 31 Jul 2023 22:32:06 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=9A=84=E5=88=9B=E5=BB=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 日志文件的创建时机调整到默认init之后 2. 连带创建日志文件的前置目录 3. 更新args.go中Config的DirOnly字段Tag --- util/args/args.go | 2 +- util/logs/log.go | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/util/args/args.go b/util/args/args.go index 61ee4fe..e63c4f9 100644 --- a/util/args/args.go +++ b/util/args/args.go @@ -23,7 +23,7 @@ var ( Path string `json:"path"` Out string `json:"out"` Logfile string `json:"log"` - DirOnly bool `json:dironly` + DirOnly bool `json:"dironly"` // 永远开启 Cache bool `json:"-"` Bar bool `json:"progress"` diff --git a/util/logs/log.go b/util/logs/log.go index fd65f04..20d78f3 100644 --- a/util/logs/log.go +++ b/util/logs/log.go @@ -10,6 +10,7 @@ import ( "log" "os" "path" + "path/filepath" "strings" "util/args" @@ -36,18 +37,25 @@ var ( logger *log.Logger ) -func init() { +func initLogger() { // 创建日志文件 var err error cwd, _ := os.Getwd() //获取当前工作目录 - filepath := args.Config.Logfile + logFilePath := args.Config.Logfile + // fmt.Printf("Log file initLogger:%s\n", args.Config.Logfile) - if filepath == "" { - filepath = path.Join(strings.ReplaceAll(cwd, `\`, `/`), "opensca.log") + if logFilePath == "" { + logFilePath = path.Join(strings.ReplaceAll(cwd, `\`, `/`), "opensca.log") } - fmt.Printf("log file: %s\n", filepath) - logFile, err = os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0777) + fmt.Printf("log file: %s\n", logFilePath) + + if err := os.MkdirAll(filepath.Dir(logFilePath), os.ModeDir); err != nil { + fmt.Println(err) + return + } + + logFile, err = os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0777) if err != nil { fmt.Println("log file create fail!") } else { @@ -62,7 +70,7 @@ func GetLogFile() *os.File { func out(level logLevel, v interface{}) { if logger == nil { - return + initLogger() } logger.SetPrefix(fmt.Sprintf("[%s] ", prefixs[level])) err := logger.Output(3, fmt.Sprint(v)) -- Gitee