diff --git a/g/crypto/gaes/gaes.go b/g/crypto/gaes/gaes.go index 62b77c257..746deba74 100644 --- a/g/crypto/gaes/gaes.go +++ b/g/crypto/gaes/gaes.go @@ -65,7 +65,6 @@ func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) { if e != nil { return nil, e } - return plainText, nil } diff --git a/g/crypto/gcrc32/gcrc32.go b/g/crypto/gcrc32/gcrc32.go index 8ee9f1a99..28052d0d4 100644 --- a/g/crypto/gcrc32/gcrc32.go +++ b/g/crypto/gcrc32/gcrc32.go @@ -4,17 +4,26 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gcrc32 provides useful API for CRC32 encryption/decryption algorithms. +// Package gcrc32 provides useful API for CRC32 encryption algorithms. package gcrc32 import ( - "hash/crc32" + "github.com/gogf/gf/g/util/gconv" + "hash/crc32" ) +// Encrypt encrypts any type of variable using CRC32 algorithms. +// It uses gconv package to convert to its bytes type. +func Encrypt(v interface{}) uint32 { + return crc32.ChecksumIEEE(gconv.Bytes(v)) +} + +// Deprecated. func EncryptString(v string) uint32 { return crc32.ChecksumIEEE([]byte(v)) } +// Deprecated. func EncryptBytes(v []byte) uint32 { return crc32.ChecksumIEEE(v) } diff --git a/g/crypto/gdes/gdes.go b/g/crypto/gdes/gdes.go index 0ca8dcb8a..f115692a4 100644 --- a/g/crypto/gdes/gdes.go +++ b/g/crypto/gdes/gdes.go @@ -3,7 +3,8 @@ // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// @author: wenzi1 +// +// @author wenzi1 // Package gdes provides useful API for DES encryption/decryption algorithms. package gdes @@ -16,11 +17,11 @@ import ( ) const ( - NOPADDING = iota + NOPADDING = iota PKCS5PADDING ) -//ECB模式DES加密 +// ECB模式DES加密 func DesECBEncrypt(key []byte, clearText []byte, padding int) ([]byte, error) { text, err := Padding(clearText, padding) if err != nil { @@ -42,7 +43,7 @@ func DesECBEncrypt(key []byte, clearText []byte, padding int) ([]byte, error) { return cipherText, nil } -//ECB模式DES解密 +// ECB模式DES解密 func DesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) { text := make([]byte, len(cipherText)) block, err := des.NewCipher(key) @@ -63,7 +64,7 @@ func DesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) { return clearText, nil } -//ECB模式3DES加密,密钥长度可以是16或24位长 +// ECB模式3DES加密,密钥长度可以是16或24位长 func TripleDesECBEncrypt(key []byte, clearText []byte, padding int) ( []byte, error) { if len(key) != 16 && len(key) != 24 { return nil, errors.New("key length error") @@ -96,7 +97,7 @@ func TripleDesECBEncrypt(key []byte, clearText []byte, padding int) ( []byte, er return cipherText, nil } -//ECB模式3DES解密,密钥长度可以是16或24位长 +// ECB模式3DES解密,密钥长度可以是16或24位长 func TripleDesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { return nil, errors.New("key length error") @@ -129,7 +130,7 @@ func TripleDesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, e return clearText, nil } -//CBC模式DES加密 +// CBC模式DES加密 func DesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { @@ -152,7 +153,7 @@ func DesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ([]byte return cipherText, nil } -//CBC模式DES解密 +// CBC模式DES解密 func DesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { @@ -175,7 +176,7 @@ func DesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) ([]byt return clearText, nil } -//CBC模式3DES加密 +// CBC模式3DES加密 func TripleDesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { return nil, errors.New("key length invalid") @@ -210,7 +211,7 @@ func TripleDesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) ( return cipherText, nil } -//CBC模式3DES解密 +// CBC模式3DES解密 func TripleDesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) ( []byte, error) { if len(key) != 16 && len(key) != 24 { return nil, errors.New("key length invalid") @@ -245,21 +246,21 @@ func TripleDesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) return clearText, nil } -//PKCS5补位 +// PKCS5补位 func PKCS5Padding(text []byte, blockSize int) []byte { padding := blockSize - len(text) % blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(text, padtext...) } -//去除PKCS5补位 +// 去除PKCS5补位 func PKCS5Unpadding(text []byte) []byte{ length := len(text) padtext := int(text[length - 1]) return text[:(length - padtext)] } -//补位方法 +// 补位方法 func Padding(text []byte, padding int)([]byte, error) { switch padding { case NOPADDING: @@ -275,7 +276,7 @@ func Padding(text []byte, padding int)([]byte, error) { return text, nil } -//去除补位方法 +// 去除补位方法 func UnPadding(text []byte, padding int)([]byte, error) { switch padding { case NOPADDING: diff --git a/g/crypto/gmd5/gmd5.go b/g/crypto/gmd5/gmd5.go index aa5fab0a3..63fc08911 100644 --- a/g/crypto/gmd5/gmd5.go +++ b/g/crypto/gmd5/gmd5.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gmd5 provides useful API for MD5 encryption/decryption algorithms. +// Package gmd5 provides useful API for MD5 encryption algorithms. package gmd5 import ( @@ -15,28 +15,31 @@ import ( "github.com/gogf/gf/g/util/gconv" ) -// 将任意类型的变量进行md5摘要(注意map等非排序变量造成的不同结果) +// Encrypt encrypts any type of variable using MD5 algorithms. +// It uses gconv package to convert to its bytes type. func Encrypt(v interface{}) string { h := md5.New() h.Write([]byte(gconv.Bytes(v))) return fmt.Sprintf("%x", h.Sum(nil)) } -// 将字符串进行MD5哈希摘要计算 + +// Deprecated. func EncryptString(v string) string { - h := md5.New() - h.Write([]byte(v)) - return fmt.Sprintf("%x", h.Sum(nil)) + h := md5.New() + h.Write([]byte(v)) + return fmt.Sprintf("%x", h.Sum(nil)) } -// 将文件内容进行MD5哈希摘要计算 + +// EncryptFile encrypts file content of using MD5 algorithms. func EncryptFile(path string) string { f, e := os.Open(path) if e != nil { return "" } defer f.Close() - h := md5.New() + h := md5.New() _, e = io.Copy(h, f) if e != nil { return "" diff --git a/g/crypto/gsha1/gsha1.go b/g/crypto/gsha1/gsha1.go index 8caa35aee..a12a02884 100644 --- a/g/crypto/gsha1/gsha1.go +++ b/g/crypto/gsha1/gsha1.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gsha1 provides useful API for SHA1 encryption/decryption algorithms. +// Package gsha1 provides useful API for SHA1 encryption algorithms. package gsha1 import ( @@ -15,20 +15,20 @@ import ( "github.com/gogf/gf/g/util/gconv" ) -// 将任意类型的变量进行SHA摘要(注意map等非排序变量造成的不同结果) -// 内部使用了md5计算,因此效率会稍微差一些,更多情况请使用 EncodeString +// Encrypt encrypts any type of variable using SHA1 algorithms. +// It uses gconv package to convert to its bytes type. func Encrypt(v interface{}) string { r := sha1.Sum(gconv.Bytes(v)) return hex.EncodeToString(r[:]) } -// 对字符串行SHA1摘要计算 +// Deprecated. func EncryptString(s string) string { - r := sha1.Sum([]byte(s)) - return hex.EncodeToString(r[:]) + r := sha1.Sum([]byte(s)) + return hex.EncodeToString(r[:]) } -// 对文件内容进行SHA1摘要计算 +// EncryptFile encrypts file content of using SHA1 algorithms. func EncryptFile(path string) string { f, e := os.Open(path) if e != nil { diff --git a/g/encoding/gjson/gjson_api.go b/g/encoding/gjson/gjson_api.go index 028478b8e..25db8a035 100644 --- a/g/encoding/gjson/gjson_api.go +++ b/g/encoding/gjson/gjson_api.go @@ -64,29 +64,44 @@ func (j *Json) GetMap(pattern string, def...interface{}) map[string]interface{} } // GetJson gets the value by specified , -// and converts it to a Json object. +// and converts it to a un-concurrent-safe Json object. func (j *Json) GetJson(pattern string, def...interface{}) *Json { result := j.Get(pattern, def...) if result != nil { - return New(result) + return New(result, true) } return nil } // GetJsons gets the value by specified , -// and converts it to a slice of Json object. +// and converts it to a slice of un-concurrent-safe Json object. func (j *Json) GetJsons(pattern string, def...interface{}) []*Json { array := j.GetArray(pattern, def...) if len(array) > 0 { - jsons := make([]*Json, len(array)) + jsonSlice := make([]*Json, len(array)) for i := 0; i < len(array); i++ { - jsons[i] = New(array[i], !j.mu.IsSafe()) + jsonSlice[i] = New(array[i], true) } - return jsons + return jsonSlice } return nil } +// GetJsonMap gets the value by specified , +// and converts it to a map of un-concurrent-safe Json object. +func (j *Json) GetJsonMap(pattern string, def...interface{}) map[string]*Json { + m := j.GetMap(pattern, def...) + if len(m) > 0 { + jsonMap := make(map[string]*Json, len(m)) + for k, v := range m { + jsonMap[k] = New(v, true) + } + return jsonMap + } + return nil +} + + // GetArray gets the value by specified , // and converts it to a slice of []interface{}. func (j *Json) GetArray(pattern string, def...interface{}) []interface{} { diff --git a/g/encoding/gjson/gjson_z_unit_basic_test.go b/g/encoding/gjson/gjson_z_unit_basic_test.go index b1d3430f1..0a43eee58 100644 --- a/g/encoding/gjson/gjson_z_unit_basic_test.go +++ b/g/encoding/gjson/gjson_z_unit_basic_test.go @@ -264,3 +264,39 @@ func TestJson_ToJson(t *testing.T) { }) } +func TestJson_Default(t *testing.T) { + gtest.Case(t, func() { + j := gjson.New(nil) + gtest.AssertEQ(j.Get("no", 100), 100) + gtest.AssertEQ(j.GetString("no", 100), "100") + gtest.AssertEQ(j.GetBool("no", "on"), true) + gtest.AssertEQ(j.GetInt("no", 100), 100) + gtest.AssertEQ(j.GetInt8("no", 100), int8(100)) + gtest.AssertEQ(j.GetInt16("no", 100), int16(100)) + gtest.AssertEQ(j.GetInt32("no", 100), int32(100)) + gtest.AssertEQ(j.GetInt64("no", 100), int64(100)) + gtest.AssertEQ(j.GetUint("no", 100), uint(100)) + gtest.AssertEQ(j.GetUint8("no", 100), uint8(100)) + gtest.AssertEQ(j.GetUint16("no", 100), uint16(100)) + gtest.AssertEQ(j.GetUint32("no", 100), uint32(100)) + gtest.AssertEQ(j.GetUint64("no", 100), uint64(100)) + gtest.AssertEQ(j.GetFloat32("no", 123.456), float32(123.456)) + gtest.AssertEQ(j.GetFloat64("no", 123.456), float64(123.456)) + gtest.AssertEQ(j.GetArray("no", g.Slice{1,2,3}), g.Slice{1,2,3}) + gtest.AssertEQ(j.GetInts("no", g.Slice{1,2,3}), g.SliceInt{1,2,3}) + gtest.AssertEQ(j.GetFloats("no", g.Slice{1,2,3}), []float64{1,2,3}) + gtest.AssertEQ(j.GetMap("no", g.Map{"k":"v"}), g.Map{"k":"v"}) + gtest.AssertEQ(j.GetVar("no", 123.456).Float64(), float64(123.456)) + gtest.AssertEQ(j.GetJson("no", g.Map{"k":"v"}).Get("k"), "v") + gtest.AssertEQ(j.GetJsons("no", g.Slice{ + g.Map{"k1":"v1"}, + g.Map{"k2":"v2"}, + g.Map{"k3":"v3"}, + })[0].Get("k1"), "v1") + gtest.AssertEQ(j.GetJsonMap("no", g.Map{ + "m1" : g.Map{"k1":"v1"}, + "m2" : g.Map{"k2":"v2"}, + })["m2"].Get("k2"), "v2") + }) +} + diff --git a/g/net/ghttp/ghttp_server_config.go b/g/net/ghttp/ghttp_server_config.go index 5619d2e7c..6f7fbad39 100644 --- a/g/net/ghttp/ghttp_server_config.go +++ b/g/net/ghttp/ghttp_server_config.go @@ -76,7 +76,7 @@ type ServerConfig struct { // 日志配置 LogPath string // 存放日志的目录路径(默认为空,表示不写文件) LogHandler LogHandler // 自定义日志处理回调方法(默认为空) - LogStdPrint bool // 是否打印日志到终端(默认开启) + LogStdout bool // 是否打印日志到终端(默认开启) ErrorLogEnabled bool // 是否开启error log(默认开启) AccessLogEnabled bool // 是否开启access log(默认关闭) @@ -111,7 +111,7 @@ var defaultServerConfig = ServerConfig { SessionMaxAge : gDEFAULT_SESSION_MAX_AGE, SessionIdName : gDEFAULT_SESSION_ID_NAME, - LogStdPrint : true, + LogStdout : true, ErrorLogEnabled : true, AccessLogEnabled : false, GzipContentTypes : defaultGzipContentTypes, diff --git a/g/net/ghttp/ghttp_server_config_logger.go b/g/net/ghttp/ghttp_server_config_logger.go index 5482de3fe..fce013d2d 100644 --- a/g/net/ghttp/ghttp_server_config_logger.go +++ b/g/net/ghttp/ghttp_server_config_logger.go @@ -28,12 +28,12 @@ func (s *Server)SetLogPath(path string) { // 设置日志内容是否输出到终端,默认情况下只有错误日志才会自动输出到终端。 // 如果需要输出请求日志到终端,默认情况下使用SetAccessLogEnabled方法开启请求日志特性即可。 -func (s *Server)SetLogStdPrint(enabled bool) { +func (s *Server)SetLogStdout(enabled bool) { if s.Status() == SERVER_STATUS_RUNNING { glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR) return } - s.config.LogStdPrint = enabled + s.config.LogStdout = enabled } // 设置是否开启access log日志功能 diff --git a/g/net/ghttp/ghttp_server_log.go b/g/net/ghttp/ghttp_server_log.go index 3801abf4f..2f4b3158d 100644 --- a/g/net/ghttp/ghttp_server_log.go +++ b/g/net/ghttp/ghttp_server_log.go @@ -32,7 +32,7 @@ func (s *Server) handleAccessLog(r *Request) { ) content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000) content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent()) - s.logger.Cat("access").Backtrace(false, 2).StdPrint(s.config.LogStdPrint).Println(content) + s.logger.Cat("access").Backtrace(false, 2).Stdout(s.config.LogStdout).Println(content) } // 处理服务错误信息,主要是panic,http请求的status由access log进行管理 @@ -60,5 +60,5 @@ func (s *Server) handleErrorLog(error interface{}, r *Request) { content += fmt.Sprintf(` %.3f`, float64(gtime.Microsecond() - r.EnterTime)/1000) } content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent()) - s.logger.Cat("error").Backtrace(true, 2).StdPrint(s.config.LogStdPrint).Error(content) + s.logger.Cat("error").Backtrace(true, 2).Stdout(s.config.LogStdout).Error(content) } diff --git a/g/net/gscanner/scanner.go b/g/net/gscanner/scanner.go deleted file mode 100644 index 84a42fd45..000000000 --- a/g/net/gscanner/scanner.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -// Package gscanner provides a port scanner for local intranet. -package gscanner - -import ( - "net" - "fmt" - "sync" - "time" - "errors" - "github.com/gogf/gf/g/net/gipv4" -) - -type scanner struct { - timeout time.Duration -} - -// 初始化一个扫描器 -func New() *scanner { - return &scanner{ - 6*time.Second, - } -} - -// 设置超时时间,注意这个时间是每一次扫描的超时时间,而不是总共的超时时间 -func (s *scanner) SetTimeout(t time.Duration) *scanner { - s.timeout = t - return s -} - -// 异步TCP扫描网段及端口,如果扫描的端口是打开的,那么将链接给定给回调函数进行调用 -// 注意startIp和endIp需要是同一个网段,否则会报错,并且回调函数不会执行 -func (s *scanner) ScanIp(startIp string, endIp string, port int, callback func(net.Conn)) error { - if callback == nil { - return errors.New("callback function should not be nil") - } - var waitGroup sync.WaitGroup - startIplong := gipv4.Ip2long(startIp) - endIplong := gipv4.Ip2long(endIp) - result := endIplong - startIplong - if startIplong == 0 || endIplong == 0 { - return errors.New("invalid startip or endip: ipv4 string should be given") - } - if result < 0 || result > 255 { - return errors.New("invalid startip and endip: startip and endip should be in the same ip segment") - } - - for i := startIplong; i <= endIplong; i++ { - waitGroup.Add(1) - go func(ip string) { - //fmt.Println("scanning:", ip) - // 这里必需设置超时时间 - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), s.timeout) - if err == nil { - callback(conn) - conn.Close() - } - //fmt.Println("scanning:", ip, "done") - waitGroup.Done() - }(gipv4.Long2ip(i)) - } - waitGroup.Wait() - return nil -} - -// 扫描目标主机打开的端口列表 -func (s *scanner) ScanPort(ip string, callback func(net.Conn)) error { - if callback == nil { - return errors.New("callback function should not be nil") - } - - var waitGroup sync.WaitGroup - for i := 0; i <= 65536; i++ { - waitGroup.Add(1) - //fmt.Println("scanning:", i) - go func(port int) { - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), s.timeout) - if err == nil { - callback(conn) - conn.Close() - } - waitGroup.Done() - }(i) - } - waitGroup.Wait() - return nil -} - diff --git a/g/net/gsmtp/smtp.go b/g/net/gsmtp/gsmtp.go similarity index 61% rename from g/net/gsmtp/smtp.go rename to g/net/gsmtp/gsmtp.go index 7bf53a9c4..c0c787480 100644 --- a/g/net/gsmtp/smtp.go +++ b/g/net/gsmtp/gsmtp.go @@ -5,6 +5,10 @@ // You can obtain one at https://github.com/gogf/gf. // Package gsmtp provides a SMTP client to access remote mail server. +// +// eg: +// s := smtp.New("smtp.exmail.qq.com:25", "notify@a.com", "password") +// glog.Println(s.SendMail("notify@a.com", "ulric@b.com;rain@c.com", "subject", "body, red")) package gsmtp import ( @@ -14,30 +18,31 @@ import ( "strings" ) -// 示例: -// s := smtp.New("smtp.exmail.qq.com:25", "notify@a.com", "password") -// glog.Println(s.SendMail("notify@a.com", "ulric@b.com;rain@c.com", "这是subject", "这是body,red")) - -type Smtp struct { +type SMTP struct { Address string Username string Password string } -func New(address, username, password string) *Smtp { - return &Smtp{ +// New creates and returns a new SMTP object. +func New(address, username, password string) *SMTP { + return &SMTP{ Address: address, Username: username, Password: password, } } -func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...string) error { - if this.Address == "" { +// SendMail connects to the server at addr, switches to TLS if +// possible, authenticates with the optional mechanism a if possible, +// and then sends an email from address from, to addresses to, with +// message msg. +func (s *SMTP) SendMail(from, tos, subject, body string, contentType ...string) error { + if s.Address == "" { return fmt.Errorf("address is necessary") } - hp := strings.Split(this.Address, ":") + hp := strings.Split(s.Address, ":") if len(hp) != 2 { return fmt.Errorf("address format error") } @@ -56,14 +61,13 @@ func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...strin return fmt.Errorf("tos invalid") } - tos = strings.Join(safeArr, ";") - + tos = strings.Join(safeArr, ";") b64 := base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") - header := make(map[string]string) - header["From"] = from - header["To"] = tos - header["Subject"] = fmt.Sprintf("=?UTF-8?B?%s?=", b64.EncodeToString([]byte(subject))) + header := make(map[string]string) + header["From"] = from + header["To"] = tos + header["Subject"] = fmt.Sprintf("=?UTF-8?B?%s?=", b64.EncodeToString([]byte(subject))) header["MIME-Version"] = "1.0" ct := "text/plain; charset=UTF-8" @@ -71,7 +75,7 @@ func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...strin ct = "text/html; charset=UTF-8" } - header["Content-Type"] = ct + header["Content-Type"] = ct header["Content-Transfer-Encoding"] = "base64" message := "" @@ -80,6 +84,6 @@ func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...strin } message += "\r\n" + b64.EncodeToString([]byte(body)) - auth := smtp.PlainAuth("", this.Username, this.Password, hp[0]) - return smtp.SendMail(this.Address, auth, from, strings.Split(tos, ";"), []byte(message)) + auth := smtp.PlainAuth("", s.Username, s.Password, hp[0]) + return smtp.SendMail(s.Address, auth, from, strings.Split(tos, ";"), []byte(message)) } \ No newline at end of file diff --git a/g/os/gfile/gfile.go b/g/os/gfile/gfile.go index 47bbdb680..0097d0468 100644 --- a/g/os/gfile/gfile.go +++ b/g/os/gfile/gfile.go @@ -27,23 +27,20 @@ import ( ) const ( - // 文件分隔符 + // Separator for file system. Separator = string(filepath.Separator) - // 默认的文件打开权限 + // Default perm for file opening. gDEFAULT_PERM = 0666 ) var ( - // 源码的main包所在目录,仅仅会设置一次 - mainPkgPath = gtype.NewString() - - // 编译时的 GOROOT 数值 - goRootOfBuild = gtype.NewString() + // The absolute file path for main package. + // It can be only checked and set once. + mainPkgPath = gtype.NewString() ) -// Create directories recursively. -// -// 给定目录的绝对路径创建目录(递归创建)。 +// Mkdir creates directories recursively with given . +// The parameter is suggested to be absolute path. func Mkdir(path string) error { err := os.MkdirAll(path, os.ModePerm) if err != nil { @@ -52,9 +49,8 @@ func Mkdir(path string) error { return nil } -// Create file with given path recursively. -// -// 给定文件的绝对路径创建文件。 +// Create creates file with given recursively. +// The parameter is suggested to be absolute path. func Create(path string) (*os.File, error) { dir := Dir(path) if !Exists(dir) { @@ -63,23 +59,17 @@ func Create(path string) (*os.File, error) { return os.Create(path) } -// Open file/directory with readonly. -// -// 只读打开文件 +// Open opens file/directory readonly. func Open(path string) (*os.File, error) { return os.Open(path) } -// Open file/directory with given and . -// -// 打开文件(带flag&perm) +// OpenFile opens file/directory with given and . func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) { return os.OpenFile(path, flag, perm) } -// Open file/directory with default perm and given . -// -// 打开文件(带flag) +// OpenWithFlag opens file/directory with default perm and given . func OpenWithFlag(path string, flag int) (*os.File, error) { f, err := os.OpenFile(path, flag, gDEFAULT_PERM) if err != nil { @@ -88,9 +78,7 @@ func OpenWithFlag(path string, flag int) (*os.File, error) { return f, nil } -// Open file/directory with given and . -// -// 打开文件(带flag&perm) +// OpenWithFlagPerm opens file/directory with given and . func OpenWithFlagPerm(path string, flag int, perm int) (*os.File, error) { f, err := os.OpenFile(path, flag, os.FileMode(perm)) if err != nil { @@ -99,9 +87,7 @@ func OpenWithFlagPerm(path string, flag int, perm int) (*os.File, error) { return f, nil } -// Check whether given path exist. -// -// 判断所给路径文件/文件夹是否存在 +// Exists checks whether given exist. func Exists(path string) bool { if _, err := os.Stat(path); !os.IsNotExist(err) { return true @@ -109,9 +95,7 @@ func Exists(path string) bool { return false } -// Check whether given path a directory. -// -// 判断所给路径是否为文件夹 +// IsDir checks whether given a directory. func IsDir(path string) bool { s, err := os.Stat(path) if err != nil { @@ -120,17 +104,13 @@ func IsDir(path string) bool { return s.IsDir() } -// Get current working directory absolute path. -// -// 获取当前工作目录(注意与SelfDir的区别). +// Pwd returns absolute path of current working directory. func Pwd() string { path, _ := os.Getwd() return path } -// Check whether given path a file(not a directory). -// -// 判断所给路径是否为文件 +// IsFile checks whether given a file, which means it's not a directory. func IsFile(path string) bool { s, err := os.Stat(path) if err != nil { @@ -139,39 +119,32 @@ func IsFile(path string) bool { return !s.IsDir() } +// Alias of Stat. // See Stat. -// -// Stat 方法的别名。 func Info(path string) (os.FileInfo, error) { return Stat(path) } // Stat returns a FileInfo describing the named file. // If there is an error, it will be of type *PathError. -// -// 获取文件或目录信息. func Stat(path string) (os.FileInfo, error) { return os.Stat(path) } -// Move renames (moves) src to dst path. -// -// 文件移动/重命名 +// Move renames (moves) to path. func Move(src string, dst string) error { return os.Rename(src, dst) } -// Rename renames (moves) src to dst path. -// -// 文件移动/重命名. +// Alias of Move. +// See Move. func Rename(src string, dst string) error { return Move(src, dst) } -// Copy file from src to dst. +// Copy file from to . // -// 文件复制. -// @TODO 支持目录复制. +// @TODO directory copy support. func Copy(src string, dst string) error { srcFile, err := Open(src) if err != nil { @@ -194,9 +167,7 @@ func Copy(src string, dst string) error { return nil } -// Get sub-file names of path. -// -// 返回目录下的文件名列表 +// DirNames returns sub-file names of given directory . func DirNames(path string) ([]string, error) { f, err := os.Open(path) if err != nil { @@ -218,8 +189,6 @@ func DirNames(path string) ([]string, error) { // Glob ignores file system errors such as I/O errors reading directories. // The only possible returned error is ErrBadPattern, when pattern // is malformed. -// -// 文件名正则匹配查找,第二个可选参数指定返回的列表是否仅为文件名(非绝对路径),默认返回绝对路径 func Glob(pattern string, onlyNames...bool) ([]string, error) { if list, err := filepath.Glob(pattern); err == nil { if len(onlyNames) > 0 && onlyNames[0] && len(list) > 0 { @@ -235,16 +204,13 @@ func Glob(pattern string, onlyNames...bool) ([]string, error) { } } -// Remove file/directory with parameter. -// -// 文件/目录删除 +// Remove deletes all file/directory with parameter. +// If parameter is directory, it deletes it recursively. func Remove(path string) error { return os.RemoveAll(path) } -// Check whether given is readable. -// -// 文件是否可读(支持文件/目录) +// IsReadable checks whether given is readable. func IsReadable(path string) bool { result := true file, err := os.OpenFile(path, os.O_RDONLY, gDEFAULT_PERM) @@ -255,14 +221,13 @@ func IsReadable(path string) bool { return result } -// Check whether given is writable. +// IsWritable checks whether given is writable. // -// 文件是否可写(支持文件/目录) -// @TODO 改进性能,利用 golang.org/x/sys 来实现跨平台的权限判断。 +// @TODO improve performance; use golang.org/x/sys to cross-plat-form func IsWritable(path string) bool { result := true if IsDir(path) { - // 如果是目录,那么创建一个临时文件进行写入测试 + // If it's a directory, create a temporary file to test whether it's writable. tmpFile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano()) if f, err := Create(tmpFile); err != nil || !Exists(tmpFile){ result = false @@ -282,16 +247,12 @@ func IsWritable(path string) bool { } // See os.Chmod. -// -// 修改文件/目录权限 func Chmod(path string, mode os.FileMode) error { return os.Chmod(path, mode) } -// Get all sub-files(absolute) of given , -// can be recursively with given parameter true. -// -// 打开目录,并返回其下一级文件列表(绝对路径),按照文件名称大小写进行排序,支持目录递归遍历。 +// ScanDir returns all sub-files with absolute paths of given , +// It scans directory recursively if given parameter is true. func ScanDir(path string, pattern string, recursive ... bool) ([]string, error) { list, err := doScanDir(path, pattern, recursive...) if err != nil { @@ -303,22 +264,24 @@ func ScanDir(path string, pattern string, recursive ... bool) ([]string, error) return list, nil } -// 内部检索目录方法,支持递归,返回没有排序的文件绝对路径列表结果。 -// pattern参数支持多个文件名称模式匹配,使用','符号分隔多个模式。 +// doScanDir is an internal method which scans directory +// and returns the absolute path list of files that are not sorted. +// +// The pattern parameter supports multiple file name patterns, +// using the ',' symbol to separate multiple patterns. +// +// It scans directory recursively if given parameter is true. func doScanDir(path string, pattern string, recursive ... bool) ([]string, error) { list := ([]string)(nil) - // 打开目录 file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() - // 读取目录下的文件列表 names, err := file.Readdirnames(-1) if err != nil { return nil, err } - // 是否递归遍历 for _, name := range names { path := fmt.Sprintf("%s%s%s", path, Separator, name) if IsDir(path) && len(recursive) > 0 && recursive[0] { @@ -327,7 +290,7 @@ func doScanDir(path string, pattern string, recursive ... bool) ([]string, error list = append(list, array...) } } - // 满足pattern才加入结果列表 + // If it meets pattern, then add it to the result list. for _, p := range strings.Split(pattern, ",") { if match, err := filepath.Match(strings.TrimSpace(p), name); err == nil && match { list = append(list, path) @@ -337,10 +300,9 @@ func doScanDir(path string, pattern string, recursive ... bool) ([]string, error return list, nil } -// See filepath.Abs. -// -// 将所给定的路径转换为绝对路径 -// 并判断文件路径是否存在,如果文件不存在,那么返回空字符串 +// RealPath converts the given to its absolute path +// and checks if the file path exists. +// If the file does not exist, return an empty string. func RealPath(path string) string { p, err := filepath.Abs(path) if err != nil { @@ -352,45 +314,46 @@ func RealPath(path string) string { return p } -// Get absolute file path of current running process(binary). -// -// 获取当前执行文件的绝对路径 +// SelfPath returns absolute file path of current running process(binary). func SelfPath() string { p, _ := filepath.Abs(os.Args[0]) return p } -// Get absolute directory path of current running process(binary). -// -// 获取当前执行文件的目录绝对路径 +// SelfDir returns absolute directory path of current running process(binary). func SelfDir() string { return filepath.Dir(SelfPath()) } -// See filepath.Base. -// -// 获取指定文件路径的文件名称 +// Basename returns the last element of path. +// Trailing path separators are removed before extracting the last element. +// If the path is empty, Base returns ".". +// If the path consists entirely of separators, Base returns a single separator. func Basename(path string) string { return filepath.Base(path) } -// See filepath.Dir. -// -// 获取指定文件路径的目录地址绝对路径. +// Dir returns all but the last element of path, typically the path's directory. +// After dropping the final element, Dir calls Clean on the path and trailing +// slashes are removed. +// If the path is empty, Dir returns ".". +// If the path consists entirely of separators, Dir returns a single separator. +// The returned path does not end in a separator unless it is the root directory. func Dir(path string) string { return filepath.Dir(path) } -// See filepath.Ext. +// Ext returns the file name extension used by path. +// The extension is the suffix beginning at the final dot +// in the final element of path; it is empty if there is +// no dot. // -// 获取指定文件路径的文件扩展名(包含"."号) +// Note: the result contains symbol '.'. func Ext(path string) string { return filepath.Ext(path) } -// Get absolute home directory path of current user. -// -// 获取用户主目录 +// Home returns absolute path of current user's home directory. func Home() (string, error) { u, err := user.Current() if nil == err { @@ -435,12 +398,15 @@ func homeWindows() (string, error) { return home, nil } -// Get absolute file path of main file, which contains the entrance function main. -// Available in develop environment. +// MainPkgPath returns absolute file path of package main, +// which contains the entrance function main. // -// 获取入口函数文件所在目录(main包文件目录), -// **仅对源码开发环境有效(即仅对生成该可执行文件的系统下有效)**。 -// 注意:该方法被第一次调用时,如果是在异步的goroutine中,该方法可能无法获取到main包路径。 +// It's only available in develop environment. +// +// Note1: Only valid for source development environments, +// IE only valid for systems that generate this executable. +// Note2: When the method is called for the first time, if it is in an asynchronous goroutine, +// the method may not get the main package path. func MainPkgPath() string { path := mainPkgPath.Val() if path != "" { @@ -473,14 +439,13 @@ func MainPkgPath() string { break } } - // 找不到,下次不用再检索了 + // If it fails finding the path, then mark it as "-", + // which means it will never do this search again. mainPkgPath.Set("-") return "" } // See os.TempDir(). -// -// 系统临时目录 func TempDir() string { return os.TempDir() } diff --git a/g/os/gfile/gfile_contents.go b/g/os/gfile/gfile_contents.go index 63f611686..e129e9850 100644 --- a/g/os/gfile/gfile_contents.go +++ b/g/os/gfile/gfile_contents.go @@ -13,18 +13,18 @@ import ( ) const ( - // 方法中涉及到读取的时候的缓冲大小 - gREAD_BUFFER = 1024 - // 方法中涉及到文件指针池的默认缓存时间(毫秒) - //gFILE_POOL_EXPIRE = 60000 + // Buffer size for reading file content. + gREAD_BUFFER = 1024 ) -// (文本)读取文件内容 +// GetContents returns the file content of as string. +// It returns en empty string if it fails reading. func GetContents(path string) string { return string(GetBinContents(path)) } -// (二进制)读取文件内容,如果文件不存在或者读取失败,返回nil。 +// GetBinContents returns the file content of as []byte. +// It returns nil if it fails reading. func GetBinContents(path string) []byte { data, err := ioutil.ReadFile(path) if err != nil { @@ -33,16 +33,16 @@ func GetBinContents(path string) []byte { return data } -// 写入文件内容 +// putContents puts binary content to file of . func putContents(path string, data []byte, flag int, perm int) error { - // 支持目录递归创建 + // It supports creating file of recursively. dir := Dir(path) if !Exists(dir) { if err := Mkdir(dir); err != nil { return err } } - // 创建/打开文件 + // Opening file with given and . f, err := OpenWithFlagPerm(path, flag, perm) if err != nil { return err @@ -56,32 +56,36 @@ func putContents(path string, data []byte, flag int, perm int) error { return nil } -// Truncate +// Truncate truncates file of to given size by . func Truncate(path string, size int) error { return os.Truncate(path, int64(size)) } -// (文本)写入文件内容 +// PutContents puts string to file of . +// It creates file of recursively if it does not exist. func PutContents(path string, content string) error { return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, gDEFAULT_PERM) } -// (文本)追加内容到文件末尾 +// PutContentsAppend appends string to file of . +// It creates file of recursively if it does not exist. func PutContentsAppend(path string, content string) error { return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_APPEND, gDEFAULT_PERM) } -// (二进制)写入文件内容 +// PutBinContents puts binary to file of . +// It creates file of recursively if it does not exist. func PutBinContents(path string, content []byte) error { return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, gDEFAULT_PERM) } -// (二进制)追加内容到文件末尾 +// PutBinContentsAppend appends binary to file of . +// It creates file of recursively if it does not exist. func PutBinContentsAppend(path string, content []byte) error { return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_APPEND, gDEFAULT_PERM) } -// 获得文件内容下一个指定字节的位置 +// GetNextCharOffset returns the file offset for given starting from . func GetNextCharOffset(reader io.ReaderAt, char byte, start int64) int64 { buffer := make([]byte, gREAD_BUFFER) offset := start @@ -100,7 +104,8 @@ func GetNextCharOffset(reader io.ReaderAt, char byte, start int64) int64 { return -1 } -// 获得文件内容下一个指定字节的位置 +// GetNextCharOffsetByPath returns the file offset for given starting from . +// It opens file of for reading with os.O_RDONLY flag and default perm. func GetNextCharOffsetByPath(path string, char byte, start int64) int64 { if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { defer f.Close() @@ -109,7 +114,10 @@ func GetNextCharOffsetByPath(path string, char byte, start int64) int64 { return -1 } -// 获得文件内容直到下一个指定字节的位置(返回值包含该位置字符内容) +// GetBinContentsTilChar returns the contents of the file as []byte +// until the next specified byte position. +// +// Note: Returned value contains the character of the last position. func GetBinContentsTilChar(reader io.ReaderAt, char byte, start int64) ([]byte, int64) { if offset := GetNextCharOffset(reader, char, start); offset != -1 { return GetBinContentsByTwoOffsets(reader, start, offset + 1), offset @@ -117,7 +125,11 @@ func GetBinContentsTilChar(reader io.ReaderAt, char byte, start int64) ([]byte, return nil, -1 } -// 获得文件内容直到下一个指定字节的位置(返回值包含该位置字符内容) +// GetBinContentsTilCharByPath returns the contents of the file given by as []byte +// until the next specified byte position. +// It opens file of for reading with os.O_RDONLY flag and default perm. +// +// Note: Returned value contains the character of the last position. func GetBinContentsTilCharByPath(path string, char byte, start int64) ([]byte, int64) { if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { defer f.Close() @@ -126,7 +138,9 @@ func GetBinContentsTilCharByPath(path string, char byte, start int64) ([]byte, i return nil, -1 } -// 获得文件内容中两个offset之间的内容 [start, end) +// GetBinContentsByTwoOffsets returns the binary content as []byte from to . +// Note: Returned value does not contain the character of the last position, which means +// it returns content range as [start, end). func GetBinContentsByTwoOffsets(reader io.ReaderAt, start int64, end int64) []byte { buffer := make([]byte, end - start) if _, err := reader.ReadAt(buffer, start); err != nil { @@ -135,7 +149,10 @@ func GetBinContentsByTwoOffsets(reader io.ReaderAt, start int64, end int64) []by return buffer } -// 获得文件内容中两个offset之间的内容 [start, end) +// GetBinContentsByTwoOffsetsByPath returns the binary content as []byte from to . +// Note: Returned value does not contain the character of the last position, which means +// it returns content range as [start, end). +// It opens file of for reading with os.O_RDONLY flag and default perm. func GetBinContentsByTwoOffsetsByPath(path string, start int64, end int64) []byte { if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { defer f.Close() diff --git a/g/os/gfile/gfile_search.go b/g/os/gfile/gfile_search.go index fbc6e10f0..acce3d0eb 100644 --- a/g/os/gfile/gfile_search.go +++ b/g/os/gfile/gfile_search.go @@ -13,31 +13,25 @@ import ( "github.com/gogf/gf/g/container/garray" ) - -// 如果给定绝对路径将会去掉其中的相对路径符号后返回; -// 如果是给定的相对路径,那么将会按照以下路径优先级搜索文件(重复路径会去重): -// prioritySearchPaths、当前工作目录、二进制文件目录、源码main包目录(开发环境下) +// Search searches file by name in following paths with priority: +// prioritySearchPaths, Pwd()、SelfDir()、MainPkgPath(). +// It returns the absolute file path of if found, or en empty string if not found. func Search(name string, prioritySearchPaths...string) (realPath string, err error) { - // 是否绝对路径 + // Check if it's a absolute path. realPath = RealPath(name) if realPath != "" { return } - // 相对路径搜索 + // Search paths array. array := garray.NewStringArray(true) - // 自定义优先路径 array.Append(prioritySearchPaths...) - // 用户工作目录 - array.Append(Pwd()) - // 二进制所在目录 - array.Append(SelfDir()) - // 源码main包目录 + array.Append(Pwd(), SelfDir()) if path := MainPkgPath(); path != "" { array.Append(path) } - // 路径去重 + // Remove repeated items. array.Unique() - // 执行相对路径搜索 + // Do the searching. array.RLockFunc(func(array []string) { path := "" for _, v := range array { @@ -48,7 +42,7 @@ func Search(name string, prioritySearchPaths...string) (realPath string, err err } } }) - // 目录不存在错误处理 + // If it fails searching, it returns formatted error. if realPath == "" { buffer := bytes.NewBuffer(nil) buffer.WriteString(fmt.Sprintf("cannot find file/folder \"%s\" in following paths:", name)) diff --git a/g/os/gfile/gfile_size.go b/g/os/gfile/gfile_size.go index 463b443ba..df37205b2 100644 --- a/g/os/gfile/gfile_size.go +++ b/g/os/gfile/gfile_size.go @@ -11,7 +11,7 @@ import ( "os" ) -// 文件大小(bytes) +// Size returns the size of file specified by in byte. func Size(path string) int64 { s, e := os.Stat(path) if e != nil { @@ -20,12 +20,12 @@ func Size(path string) int64 { return s.Size() } -// 格式化文件大小 +// ReadableSize formats size of file given by , for more human readable. func ReadableSize(path string) string { return FormatSize(float64(Size(path))) } -// 格式化文件大小 +// FormatSize formats size for more human readable. func FormatSize(raw float64) string { var t float64 = 1024 var d float64 = 1 diff --git a/g/os/gfile/gfile_time.go b/g/os/gfile/gfile_time.go index 9391c1827..a2e138fc6 100644 --- a/g/os/gfile/gfile_time.go +++ b/g/os/gfile/gfile_time.go @@ -10,7 +10,7 @@ import ( "os" ) -// 文件修改时间(时间戳,秒) +// MTime returns the modification time of file given by in second. func MTime(path string) int64 { s, e := os.Stat(path) if e != nil { @@ -19,7 +19,7 @@ func MTime(path string) int64 { return s.ModTime().Unix() } -// 文件修改时间(时间戳,毫秒) +// MTimeMillisecond returns the modification time of file given by in millisecond. func MTimeMillisecond(path string) int64 { s, e := os.Stat(path) if e != nil { diff --git a/g/os/gfile/gfile_contents_test.go b/g/os/gfile/gfile_z_contents_test.go similarity index 100% rename from g/os/gfile/gfile_contents_test.go rename to g/os/gfile/gfile_z_contents_test.go diff --git a/g/os/gfile/gfile_search_test.go b/g/os/gfile/gfile_z_search_test.go similarity index 100% rename from g/os/gfile/gfile_search_test.go rename to g/os/gfile/gfile_z_search_test.go diff --git a/g/os/gfile/gfile_size_test.go b/g/os/gfile/gfile_z_size_test.go similarity index 100% rename from g/os/gfile/gfile_size_test.go rename to g/os/gfile/gfile_z_size_test.go diff --git a/g/os/gfile/gfile_test.go b/g/os/gfile/gfile_z_test.go similarity index 100% rename from g/os/gfile/gfile_test.go rename to g/os/gfile/gfile_z_test.go diff --git a/g/os/gfile/gfile_time_test.go b/g/os/gfile/gfile_z_time_test.go similarity index 100% rename from g/os/gfile/gfile_time_test.go rename to g/os/gfile/gfile_z_time_test.go diff --git a/g/os/glog/glog.go b/g/os/glog/glog.go index bdd08f641..7dd62c664 100644 --- a/g/os/glog/glog.go +++ b/g/os/glog/glog.go @@ -10,9 +10,8 @@ package glog import ( - "github.com/gogf/gf/g/container/gtype" - "github.com/gogf/gf/g/internal/cmdenv" - "io" + "github.com/gogf/gf/g/internal/cmdenv" + "io" ) const ( @@ -28,9 +27,6 @@ const ( ) var ( - // Default level for log - defaultLevel = gtype.NewInt(LEVEL_ALL) - // Default logger object, for package method usage logger = New() ) @@ -44,6 +40,12 @@ func SetPath(path string) { logger.SetPath(path) } +// GetPath returns the logging directory path for file logging. +// It returns empty string if no directory path set. +func GetPath() string { + return logger.GetPath() +} + // SetFile sets the file name for file logging. // Datetime pattern can be used in , eg: access-{Ymd}.log. // The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log @@ -54,7 +56,11 @@ func SetFile(pattern string) { // SetLevel sets the default logging level. func SetLevel(level int) { logger.SetLevel(level) - defaultLevel.Set(level) +} + +// GetLevel returns the default logging level value. +func GetLevel() int { + return logger.GetLevel() } // SetWriter sets the customized logging for logging. @@ -71,26 +77,36 @@ func GetWriter() io.Writer { return logger.GetWriter() } -// GetLevel returns the default logging level value. -func GetLevel() int { - return defaultLevel.Val() -} - // SetDebug enables/disables the debug level for default logger. // The debug level is enbaled in default. func SetDebug(debug bool) { logger.SetDebug(debug) } -// SetStdPrint sets whether ouptput the logging contents to stdout, which is false in default. -func SetStdPrint(open bool) { - logger.SetStdPrint(open) +// SetStdoutPrint sets whether ouptput the logging contents to stdout, which is false in default. +func SetStdoutPrint(enabled bool) { + logger.SetStdoutPrint(enabled) } -// GetPath returns the logging directory path for file logging. -// It returns empty string if no directory path set. -func GetPath() string { - return logger.GetPath() +// SetHeaderPrint sets whether output header of the logging contents, which is true in default. +func SetHeaderPrint(enabled bool) { + logger.SetHeaderPrint(enabled) +} + +// SetPrefix sets prefix string for every logging content. +// Prefix is part of header, which means if header output is shut, no prefix will be output. +func SetPrefix(prefix string) { + logger.SetPrefix(prefix) +} + +// SetFlags sets extra flags for logging output features. +func SetFlags(flags int) { + logger.SetFlags(flags) +} + +// GetFlags returns the flags of logger. +func GetFlags() int { + return logger.GetFlags() } // PrintBacktrace prints the caller backtrace, @@ -109,166 +125,3 @@ func GetBacktrace(skip...int) string { func SetBacktrace(enabled bool) { logger.SetBacktrace(enabled) } - -// To is a chaining function, -// which redirects current logging content output to the sepecified . -func To(writer io.Writer) *Logger { - return logger.To(writer) -} - -// Path is a chaining function, -// which sets the directory path to for current logging content output. -func Path(path string) *Logger { - return logger.Path(path) -} - -// Cat is a chaining function, -// which sets the category to for current logging content output. -func Cat(category string) *Logger { - return logger.Cat(category) -} - -// File is a chaining function, -// which sets file name for the current logging content output. -func File(pattern string) *Logger { - return logger.File(pattern) -} - -// Level is a chaining function, -// which sets logging level for the current logging content output. -func Level(level int) *Logger { - return logger.Level(level) -} - -// Backtrace is a chaining function, -// which sets backtrace options for the current logging content output . -func Backtrace(enabled bool, skip...int) *Logger { - return logger.Backtrace(enabled, skip...) -} - -// StdPrint is a chaining function, -// which enables/disables stdout for the current logging content output. -func StdPrint(enabled bool) *Logger { - return logger.StdPrint(enabled) -} - -// Header is a chaining function, -// which enables/disables log header for the current logging content output. -func Header(enabled bool) *Logger { - return logger.Header(enabled) -} - -func Print(v ...interface{}) { - logger.Print(v ...) -} - -func Printf(format string, v ...interface{}) { - logger.Printf(format, v ...) -} - -func Println(v ...interface{}) { - logger.Println(v ...) -} - -func Printfln(format string, v ...interface{}) { - logger.Printfln(format, v ...) -} - -// Fatal prints the logging content with [FATA] header and newline, then exit the current process. -func Fatal(v ...interface{}) { - logger.Fatal(v ...) -} - -// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. -func Fatalf(format string, v ...interface{}) { - logger.Fatalf(format, v ...) -} - -// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. -func Fatalfln(format string, v ...interface{}) { - logger.Fatalfln(format, v ...) -} - -func Panic(v ...interface{}) { - logger.Panic(v ...) -} - -func Panicf(format string, v ...interface{}) { - logger.Panicf(format, v ...) -} - -func Panicfln(format string, v ...interface{}) { - logger.Panicfln(format, v ...) -} - -func Info(v ...interface{}) { - logger.Info(v...) -} - -func Debug(v ...interface{}) { - logger.Debug(v...) -} - -func Notice(v ...interface{}) { - logger.Notice(v...) -} - -func Warning(v ...interface{}) { - logger.Warning(v...) -} - -func Error(v ...interface{}) { - logger.Error(v...) -} - -func Critical(v ...interface{}) { - logger.Critical(v...) -} - -func Infof(format string, v ...interface{}) { - logger.Infof(format, v...) -} - -func Debugf(format string, v ...interface{}) { - logger.Debugf(format, v...) -} - -func Noticef(format string, v ...interface{}) { - logger.Noticef(format, v...) -} - -func Warningf(format string, v ...interface{}) { - logger.Warningf(format, v...) -} - -func Errorf(format string, v ...interface{}) { - logger.Errorf(format, v...) -} - -func Criticalf(format string, v ...interface{}) { - logger.Criticalf(format, v...) -} - -func Infofln(format string, v ...interface{}) { - logger.Infofln(format, v...) -} - -func Debugfln(format string, v ...interface{}) { - logger.Debugfln(format, v...) -} - -func Noticefln(format string, v ...interface{}) { - logger.Noticefln(format, v...) -} - -func Warningfln(format string, v ...interface{}) { - logger.Warningfln(format, v...) -} - -func Errorfln(format string, v ...interface{}) { - logger.Errorfln(format, v...) -} - -func Criticalfln(format string, v ...interface{}) { - logger.Criticalfln(format, v...) -} diff --git a/g/os/glog/glog_api.go b/g/os/glog/glog_api.go new file mode 100644 index 000000000..1381b7e63 --- /dev/null +++ b/g/os/glog/glog_api.go @@ -0,0 +1,162 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package glog + +// Print prints with newline using fmt.Sprintln. +// The param can be multiple variables. +func Print(v ...interface{}) { + logger.Print(v ...) +} + +// Printf prints with format using fmt.Sprintf. +// The param can be multiple variables. +func Printf(format string, v ...interface{}) { + logger.Printf(format, v ...) +} + +// See Print. +func Println(v ...interface{}) { + logger.Println(v ...) +} + +// Printf prints with newline and format using fmt.Sprintf. +// The param can be multiple variables. +func Printfln(format string, v ...interface{}) { + logger.Printfln(format, v ...) +} + +// Fatal prints the logging content with [FATA] header and newline, then exit the current process. +func Fatal(v ...interface{}) { + logger.Fatal(v ...) +} + +// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. +func Fatalf(format string, v ...interface{}) { + logger.Fatalf(format, v ...) +} + +// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. +func Fatalfln(format string, v ...interface{}) { + logger.Fatalfln(format, v ...) +} + +// Panic prints the logging content with [PANI] header and newline, then panics. +func Panic(v ...interface{}) { + logger.Panic(v ...) +} + +// Panicf prints the logging content with [PANI] header and custom format, then panics. +func Panicf(format string, v ...interface{}) { + logger.Panicf(format, v ...) +} + +// Panicfln prints the logging content with [PANI] header, newline and custom format, then panics. +func Panicfln(format string, v ...interface{}) { + logger.Panicfln(format, v ...) +} + +// Info prints the logging content with [INFO] header and newline. +func Info(v ...interface{}) { + logger.Info(v...) +} + +// Infof prints the logging content with [INFO] header and custom format. +func Infof(format string, v ...interface{}) { + logger.Infof(format, v...) +} + +// Infofln prints the logging content with [INFO] header, newline and custom format. +func Infofln(format string, v ...interface{}) { + logger.Infofln(format, v...) +} + +// Debug prints the logging content with [DEBU] header and newline. +func Debug(v ...interface{}) { + logger.Debug(v...) +} + +// Debugf prints the logging content with [DEBU] header and custom format. +func Debugf(format string, v ...interface{}) { + logger.Debugf(format, v...) +} + +// Debugfln prints the logging content with [DEBU] header, newline and custom format. +func Debugfln(format string, v ...interface{}) { + logger.Debugfln(format, v...) +} + +// Notice prints the logging content with [NOTI] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func Notice(v ...interface{}) { + logger.Notice(v...) +} + +// Noticef prints the logging content with [NOTI] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Noticef(format string, v ...interface{}) { + logger.Noticef(format, v...) +} + +// Noticefln prints the logging content with [NOTI] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Noticefln(format string, v ...interface{}) { + logger.Noticefln(format, v...) +} + +// Warning prints the logging content with [WARN] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func Warning(v ...interface{}) { + logger.Warning(v...) +} + +// Warningf prints the logging content with [WARN] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Warningf(format string, v ...interface{}) { + logger.Warningf(format, v...) +} + +// Warningfln prints the logging content with [WARN] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Warningfln(format string, v ...interface{}) { + logger.Warningfln(format, v...) +} + +// Error prints the logging content with [ERRO] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func Error(v ...interface{}) { + logger.Error(v...) +} + +// Errorf prints the logging content with [ERRO] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Errorf(format string, v ...interface{}) { + logger.Errorf(format, v...) +} + +// Errorfln prints the logging content with [ERRO] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Errorfln(format string, v ...interface{}) { + logger.Errorfln(format, v...) +} + +// Critical prints the logging content with [CRIT] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func Critical(v ...interface{}) { + logger.Critical(v...) +} + +// Criticalf prints the logging content with [CRIT] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Criticalf(format string, v ...interface{}) { + logger.Criticalf(format, v...) +} + +// Criticalfln prints the logging content with [CRIT] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func Criticalfln(format string, v ...interface{}) { + logger.Criticalfln(format, v...) +} diff --git a/g/os/glog/glog_chaining.go b/g/os/glog/glog_chaining.go new file mode 100644 index 000000000..537210a17 --- /dev/null +++ b/g/os/glog/glog_chaining.go @@ -0,0 +1,75 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package glog + +import ( + "io" +) + +// To is a chaining function, +// which redirects current logging content output to the sepecified . +func To(writer io.Writer) *Logger { + return logger.To(writer) +} + +// Path is a chaining function, +// which sets the directory path to for current logging content output. +func Path(path string) *Logger { + return logger.Path(path) +} + +// Cat is a chaining function, +// which sets the category to for current logging content output. +func Cat(category string) *Logger { + return logger.Cat(category) +} + +// File is a chaining function, +// which sets file name for the current logging content output. +func File(pattern string) *Logger { + return logger.File(pattern) +} + +// Level is a chaining function, +// which sets logging level for the current logging content output. +func Level(level int) *Logger { + return logger.Level(level) +} + +// Skip is a chaining function, +// which sets backtrace skip for the current logging content output. +// It also affects the caller file path checks when line number printing enabled. +func Skip(skip int) *Logger { + return logger.Skip(skip) +} + +// Backtrace is a chaining function, +// which sets backtrace options for the current logging content output . +func Backtrace(enabled bool, skip...int) *Logger { + return logger.Backtrace(enabled, skip...) +} + +// StdPrint is a chaining function, +// which enables/disables stdout for the current logging content output. +// It's enabled in default. +func Stdout(enabled...bool) *Logger { + return logger.Stdout(enabled...) +} + +// Header is a chaining function, +// which enables/disables log header for the current logging content output. +// It's enabled in default. +func Header(enabled...bool) *Logger { + return logger.Header(enabled...) +} + +// Line is a chaining function, +// which enables/disables printing its caller file along with its line number. +// The param specified whether print the long absolute file path, eg: /a/b/c/d.go:23. +func Line(long...bool) *Logger { + return logger.Line(long...) +} diff --git a/g/os/glog/glog_logger.go b/g/os/glog/glog_logger.go index c75333071..4332eb4af 100644 --- a/g/os/glog/glog_logger.go +++ b/g/os/glog/glog_logger.go @@ -9,9 +9,9 @@ package glog import ( + "bytes" "errors" "fmt" - "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/os/gfpool" "github.com/gogf/gf/g/os/gtime" @@ -20,21 +20,21 @@ import ( "os" "runtime" "strings" - "sync" "time" ) type Logger struct { - mu sync.RWMutex - pr *Logger // Parent logger. - writer io.Writer // Customized io.Writer. - path *gtype.String // Logging directory path. - file *gtype.String // Format for logging file. - level *gtype.Int // Output level. - btSkip *gtype.Int // Skip count for backtrace. - btStatus *gtype.Int // Backtrace status(1: enabled - default; 0: disabled) - printHeader *gtype.Bool // Print header or not(true in default). - alsoStdPrint *gtype.Bool // Output to stdout or not(true in default). + parent *Logger // Parent logger. + writer io.Writer // Customized io.Writer. + flags int // Extra flags for logging output features. + path string // Logging directory path. + file string // Format for logging file. + level int // Output level. + prefix string // Prefix string for every logging content. + btSkip int // Skip count for backtrace. + btStatus int // Backtrace status(1: enabled - default; 0: disabled) + headerPrint bool // Print header or not(true in default). + stdoutPrint bool // Output to stdout or not(true in default). } const ( @@ -44,11 +44,18 @@ const ( gDEFAULT_FPOOL_EXPIRE = 60000 ) +const ( + F_FILE_LONG = 1 << iota // Print full file name and line number: /a/b/c/d.go:23. + F_FILE_SHORT // Print final file name element and line number: d.go:23. overrides F_FILE_LONG. + F_TIME_DATE // Print the date in the local time zone: 2009-01-23. + F_TIME_TIME // Print the time in the local time zone: 01:23:23. + F_TIME_MILLI // Print the time with milliseconds in the local time zone: 01:23:23.675. + F_TIME_STD = F_TIME_DATE | F_TIME_MILLI +) + var ( // Default line break. - ln = "\n" - // Mutex to ensure log output sequence. - stdMu = sync.RWMutex{} + ln = "\n" ) func init() { @@ -61,16 +68,12 @@ func init() { // New creates and returns a custom logger. func New() *Logger { logger := &Logger { - path : gtype.NewString(), - file : gtype.NewString(gDEFAULT_FILE_FORMAT), - level : gtype.NewInt(defaultLevel.Val()), - btSkip : gtype.NewInt(), - btStatus : gtype.NewInt(1), - printHeader : gtype.NewBool(true), - alsoStdPrint : gtype.NewBool(true), - } - logger.writer = &Writer { - logger : logger, + file : gDEFAULT_FILE_FORMAT, + flags : F_TIME_STD, + level : LEVEL_ALL, + btStatus : 1, + headerPrint : true, + stdoutPrint : true, } return logger } @@ -78,53 +81,62 @@ func New() *Logger { // Clone returns a new logger, which is the clone the current logger. func (l *Logger) Clone() *Logger { logger := &Logger { - pr : l, - path : l.path.Clone(), - file : l.file.Clone(), - level : l.level.Clone(), - btSkip : l.btSkip.Clone(), - btStatus : l.btStatus.Clone(), - printHeader : l.printHeader.Clone(), - alsoStdPrint : l.alsoStdPrint.Clone(), + parent : l, + path : l.path, + file : l.file, + level : l.level, + flags : l.flags, + prefix : l.prefix, + btSkip : l.btSkip, + btStatus : l.btStatus, + headerPrint : l.headerPrint, + stdoutPrint : l.stdoutPrint, } - logger.writer = &Writer { - logger : logger, - } return logger } // SetLevel sets the logging level. func (l *Logger) SetLevel(level int) { - l.level.Set(level) + l.level = level } // GetLevel returns the logging level value. func (l *Logger) GetLevel() int { - return l.level.Val() + return l.level } // SetDebug enables/disables the debug level for logger. // The debug level is enabled in default. func (l *Logger) SetDebug(debug bool) { if debug { - l.level.Set(l.level.Val() | LEVEL_DEBU) + l.level = l.level | LEVEL_DEBU } else { - l.level.Set(l.level.Val() & ^LEVEL_DEBU) + l.level = l.level & ^LEVEL_DEBU } } +// SetFlags sets extra flags for logging output features. +func (l *Logger) SetFlags(flags int) { + l.flags = flags +} + +// GetFlags returns the flags of logger. +func (l *Logger) GetFlags() int { + return l.flags +} + // SetBacktrace enables/disables the backtrace feature in failure logging outputs. func (l *Logger) SetBacktrace(enabled bool) { if enabled { - l.btStatus.Set(1) + l.btStatus = 1 } else { - l.btStatus.Set(0) + l.btStatus = 0 } } // SetBacktraceSkip sets the backtrace offset from the end point. func (l *Logger) SetBacktraceSkip(skip int) { - l.btSkip.Set(skip) + l.btSkip = skip } // SetWriter sets the customized logging for logging. @@ -132,26 +144,21 @@ func (l *Logger) SetBacktraceSkip(skip int) { // Developer can use customized logging to redirect logging output to another service, // eg: kafka, mysql, mongodb, etc. func (l *Logger) SetWriter(writer io.Writer) { - l.mu.Lock() l.writer = writer - l.mu.Unlock() } // GetWriter returns the customized writer object, which implements the io.Writer interface. -// It returns a default writer if no customized writer set. +// It returns nil if no writer previously set. func (l *Logger) GetWriter() io.Writer { - l.mu.RLock() - r := l.writer - l.mu.RUnlock() - return r + return l.writer } // getFilePointer returns the file pinter for file logging. // It returns nil if file logging is disabled, or file opening fails. func (l *Logger) getFilePointer() *gfpool.File { - if path := l.path.Val(); path != "" { + if path := l.path; path != "" { // Content containing "{}" in the file name is formatted using gtime - file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file.Val(), func(s string) string { + file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file, func(s string) string { return gtime.Now().Format(strings.Trim(s, "{}")) }) // Create path if it does not exist。 @@ -161,8 +168,11 @@ func (l *Logger) getFilePointer() *gfpool.File { return nil } } - fpath := path + gfile.Separator + file - if fp, err := gfpool.Open(fpath, gDEFAULT_FILE_POOL_FLAGS, gDEFAULT_FPOOL_PERM, gDEFAULT_FPOOL_EXPIRE); err == nil { + if fp, err := gfpool.Open( + path + gfile.Separator + file, + gDEFAULT_FILE_POOL_FLAGS, + gDEFAULT_FPOOL_PERM, + gDEFAULT_FPOOL_EXPIRE); err == nil { return fp } else { fmt.Fprintln(os.Stderr, err) @@ -182,69 +192,73 @@ func (l *Logger) SetPath(path string) error { return err } } - l.path.Set(strings.TrimRight(path, gfile.Separator)) + l.path = strings.TrimRight(path, gfile.Separator) return nil } // GetPath returns the logging directory path for file logging. // It returns empty string if no directory path set. func (l *Logger) GetPath() string { - return l.path.Val() + return l.path } // SetFile sets the file name for file logging. // Datetime pattern can be used in , eg: access-{Ymd}.log. // The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log func (l *Logger) SetFile(pattern string) { - l.file.Set(pattern) + l.file = pattern } -// SetStdPrint sets whether output the logging contents to stdout, which is false in default. -func (l *Logger) SetStdPrint(enabled bool) { - l.alsoStdPrint.Set(enabled) +// SetStdoutPrint sets whether output the logging contents to stdout, which is true in default. +func (l *Logger) SetStdoutPrint(enabled bool) { + l.stdoutPrint = enabled +} + +// SetHeaderPrint sets whether output header of the logging contents, which is true in default. +func (l *Logger) SetHeaderPrint(enabled bool) { + l.headerPrint = enabled +} + +// SetPrefix sets prefix string for every logging content. +// Prefix is part of header, which means if header output is shut, no prefix will be output. +func (l *Logger) SetPrefix(prefix string) { + l.prefix = prefix } // print prints to defined writer, logging file or passed . -// It internally uses memory lock for file logging to ensure logging sequence. func (l *Logger) print(std io.Writer, s string) { - // Customized writer has the most high priority. - if l.printHeader.Val() { + // Custom writer has the most high priority. + if l.headerPrint { s = l.format(s) } - writer := l.GetWriter() - if _, ok := writer.(*Writer); ok { + if l.writer == nil { if f := l.getFilePointer(); f != nil { defer f.Close() if _, err := io.WriteString(f, s); err != nil { fmt.Fprintln(os.Stderr, err.Error()) } } - // Also output to stdout? - if l.alsoStdPrint.Val() { - l.doStdLockPrint(std, s) + // Allow output to stdout? + if l.stdoutPrint { + if _, err := std.Write([]byte(s)); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } } } else { - l.doStdLockPrint(writer, s) + if _, err := l.writer.Write([]byte(s)); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } } } -// doStdLockPrint prints to concurrent-safely. -func (l *Logger) doStdLockPrint(std io.Writer, s string) { - stdMu.Lock() - if _, err := std.Write([]byte(s)); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } - stdMu.Unlock() -} - -// stdPrint prints content without backtrace. -func (l *Logger) stdPrint(s string) { +// printStd prints content without backtrace. +func (l *Logger) printStd(s string) { l.print(os.Stdout, s) } -// stdPrint prints content with backtrace check. -func (l *Logger) errPrint(s string) { - if l.btStatus.Val() == 1 { +// printStd prints content with backtrace check. +func (l *Logger) printErr(s string) { + if l.btStatus == 1 { s = l.appendBacktrace(s) } // In matter of sequence, do not use stderr here, but use the same stdout. @@ -283,10 +297,9 @@ func (l *Logger) GetBacktrace(skip...int) string { customSkip = skip[0] } backtrace := "" - index := 1 from := 0 - // 首先定位业务文件开始位置 - for i := 0; i < 10; i++ { + // Find the caller position exclusive of the glog file. + for i := 0; i < 100; i++ { if _, file, _, ok := runtime.Caller(i); ok { if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { from = i @@ -294,11 +307,11 @@ func (l *Logger) GetBacktrace(skip...int) string { } } } - // 从业务文件开始位置根据自定义的skip开始backtrace + // Find the true caller file path using custom skip. + index := 1 goRoot := runtime.GOROOT() - for i := from + customSkip + l.btSkip.Val(); i < 10000; i++ { + for i := from + customSkip + l.btSkip; i < 10000; i++ { if _, file, cline, ok := runtime.Caller(i); ok && file != "" { - // 不打印出go源码路径及glog包文件路径,日志打印必须从业务源码文件开始,且从glog包文件开始检索 if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, ln) index++ @@ -310,171 +323,68 @@ func (l *Logger) GetBacktrace(skip...int) string { return backtrace } -func (l *Logger) format(s string) string { - return time.Now().Format("2006-01-02 15:04:05.000 ") + s +// getLongFile returns the absolute file path along with its line number of the caller. +func (l *Logger) getLongFile() string { + from := 0 + // Find the caller position exclusive of the glog file. + for i := 0; i < 100; i++ { + if _, file, line, ok := runtime.Caller(i); ok { + if !gregex.IsMatchString("/g/os/glog/glog.+$", file) { + from = i + break + return fmt.Sprintf(`%s:%d`, file, line) + } + } + } + // Find the true caller file path using custom skip. + goRoot := runtime.GOROOT() + for i := from + l.btSkip; i < 10000; i++ { + if _, file, line, ok := runtime.Caller(i); ok && file != "" { + if (goRoot == "" || !gregex.IsMatchString("^" + goRoot, file)) && !gregex.IsMatchString(``, file) { + return fmt.Sprintf(`%s:%d`, file, line) + } + } else { + break + } + } + return "" } -func (l *Logger) Print(v ...interface{}) { - l.stdPrint(fmt.Sprintln(v...)) + +// format formats the content according the flags. +func (l *Logger) format(content string) string { + buffer := bytes.NewBuffer(nil) + // Time. + timeFormat := "" + if l.flags & F_TIME_DATE > 0 { + timeFormat += "2006-01-02 " + } + if l.flags & F_TIME_TIME > 0 { + timeFormat += "15:04:05 " + } + if l.flags & F_TIME_MILLI > 0 { + timeFormat += "15:04:05.000 " + } + if len(timeFormat) > 0 { + buffer.WriteString(time.Now().Format(timeFormat)) + } + // Caller path. + callerPath := "" + if l.flags & F_FILE_LONG > 0 { + callerPath = l.getLongFile() + ": " + } + if l.flags & F_FILE_SHORT > 0 { + callerPath = gfile.Basename(l.getLongFile()) + ": " + } + if len(callerPath) > 0 { + buffer.WriteString(callerPath) + } + // Prefix. + if len(l.prefix) > 0 { + buffer.WriteString(l.prefix + " ") + } + // Content. + buffer.WriteString(content) + return buffer.String() } -func (l *Logger) Printf(format string, v ...interface{}) { - l.stdPrint(fmt.Sprintf(format, v...)) -} - -func (l *Logger) Println(v ...interface{}) { - l.stdPrint(fmt.Sprintln(v...)) -} - -func (l *Logger) Printfln(format string, v ...interface{}) { - l.stdPrint(fmt.Sprintf(format + ln, v...)) -} - -// Fatal prints the logging content with [FATA] header and newline, then exit the current process. -func (l *Logger) Fatal(v ...interface{}) { - l.errPrint("[FATA] " + fmt.Sprintln(v...)) - os.Exit(1) -} - -// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. -func (l *Logger) Fatalf(format string, v ...interface{}) { - l.errPrint("[FATA] " + fmt.Sprintf(format, v...)) - os.Exit(1) -} - -// Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. -func (l *Logger) Fatalfln(format string, v ...interface{}) { - l.errPrint("[FATA] " + fmt.Sprintf(format + ln, v...)) - os.Exit(1) -} - -func (l *Logger) Panic(v ...interface{}) { - s := fmt.Sprintln(v...) - l.errPrint("[PANI] " + s) - panic(s) -} - -func (l *Logger) Panicf(format string, v ...interface{}) { - s := fmt.Sprintf(format, v...) - l.errPrint("[PANI] " + s) - panic(s) -} - -func (l *Logger) Panicfln(format string, v ...interface{}) { - s := fmt.Sprintf(format + ln, v...) - l.errPrint("[PANI] " + s) - panic(s) -} - -func (l *Logger) Info(v ...interface{}) { - if l.checkLevel(LEVEL_INFO) { - l.stdPrint("[INFO] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Infof(format string, v ...interface{}) { - if l.checkLevel(LEVEL_INFO) { - l.stdPrint("[INFO] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Infofln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_INFO) { - l.stdPrint("[INFO] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Debug(v ...interface{}) { - if l.checkLevel(LEVEL_DEBU) { - l.stdPrint("[DEBU] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Debugf(format string, v ...interface{}) { - if l.checkLevel(LEVEL_DEBU) { - l.stdPrint("[DEBU] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Debugfln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_DEBU) { - l.stdPrint("[DEBU] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Notice(v ...interface{}) { - if l.checkLevel(LEVEL_NOTI) { - l.errPrint("[NOTI] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Noticef(format string, v ...interface{}) { - if l.checkLevel(LEVEL_NOTI) { - l.errPrint("[NOTI] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Noticefln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_NOTI) { - l.errPrint("[NOTI] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Warning(v ...interface{}) { - if l.checkLevel(LEVEL_WARN) { - l.errPrint("[WARN] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Warningf(format string, v ...interface{}) { - if l.checkLevel(LEVEL_WARN) { - l.errPrint("[WARN] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Warningfln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_WARN) { - l.errPrint("[WARN] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Error(v ...interface{}) { - if l.checkLevel(LEVEL_ERRO) { - l.errPrint("[ERRO] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Errorf(format string, v ...interface{}) { - if l.checkLevel(LEVEL_ERRO) { - l.errPrint("[ERRO] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Errorfln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_ERRO) { - l.errPrint("[ERRO] " + fmt.Sprintf(format, v...) + ln) - } -} - -func (l *Logger) Critical(v ...interface{}) { - if l.checkLevel(LEVEL_CRIT) { - l.errPrint("[CRIT] " + fmt.Sprintln(v...)) - } -} - -func (l *Logger) Criticalf(format string, v ...interface{}) { - if l.checkLevel(LEVEL_CRIT) { - l.errPrint("[CRIT] " + fmt.Sprintf(format, v...)) - } -} - -func (l *Logger) Criticalfln(format string, v ...interface{}) { - if l.checkLevel(LEVEL_CRIT) { - l.errPrint("[CRIT] " + fmt.Sprintf(format, v...) + ln) - } -} - -// checkLevel checks whether the given could be output. -func (l *Logger) checkLevel(level int) bool { - return l.level.Val() & level > 0 -} \ No newline at end of file diff --git a/g/os/glog/glog_logger_api.go b/g/os/glog/glog_logger_api.go new file mode 100644 index 000000000..08327bc78 --- /dev/null +++ b/g/os/glog/glog_logger_api.go @@ -0,0 +1,217 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package glog + +import ( + "fmt" + "os" +) + +// Print prints with newline using fmt.Sprintln. +// The param can be multiple variables. +func (l *Logger) Print(v ...interface{}) { + l.printStd(fmt.Sprintln(v...)) +} + +// Printf prints with format using fmt.Sprintf. +// The param can be multiple variables. +func (l *Logger) Printf(format string, v ...interface{}) { + l.printStd(fmt.Sprintf(format, v...)) +} + +// See Print. +func (l *Logger) Println(v ...interface{}) { + l.Print(v...) +} + +// Printfln prints with newline and format using fmt.Sprintf. +// The param can be multiple variables. +func (l *Logger) Printfln(format string, v ...interface{}) { + l.printStd(fmt.Sprintf(format + ln, v...)) +} + +// Fatal prints the logging content with [FATA] header and newline, then exit the current process. +func (l *Logger) Fatal(v ...interface{}) { + l.printErr("[FATA] " + fmt.Sprintln(v...)) + os.Exit(1) +} + +// Fatalf prints the logging content with [FATA] header and custom format, then exit the current process. +func (l *Logger) Fatalf(format string, v ...interface{}) { + l.printErr("[FATA] " + fmt.Sprintf(format, v...)) + os.Exit(1) +} + +// Fatalfln prints the logging content with [FATA] header, custom format and newline, then exit the current process. +func (l *Logger) Fatalfln(format string, v ...interface{}) { + l.printErr("[FATA] " + fmt.Sprintf(format + ln, v...)) + os.Exit(1) +} + +// Panic prints the logging content with [PANI] header and newline, then panics. +func (l *Logger) Panic(v ...interface{}) { + s := fmt.Sprintln(v...) + l.printErr("[PANI] " + s) + panic(s) +} + +// Panicf prints the logging content with [PANI] header and custom format, then panics. +func (l *Logger) Panicf(format string, v ...interface{}) { + s := fmt.Sprintf(format, v...) + l.printErr("[PANI] " + s) + panic(s) +} + +// Panicfln prints the logging content with [PANI] header, newline and custom format, then panics. +func (l *Logger) Panicfln(format string, v ...interface{}) { + s := fmt.Sprintf(format + ln, v...) + l.printErr("[PANI] " + s) + panic(s) +} + +// Info prints the logging content with [INFO] header and newline. +func (l *Logger) Info(v ...interface{}) { + if l.checkLevel(LEVEL_INFO) { + l.printStd("[INFO] " + fmt.Sprintln(v...)) + } +} + +// Infof prints the logging content with [INFO] header and custom format. +func (l *Logger) Infof(format string, v ...interface{}) { + if l.checkLevel(LEVEL_INFO) { + l.printStd("[INFO] " + fmt.Sprintf(format, v...)) + } +} + +// Infofln prints the logging content with [INFO] header, newline and custom format. +func (l *Logger) Infofln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_INFO) { + l.printStd("[INFO] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Debug prints the logging content with [DEBU] header and newline. +func (l *Logger) Debug(v ...interface{}) { + if l.checkLevel(LEVEL_DEBU) { + l.printStd("[DEBU] " + fmt.Sprintln(v...)) + } +} + +// Debugf prints the logging content with [DEBU] header and custom format. +func (l *Logger) Debugf(format string, v ...interface{}) { + if l.checkLevel(LEVEL_DEBU) { + l.printStd("[DEBU] " + fmt.Sprintf(format, v...)) + } +} + +// Debugfln prints the logging content with [DEBU] header, newline and custom format. +func (l *Logger) Debugfln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_DEBU) { + l.printStd("[DEBU] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Notice prints the logging content with [NOTI] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Notice(v ...interface{}) { + if l.checkLevel(LEVEL_NOTI) { + l.printErr("[NOTI] " + fmt.Sprintln(v...)) + } +} + +// Noticef prints the logging content with [NOTI] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Noticef(format string, v ...interface{}) { + if l.checkLevel(LEVEL_NOTI) { + l.printErr("[NOTI] " + fmt.Sprintf(format, v...)) + } +} + +// Noticefln prints the logging content with [NOTI] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Noticefln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_NOTI) { + l.printErr("[NOTI] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Warning prints the logging content with [WARN] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Warning(v ...interface{}) { + if l.checkLevel(LEVEL_WARN) { + l.printErr("[WARN] " + fmt.Sprintln(v...)) + } +} + +// Warningf prints the logging content with [WARN] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Warningf(format string, v ...interface{}) { + if l.checkLevel(LEVEL_WARN) { + l.printErr("[WARN] " + fmt.Sprintf(format, v...)) + } +} + +// Warningfln prints the logging content with [WARN] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Warningfln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_WARN) { + l.printErr("[WARN] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Error prints the logging content with [ERRO] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Error(v ...interface{}) { + if l.checkLevel(LEVEL_ERRO) { + l.printErr("[ERRO] " + fmt.Sprintln(v...)) + } +} + +// Errorf prints the logging content with [ERRO] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Errorf(format string, v ...interface{}) { + if l.checkLevel(LEVEL_ERRO) { + l.printErr("[ERRO] " + fmt.Sprintf(format, v...)) + } +} + +// Errorfln prints the logging content with [ERRO] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Errorfln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_ERRO) { + l.printErr("[ERRO] " + fmt.Sprintf(format, v...) + ln) + } +} + +// Critical prints the logging content with [CRIT] header and newline. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Critical(v ...interface{}) { + if l.checkLevel(LEVEL_CRIT) { + l.printErr("[CRIT] " + fmt.Sprintln(v...)) + } +} + +// Criticalf prints the logging content with [CRIT] header and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Criticalf(format string, v ...interface{}) { + if l.checkLevel(LEVEL_CRIT) { + l.printErr("[CRIT] " + fmt.Sprintf(format, v...)) + } +} + +// Criticalfln prints the logging content with [CRIT] header, newline and custom format. +// It also prints caller backtrace info if backtrace feature is enabled. +func (l *Logger) Criticalfln(format string, v ...interface{}) { + if l.checkLevel(LEVEL_CRIT) { + l.printErr("[CRIT] " + fmt.Sprintf(format, v...) + ln) + } +} + +// checkLevel checks whether the given could be output. +func (l *Logger) checkLevel(level int) bool { + return l.level & level > 0 +} \ No newline at end of file diff --git a/g/os/glog/glog_logger_chaining.go b/g/os/glog/glog_logger_chaining.go index b478a6c76..fbef51b04 100644 --- a/g/os/glog/glog_logger_chaining.go +++ b/g/os/glog/glog_logger_chaining.go @@ -15,7 +15,7 @@ import ( // which redirects current logging content output to the specified . func (l *Logger) To(writer io.Writer) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -28,7 +28,7 @@ func (l *Logger) To(writer io.Writer) *Logger { // which sets the directory path to for current logging content output. func (l *Logger) Path(path string) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -44,14 +44,13 @@ func (l *Logger) Path(path string) *Logger { // Param can be hierarchical, eg: module/user. func (l *Logger) Cat(category string) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l } - path := l.path.Val() - if path != "" { - logger.SetPath(path + gfile.Separator + category) + if logger.path != "" { + logger.SetPath(logger.path + gfile.Separator + category) } return logger } @@ -60,7 +59,7 @@ func (l *Logger) Cat(category string) *Logger { // which sets file name for the current logging content output. func (l *Logger) File(file string) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -73,7 +72,7 @@ func (l *Logger) File(file string) *Logger { // which sets logging level for the current logging content output. func (l *Logger) Level(level int) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -82,11 +81,25 @@ func (l *Logger) Level(level int) *Logger { return logger } +// Skip is a chaining function, +// which sets backtrace skip for the current logging content output. +// It also affects the caller file path checks when line number printing enabled. +func (l *Logger) Skip(skip int) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + logger.SetBacktraceSkip(skip) + return logger +} + // Backtrace is a chaining function, // which sets backtrace options for the current logging content output . func (l *Logger) Backtrace(enabled bool, skip...int) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l @@ -98,28 +111,63 @@ func (l *Logger) Backtrace(enabled bool, skip...int) *Logger { return logger } -// StdPrint is a chaining function, +// Stdout is a chaining function, // which enables/disables stdout for the current logging content output. -func (l *Logger) StdPrint(enabled bool) *Logger { - logger := (*Logger)(nil) - if l.pr == nil { - logger = l.Clone() - } else { - logger = l - } - logger.SetStdPrint(enabled) - return logger +// It's enabled in default. +func (l *Logger) Stdout(enabled...bool) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + if len(enabled) > 0 && enabled[0] { + logger.stdoutPrint = true + } else { + logger.stdoutPrint = false + } + return logger +} + +// See Stdout. +// Deprecated. +func (l *Logger) StdPrint(enabled...bool) *Logger { + return l.Stdout(enabled...) } // Header is a chaining function, // which enables/disables log header for the current logging content output. -func (l *Logger) Header(enabled bool) *Logger { +// It's enabled in default. +func (l *Logger) Header(enabled...bool) *Logger { logger := (*Logger)(nil) - if l.pr == nil { + if l.parent == nil { logger = l.Clone() } else { logger = l } - logger.printHeader.Set(enabled) + if len(enabled) > 0 && enabled[0] { + logger.SetHeaderPrint(true) + } else { + logger.SetHeaderPrint(false) + } return logger +} + +// Line is a chaining function, +// which enables/disables printing its caller file path along with its line number. +// The param specified whether print the long absolute file path, eg: /a/b/c/d.go:23, +// or else short one: d.go:23. +func (l *Logger) Line(long...bool) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + if len(long) > 0 && long[0] { + logger.flags |= F_FILE_LONG + } else { + logger.flags |= F_FILE_SHORT + } + return logger } \ No newline at end of file diff --git a/g/os/glog/glog_logger_writer.go b/g/os/glog/glog_logger_writer.go index 525fe438f..d2eef9dee 100644 --- a/g/os/glog/glog_logger_writer.go +++ b/g/os/glog/glog_logger_writer.go @@ -6,13 +6,9 @@ package glog -type Writer struct { - logger *Logger -} - // Write implements the io.Writer interface. -// It just prints the content with header or level. -func (w *Writer) Write(p []byte) (n int, err error) { - w.logger.Header(false).Print(string(p)) +// It just prints the content using Print. +func (l *Logger) Write(p []byte) (n int, err error) { + l.Header(false).Print(string(p)) return len(p), nil } \ No newline at end of file diff --git a/geg/os/glog/glog_file.go b/geg/os/glog/glog_file.go index 8a409863f..29797dff0 100644 --- a/geg/os/glog/glog_file.go +++ b/geg/os/glog/glog_file.go @@ -11,7 +11,7 @@ func main() { l := glog.New() path := "/tmp/glog" l.SetPath(path) - l.SetStdPrint(false) + l.SetStdoutPrint(false) // 使用默认文件名称格式 l.Println("标准文件名称格式,使用当前时间时期") // 通过SetFile设置文件名称格式 diff --git a/geg/os/glog/glog_flags.go b/geg/os/glog/glog_flags.go new file mode 100644 index 000000000..b2561ee33 --- /dev/null +++ b/geg/os/glog/glog_flags.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" +) + +func main() { + l := glog.New() + l.SetFlags(glog.F_TIME_TIME|glog.F_FILE_SHORT) + l.Println("time and short line number") + l.SetFlags(glog.F_TIME_MILLI|glog.F_FILE_LONG) + l.Println("time with millisecond and long line number") + l.SetFlags(glog.F_TIME_STD|glog.F_FILE_LONG) + l.Println("standard time format and long line number") +} diff --git a/geg/os/glog/glog_line.go b/geg/os/glog/glog_line.go new file mode 100644 index 000000000..41d2e0779 --- /dev/null +++ b/geg/os/glog/glog_line.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" +) + +func main() { + glog.Line().Println("this is the short file name with its line number") + glog.Line(true).Println("lone file name with line number") +} diff --git a/geg/os/glog/glog_line2.go b/geg/os/glog/glog_line2.go new file mode 100644 index 000000000..2e3730114 --- /dev/null +++ b/geg/os/glog/glog_line2.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" +) + +func PrintLog(content string) { + glog.Skip(1).Line().Println("line number with skip:", content) + glog.Line().Println("line number without skip:", content) +} + +func main() { + PrintLog("just test") +} diff --git a/geg/os/glog/glog_prefix.go b/geg/os/glog/glog_prefix.go new file mode 100644 index 000000000..7ce5a3d61 --- /dev/null +++ b/geg/os/glog/glog_prefix.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" +) + +func main() { + l := glog.New() + l.SetPrefix("[API]") + l.Println("hello world") + l.Error("error occurred") +} diff --git a/geg/os/glog/glog_stdout.go b/geg/os/glog/glog_stdout.go new file mode 100644 index 000000000..3935519ff --- /dev/null +++ b/geg/os/glog/glog_stdout.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/gogf/gf/g/os/glog" + "sync" +) + +func main() { + wg := sync.WaitGroup{} + c := make(chan struct{}) + wg.Add(3000) + for i := 0; i < 3000; i++ { + go func() { + <-c + glog.Println("abcdefghijklmnopqrstuvwxyz1234567890") + wg.Done() + }() + } + close(c) + wg.Wait() +} diff --git a/geg/os/glog/glog_writer.go b/geg/os/glog/glog_writer.go deleted file mode 100644 index 792678261..000000000 --- a/geg/os/glog/glog_writer.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/gogf/gf/g/os/glog" -) - -func main() { - w := glog.GetWriter() - w.Write([]byte("hello")) - - glog.Path("/tmp/glog/test").GetWriter().Write([]byte("hello")) -} diff --git a/geg/os/glog/glog_writer_greylog.go b/geg/os/glog/glog_writer_greylog.go new file mode 100644 index 000000000..7d154fa62 --- /dev/null +++ b/geg/os/glog/glog_writer_greylog.go @@ -0,0 +1,30 @@ +package main + +//import ( +// "github.com/gogf/gf/g/os/glog" +// "github.com/robertkowalski/graylog-golang" +//) +// +//type MyGrayLogWriter struct { +// gelf *gelf.Gelf +// logger *glog.Logger +//} +// +//func (w *MyGrayLogWriter) Write(p []byte) (n int, err error) { +// w.gelf.Send(p) +// return w.logger.Write(p) +//} +// +//func main() { +// glog.SetWriter(&MyGrayLogWriter{ +// logger : glog.New(), +// gelf : gelf.New(gelf.Config{ +// GraylogPort : 80, +// GraylogHostname : "graylog-host.com", +// Connection : "wan", +// MaxChunkSizeWan : 42, +// MaxChunkSizeLan : 1337, +// }), +// }) +// glog.Println("test log") +//} diff --git a/geg/os/glog/glog_writer_hook.go b/geg/os/glog/glog_writer_hook.go new file mode 100644 index 000000000..02e999248 --- /dev/null +++ b/geg/os/glog/glog_writer_hook.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g/os/glog" + "github.com/gogf/gf/g/text/gregex" +) + +type MyWriter struct { + logger *glog.Logger +} + +func (w *MyWriter) Write(p []byte) (n int, err error) { + s := string(p) + if gregex.IsMatchString(`\[(PANI|FATA)\]`, s) { + fmt.Println("SERIOUS ISSUE OCCURRED!! I'd better tell monitor in first time!") + ghttp.PostContent("http://monitor.mydomain.com", s) + } + return w.logger.Write(p) +} + +func main() { + glog.SetWriter(&MyWriter{ + logger : glog.New(), + }) + glog.Fatal("FATAL ERROR") +} diff --git a/geg/other/test.go b/geg/other/test.go index 47632c222..4f5b4d622 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -3,12 +3,14 @@ package main import ( "fmt" "github.com/gogf/gf/g" - "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/util/gconv" + "log" + "os" ) func main() { - fmt.Println(gfile.Dir("/")) + var mylog = log.New(os.Stdout, "[Api] ", log.LstdFlags|log.Lshortfile) + mylog.Println(123) return a := []int{1,2,3} fmt.Println(a[:0]) diff --git a/version.go b/version.go index 3a5151943..6589dd5ed 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.6.13" +const VERSION = "v1.6.14" const AUTHORS = "john"