mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
@ -64,13 +64,15 @@ func main() {
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
|
||||
# Contributors
|
||||
|
||||
- [aloncn](https://github.com/aloncn)
|
||||
- [chenyang351](https://github.com/chenyang351)
|
||||
- [garfieldkwong](https://gitee.com/garfieldkwong)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [johng](https://gitee.com/johng)
|
||||
- [johng](https://johng.cn)
|
||||
- [jroam](https://github.com/jroam)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [qq1054000800](https://gitee.com/qq1054000800)
|
||||
- [qq976739120](https://github.com/qq976739120)
|
||||
@ -86,6 +88,7 @@ func main() {
|
||||
|
||||
- [flyke-xu](https://gitee.com/flyke-xu)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [ireadx](https://github.com/ireadx)
|
||||
- [mg91](https://gitee.com/mg91)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [tiangenglan](https://gitee.com/tiangenglan)
|
||||
@ -98,4 +101,3 @@ func main() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -30,7 +30,6 @@ go get -u github.com/gogf/gf
|
||||
```
|
||||
require github.com/gogf/gf latest
|
||||
```
|
||||
> 如果您是从旧版本`1.x`升级到`1.5.0`那么请参考:[1.x升级到1.5.0](https://goframe.org/upgradeto150)
|
||||
|
||||
# 限制
|
||||
```shell
|
||||
@ -90,7 +89,8 @@ func main() {
|
||||
- [chenyang351](https://github.com/chenyang351)
|
||||
- [garfieldkwong](https://gitee.com/garfieldkwong)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [johng](https://gitee.com/johng)
|
||||
- [johng](https://johng.cn)
|
||||
- [jroam](https://github.com/jroam)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [qq1054000800](https://gitee.com/qq1054000800)
|
||||
- [qq976739120](https://github.com/qq976739120)
|
||||
@ -106,6 +106,7 @@ func main() {
|
||||
|
||||
- [flyke-xu](https://gitee.com/flyke-xu)
|
||||
- [hailaz](https://gitee.com/hailaz)
|
||||
- [ireadx](https://github.com/ireadx)
|
||||
- [mg91](https://gitee.com/mg91)
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [tiangenglan](https://gitee.com/tiangenglan)
|
||||
|
||||
@ -15,60 +15,60 @@ import (
|
||||
)
|
||||
|
||||
// Console values.
|
||||
type gCmdValue struct {
|
||||
values []string
|
||||
type gCmdValue struct {
|
||||
values []string
|
||||
}
|
||||
|
||||
// Console options.
|
||||
type gCmdOption struct {
|
||||
options map[string]string
|
||||
options map[string]string
|
||||
}
|
||||
|
||||
var Value = &gCmdValue{} // Console values.
|
||||
var Option = &gCmdOption{} // Console options.
|
||||
var Value = &gCmdValue{} // Console values.
|
||||
var Option = &gCmdOption{} // Console options.
|
||||
var cmdFuncMap = make(map[string]func()) // Registered callback functions.
|
||||
|
||||
func init() {
|
||||
reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`)
|
||||
Option.options = make(map[string]string)
|
||||
for i := 0; i < len(os.Args); i++ {
|
||||
result := reg.FindStringSubmatch(os.Args[i])
|
||||
if len(result) > 1 {
|
||||
Option.options[result[1]] = result[2]
|
||||
} else {
|
||||
Value.values = append(Value.values, os.Args[i])
|
||||
}
|
||||
}
|
||||
reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`)
|
||||
Option.options = make(map[string]string)
|
||||
for i := 0; i < len(os.Args); i++ {
|
||||
result := reg.FindStringSubmatch(os.Args[i])
|
||||
if len(result) > 1 {
|
||||
Option.options[result[1]] = result[2]
|
||||
} else {
|
||||
Value.values = append(Value.values, os.Args[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BindHandle registers callback function <f> with <cmd>.
|
||||
func BindHandle (cmd string, f func()) {
|
||||
if _, ok := cmdFuncMap[cmd]; ok {
|
||||
glog.Fatal("duplicated handle for command:" + cmd)
|
||||
} else {
|
||||
cmdFuncMap[cmd] = f
|
||||
}
|
||||
func BindHandle(cmd string, f func()) {
|
||||
if _, ok := cmdFuncMap[cmd]; ok {
|
||||
glog.Fatal("duplicated handle for command:" + cmd)
|
||||
} else {
|
||||
cmdFuncMap[cmd] = f
|
||||
}
|
||||
}
|
||||
|
||||
// RunHandle executes the callback function registered by <cmd>.
|
||||
func RunHandle (cmd string) {
|
||||
if handle, ok := cmdFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
glog.Fatal("no handle found for command:" + cmd)
|
||||
}
|
||||
func RunHandle(cmd string) {
|
||||
if handle, ok := cmdFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
glog.Fatal("no handle found for command:" + cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// AutoRun automatically recognizes and executes the callback function
|
||||
// by value of index 0 (the first console parameter).
|
||||
func AutoRun () {
|
||||
if cmd := Value.Get(1); cmd != "" {
|
||||
if handle, ok := cmdFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
glog.Fatal("no handle found for command:" + cmd)
|
||||
}
|
||||
} else {
|
||||
glog.Fatal("no command found")
|
||||
}
|
||||
func AutoRun() {
|
||||
if cmd := Value.Get(1); cmd != "" {
|
||||
if handle, ok := cmdFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
glog.Fatal("no handle found for command:" + cmd)
|
||||
}
|
||||
} else {
|
||||
glog.Fatal("no command found")
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,18 +15,22 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 定时任务项
|
||||
// Timed task entry.
|
||||
type Entry struct {
|
||||
cron *Cron // 所属定时任务
|
||||
entry *gtimer.Entry // 定时器任务对象
|
||||
schedule *cronSchedule // 定时任务配置对象
|
||||
jobName string // 任务注册方法名称
|
||||
Name string // 定时任务名称
|
||||
Job func() // 注册定时任务方法
|
||||
Time time.Time // 注册时间
|
||||
cron *Cron // Cron object belonged to.
|
||||
entry *gtimer.Entry // Associated gtimer.Entry.
|
||||
schedule *cronSchedule // Timed schedule object.
|
||||
jobName string // Callback function name(address info).
|
||||
Name string // Entry name.
|
||||
Job func() `json:"-"` // Callback function.
|
||||
Time time.Time // Registered time.
|
||||
}
|
||||
|
||||
// 创建定时任务
|
||||
// addEntry creates and returns a new Entry object.
|
||||
// Param <job> is the callback function for timed task execution.
|
||||
// Param <singleton> specifies whether timed task executing in singleton mode.
|
||||
// Param <times> limits the times for timed task executing.
|
||||
// Param <name> names this entry for manual control.
|
||||
func (c *Cron) addEntry(pattern string, job func(), singleton bool, times int, name ... string) (*Entry, error) {
|
||||
schedule, err := newSchedule(pattern)
|
||||
if err != nil {
|
||||
|
||||
@ -3,11 +3,10 @@
|
||||
// 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 john, zseeker
|
||||
|
||||
// Package glog implements powerful and easy-to-use levelled logging functionality.
|
||||
//
|
||||
// 日志模块, 直接文件/输出操作,没有异步逻辑,没有使用缓存或者通道
|
||||
package glog
|
||||
|
||||
import (
|
||||
@ -29,10 +28,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// default level for log
|
||||
// Default level for log
|
||||
defaultLevel = gtype.NewInt(LEVEL_ALL)
|
||||
|
||||
// default logger object, for package method usage
|
||||
// Default logger object, for package method usage
|
||||
logger = New()
|
||||
)
|
||||
|
||||
@ -41,8 +40,6 @@ func init() {
|
||||
}
|
||||
|
||||
// SetPath sets the directory path for file logging.
|
||||
//
|
||||
// 日志日志目录绝对路径.
|
||||
func SetPath(path string) {
|
||||
logger.SetPath(path)
|
||||
}
|
||||
@ -50,151 +47,113 @@ func SetPath(path string) {
|
||||
// SetFile sets the file name <pattern> for file logging.
|
||||
// Datetime pattern can be used in <pattern>, eg: access-{Ymd}.log.
|
||||
// The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log
|
||||
//
|
||||
// 日志文件名称.
|
||||
func SetFile(pattern string) {
|
||||
logger.SetFile(pattern)
|
||||
}
|
||||
|
||||
// SetLevel sets the default logging level.
|
||||
//
|
||||
// 设置全局的日志记录等级.
|
||||
func SetLevel(level int) {
|
||||
logger.SetLevel(level)
|
||||
defaultLevel.Set(level)
|
||||
}
|
||||
|
||||
// SetWriter sets the customed logging <writer> for logging.
|
||||
// SetWriter sets the customized logging <writer> for logging.
|
||||
// The <writer> object should implements the io.Writer interface.
|
||||
// Developer can use customed logging <writer> to redirect logging output to another service,
|
||||
// Developer can use customized logging <writer> to redirect logging output to another service,
|
||||
// eg: kafka, mysql, mongodb, etc.
|
||||
//
|
||||
// 可自定义IO接口,IO可以是文件输出、标准输出、网络输出.
|
||||
func SetWriter(writer io.Writer) {
|
||||
logger.SetWriter(writer)
|
||||
}
|
||||
|
||||
// GetWriter returns the customed writer object, which implements the io.Writer interface.
|
||||
// It returns nil if no customed writer set.
|
||||
//
|
||||
// 返回自定义的IO,默认为nil.
|
||||
// GetWriter returns the customized writer object, which implements the io.Writer interface.
|
||||
// It returns nil if no customized writer set.
|
||||
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.
|
||||
//
|
||||
// 设置是否允许输出DEBUG信息.
|
||||
func SetDebug(debug bool) {
|
||||
logger.SetDebug(debug)
|
||||
}
|
||||
|
||||
// SetStdPrint sets whether ouptput the logging contents to stdout, which is false indefault.
|
||||
//
|
||||
// 设置写日志的同时开启or关闭控制台打印,默认是关闭的
|
||||
// SetStdPrint sets whether ouptput the logging contents to stdout, which is false in default.
|
||||
func SetStdPrint(open bool) {
|
||||
logger.SetStdPrint(open)
|
||||
}
|
||||
|
||||
// GetPath returns the logging directory path for file logging.
|
||||
// It returns empty string if no directory path set.
|
||||
//
|
||||
// 获取日志目录绝对路径
|
||||
func GetPath() string {
|
||||
return logger.GetPath()
|
||||
}
|
||||
|
||||
// PrintBacktrace prints the caller backtrace,
|
||||
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
|
||||
//
|
||||
// 打印文件调用回溯信息
|
||||
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
||||
func PrintBacktrace(skip...int) {
|
||||
logger.PrintBacktrace(skip...)
|
||||
}
|
||||
|
||||
// GetBacktrace returns the caller backtrace content,
|
||||
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
|
||||
//
|
||||
// 获取文件调用回溯信息.
|
||||
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
||||
func GetBacktrace(skip...int) string {
|
||||
return logger.GetBacktrace(skip...)
|
||||
}
|
||||
|
||||
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
|
||||
//
|
||||
// 是否关闭全局的backtrace信息
|
||||
func SetBacktrace(enabled bool) {
|
||||
logger.SetBacktrace(enabled)
|
||||
}
|
||||
|
||||
// To is a chaining function,
|
||||
// which redirects current logging content output to the sepecified <writer>.
|
||||
//
|
||||
// 链式操作,设置下一次写入日志内容的Writer
|
||||
func To(writer io.Writer) *Logger {
|
||||
return logger.To(writer)
|
||||
}
|
||||
|
||||
// Path is a chaining function,
|
||||
// which sets the directory path to <path> for current logging content output.
|
||||
//
|
||||
// 链式操作,设置下一次输出的日志路径。
|
||||
func Path(path string) *Logger {
|
||||
return logger.Path(path)
|
||||
}
|
||||
|
||||
// Cat is a chaining function,
|
||||
// which sets the category to <category> for current logging content output.
|
||||
//
|
||||
// 设置下一次输出的分类,支持多级分类设置.
|
||||
func Cat(category string) *Logger {
|
||||
return logger.Cat(category)
|
||||
}
|
||||
|
||||
// File is a chaining function,
|
||||
// which sets file name <pattern> 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)
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// 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 john, zseeker
|
||||
|
||||
package glog
|
||||
@ -26,15 +27,15 @@ import (
|
||||
|
||||
type Logger struct {
|
||||
mu sync.RWMutex
|
||||
pr *Logger // 父级Logger
|
||||
io io.Writer // 日志内容写入的IO接口
|
||||
path *gtype.String // 日志写入的目录路径
|
||||
file *gtype.String // 日志文件名称格式
|
||||
level *gtype.Int // 日志输出等级
|
||||
btSkip *gtype.Int // 错误产生时的backtrace回调信息skip条数
|
||||
btStatus *gtype.Int // 是否当打印错误时同时开启backtrace打印(默认-1,表示默认打印逻辑 - 错误才打印)
|
||||
printHeader *gtype.Bool // 是否不打印前缀信息(时间,级别等)
|
||||
alsoStdPrint *gtype.Bool // 控制台打印开关,当输出到文件/自定义输出时也同时打印到终端
|
||||
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).
|
||||
}
|
||||
|
||||
const (
|
||||
@ -45,70 +46,66 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// 默认的日志换行符
|
||||
// Default line break.
|
||||
ln = "\n"
|
||||
// 标准输出互斥锁,防止标准输出串日志
|
||||
// Mutex to ensure log output sequence.
|
||||
stdMu = sync.RWMutex{}
|
||||
)
|
||||
|
||||
// 初始化日志换行符
|
||||
func init() {
|
||||
// Initialize log line breaks depending on underlying os.
|
||||
if runtime.GOOS == "windows" {
|
||||
ln = "\r\n"
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a custom logger.
|
||||
//
|
||||
// 新建自定义的日志操作对象
|
||||
// New creates and returns a custom logger.
|
||||
func New() *Logger {
|
||||
return &Logger {
|
||||
io : nil,
|
||||
logger := &Logger {
|
||||
path : gtype.NewString(),
|
||||
file : gtype.NewString(gDEFAULT_FILE_FORMAT),
|
||||
level : gtype.NewInt(defaultLevel.Val()),
|
||||
btSkip : gtype.NewInt(),
|
||||
btStatus : gtype.NewInt(-1),
|
||||
btStatus : gtype.NewInt(1),
|
||||
printHeader : gtype.NewBool(true),
|
||||
alsoStdPrint : gtype.NewBool(true),
|
||||
}
|
||||
logger.writer = &Writer {
|
||||
logger : logger,
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
// Clone returns a new logger, which is the clone the current logger.
|
||||
//
|
||||
// Logger拷贝.
|
||||
func (l *Logger) Clone() *Logger {
|
||||
return &Logger {
|
||||
logger := &Logger {
|
||||
pr : l,
|
||||
io : l.GetWriter(),
|
||||
path : l.path.Clone(),
|
||||
file : l.file.Clone(),
|
||||
level : l.level.Clone(),
|
||||
btSkip : l.btSkip.Clone(),
|
||||
btStatus : l.btStatus.Clone(),
|
||||
btStatus : l.btStatus.Clone(),
|
||||
printHeader : l.printHeader.Clone(),
|
||||
alsoStdPrint : l.alsoStdPrint.Clone(),
|
||||
}
|
||||
logger.writer = &Writer {
|
||||
logger : logger,
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
// SetLevel sets the logging level.
|
||||
//
|
||||
// 设置日志记录等级
|
||||
func (l *Logger) SetLevel(level int) {
|
||||
l.level.Set(level)
|
||||
}
|
||||
|
||||
// GetLevel returns the logging level value.
|
||||
//
|
||||
// 获取日志记录等级
|
||||
func (l *Logger) GetLevel() int {
|
||||
return l.level.Val()
|
||||
}
|
||||
|
||||
// SetDebug enables/disables the debug level for logger.
|
||||
// The debug level is enbaled in default.
|
||||
//
|
||||
// 快捷方法,打开或关闭DEBU日志信息
|
||||
// The debug level is enabled in default.
|
||||
func (l *Logger) SetDebug(debug bool) {
|
||||
if debug {
|
||||
l.level.Set(l.level.Val() | LEVEL_DEBU)
|
||||
@ -124,7 +121,6 @@ func (l *Logger) SetBacktrace(enabled bool) {
|
||||
} else {
|
||||
l.btStatus.Set(0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// SetBacktraceSkip sets the backtrace offset from the end point.
|
||||
@ -132,47 +128,41 @@ func (l *Logger) SetBacktraceSkip(skip int) {
|
||||
l.btSkip.Set(skip)
|
||||
}
|
||||
|
||||
// SetWriter sets the customed logging <writer> for logging.
|
||||
// SetWriter sets the customized logging <writer> for logging.
|
||||
// The <writer> object should implements the io.Writer interface.
|
||||
// Developer can use customed logging <writer> to redirect logging output to another service,
|
||||
// Developer can use customized logging <writer> to redirect logging output to another service,
|
||||
// eg: kafka, mysql, mongodb, etc.
|
||||
//
|
||||
// 可自定义IO接口,IO可以是文件输出、标准输出、网络输出
|
||||
func (l *Logger) SetWriter(writer io.Writer) {
|
||||
l.mu.Lock()
|
||||
l.io = writer
|
||||
l.writer = writer
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// GetWriter returns the customed writer object, which implements the io.Writer interface.
|
||||
// It returns nil if no customed writer set.
|
||||
//
|
||||
// 返回自定义的IO,默认为nil
|
||||
// GetWriter returns the customized writer object, which implements the io.Writer interface.
|
||||
// It returns a default writer if no customized writer set.
|
||||
func (l *Logger) GetWriter() io.Writer {
|
||||
l.mu.RLock()
|
||||
r := l.io
|
||||
r := l.writer
|
||||
l.mu.RUnlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// getFilePointer returns the file pinter for file logging.
|
||||
// It returns nil if file logging disabled, or file open fails.
|
||||
//
|
||||
// 获取默认的文件IO.
|
||||
// It returns nil if file logging is disabled, or file opening fails.
|
||||
func (l *Logger) getFilePointer() *gfpool.File {
|
||||
if path := l.path.Val(); path != "" {
|
||||
// 文件名称中使用"{}"包含的内容使用gtime格式化
|
||||
// Content containing "{}" in the file name is formatted using gtime
|
||||
file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file.Val(), func(s string) string {
|
||||
return gtime.Now().Format(strings.Trim(s, "{}"))
|
||||
})
|
||||
// 如果日志目录不存在则创建目录路径
|
||||
// Create path if it does not exist。
|
||||
if !gfile.Exists(path) {
|
||||
if err := gfile.Mkdir(path); err != nil {
|
||||
fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fpath := path + gfile.Separator + file
|
||||
fpath := path + gfile.Separator + file
|
||||
if fp, err := gfpool.Open(fpath, gDEFAULT_FILE_POOL_FLAGS, gDEFAULT_FPOOL_PERM, gDEFAULT_FPOOL_EXPIRE); err == nil {
|
||||
return fp
|
||||
} else {
|
||||
@ -183,14 +173,10 @@ func (l *Logger) getFilePointer() *gfpool.File {
|
||||
}
|
||||
|
||||
// SetPath sets the directory path for file logging.
|
||||
//
|
||||
// 设置日志文件的存储目录路径.
|
||||
func (l *Logger) SetPath(path string) error {
|
||||
// path必须有值
|
||||
if path == "" {
|
||||
return errors.New("path is empty")
|
||||
}
|
||||
// 如果目录不存在,则递归创建
|
||||
if !gfile.Exists(path) {
|
||||
if err := gfile.Mkdir(path); err != nil {
|
||||
fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error()))
|
||||
@ -203,8 +189,6 @@ func (l *Logger) SetPath(path string) error {
|
||||
|
||||
// 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()
|
||||
}
|
||||
@ -212,29 +196,24 @@ func (l *Logger) GetPath() string {
|
||||
// SetFile sets the file name <pattern> for file logging.
|
||||
// Datetime pattern can be used in <pattern>, 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)
|
||||
}
|
||||
|
||||
// SetStdPrint sets whether ouptput the logging contents to stdout, which is false indefault.
|
||||
//
|
||||
// 设置写日志时开启or关闭控制台打印,默认是关闭的
|
||||
// SetStdPrint sets whether output the logging contents to stdout, which is false in default.
|
||||
func (l *Logger) SetStdPrint(enabled bool) {
|
||||
l.alsoStdPrint.Set(enabled)
|
||||
}
|
||||
|
||||
// 这里的写锁保证统一时刻只会写入一行日志,防止串日志的情况
|
||||
// print prints <s> to defined writer, logging file or passed <std>.
|
||||
// It internally uses memory lock for file logging to ensure logging sequence.
|
||||
func (l *Logger) print(std io.Writer, s string) {
|
||||
// 优先使用自定义的IO输出
|
||||
// Customized writer has the most high priority.
|
||||
if l.printHeader.Val() {
|
||||
s = l.format(s)
|
||||
}
|
||||
writer := l.GetWriter()
|
||||
if writer == nil {
|
||||
// 如果设置的writer为空,那么其次判断是否有文件输出设置
|
||||
// 内部使用了内存锁,保证在glog中对同一个日志文件的并发写入不会串日志(并发安全)
|
||||
if _, ok := writer.(*Writer); ok {
|
||||
if f := l.getFilePointer(); f != nil {
|
||||
defer f.Close()
|
||||
key := l.path.Val()
|
||||
@ -245,7 +224,7 @@ func (l *Logger) print(std io.Writer, s string) {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
}
|
||||
}
|
||||
// 当没有设置writer时,需要判断是否允许输出到标准输出
|
||||
// Also output to stdout?
|
||||
if l.alsoStdPrint.Val() {
|
||||
l.doStdLockPrint(std, s)
|
||||
}
|
||||
@ -254,7 +233,7 @@ func (l *Logger) print(std io.Writer, s string) {
|
||||
}
|
||||
}
|
||||
|
||||
// 并发安全打印到标准输出
|
||||
// doStdLockPrint prints <s> to <std> concurrent-safely.
|
||||
func (l *Logger) doStdLockPrint(std io.Writer, s string) {
|
||||
stdMu.Lock()
|
||||
if _, err := std.Write([]byte(s)); err != nil {
|
||||
@ -263,23 +242,21 @@ func (l *Logger) doStdLockPrint(std io.Writer, s string) {
|
||||
stdMu.Unlock()
|
||||
}
|
||||
|
||||
// 核心打印数据方法(标准输出)
|
||||
// stdPrint prints content <s> without backtrace.
|
||||
func (l *Logger) stdPrint(s string) {
|
||||
l.print(os.Stdout, s)
|
||||
}
|
||||
|
||||
// 核心打印数据方法(标准错误)
|
||||
// stdPrint prints content <s> with backtrace check.
|
||||
func (l *Logger) errPrint(s string) {
|
||||
// 记录调用回溯信息
|
||||
status := l.btStatus.Val()
|
||||
if status == -1 || status == 1 {
|
||||
if l.btStatus.Val() == 1 {
|
||||
s = l.appendBacktrace(s)
|
||||
}
|
||||
// 防止串日志情况,这里不使用stderr,而是使用stdout
|
||||
// In matter of sequence, do not use stderr here, but use the same stdout.
|
||||
l.print(os.Stdout, s)
|
||||
}
|
||||
|
||||
// 输出内容中添加回溯信息
|
||||
// appendBacktrace appends backtrace to the <s>.
|
||||
func (l *Logger) appendBacktrace(s string, skip...int) string {
|
||||
trace := l.GetBacktrace(skip...)
|
||||
if trace != "" {
|
||||
@ -298,17 +275,13 @@ func (l *Logger) appendBacktrace(s string, skip...int) string {
|
||||
}
|
||||
|
||||
// PrintBacktrace prints the caller backtrace,
|
||||
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
|
||||
//
|
||||
// 直接打印回溯信息,参数skip表示调用端往上多少级开始回溯
|
||||
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
||||
func (l *Logger) PrintBacktrace(skip...int) {
|
||||
l.Println(l.appendBacktrace("", skip...))
|
||||
}
|
||||
|
||||
// GetBacktrace returns the caller backtrace content,
|
||||
// the optional parameter <skip> specify the skipped backtraces offset from the end point.
|
||||
//
|
||||
// 获取文件调用回溯字符串,参数skip表示调用端往上多少级开始回溯
|
||||
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
||||
func (l *Logger) GetBacktrace(skip...int) string {
|
||||
customSkip := 0
|
||||
if len(skip) > 0 {
|
||||
@ -507,8 +480,6 @@ func (l *Logger) Criticalfln(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
// checkLevel checks whether the given <level> could be output.
|
||||
//
|
||||
// 判断给定level是否满足
|
||||
func (l *Logger) checkLevel(level int) bool {
|
||||
return l.level.Val() & level > 0
|
||||
}
|
||||
@ -13,8 +13,6 @@ import (
|
||||
|
||||
// To is a chaining function,
|
||||
// which redirects current logging content output to the specified <writer>.
|
||||
//
|
||||
// 链式操作,设置下一次写入日志内容的Writer
|
||||
func (l *Logger) To(writer io.Writer) *Logger {
|
||||
logger := (*Logger)(nil)
|
||||
if l.pr == nil {
|
||||
@ -28,8 +26,6 @@ func (l *Logger) To(writer io.Writer) *Logger {
|
||||
|
||||
// Path is a chaining function,
|
||||
// which sets the directory path to <path> for current logging content output.
|
||||
//
|
||||
// 链式操作,设置下一次输出的日志路径。
|
||||
func (l *Logger) Path(path string) *Logger {
|
||||
logger := (*Logger)(nil)
|
||||
if l.pr == nil {
|
||||
@ -45,9 +41,7 @@ func (l *Logger) Path(path string) *Logger {
|
||||
|
||||
// Cat is a chaining function,
|
||||
// which sets the category to <category> for current logging content output.
|
||||
//
|
||||
// 链式操作,设置下一次输出的日志分类(可以按照文件目录层级设置),在当前logpath或者当前工作目录下创建category目录,
|
||||
// 这是一个链式操作,可以设置多个分类,将会创建层级的日志分类目录。
|
||||
// Param <category> can be hierarchical, eg: module/user.
|
||||
func (l *Logger) Cat(category string) *Logger {
|
||||
logger := (*Logger)(nil)
|
||||
if l.pr == nil {
|
||||
@ -64,8 +58,6 @@ func (l *Logger) Cat(category string) *Logger {
|
||||
|
||||
// File is a chaining function,
|
||||
// which sets file name <pattern> for the current logging content output.
|
||||
//
|
||||
// 日志文件格式
|
||||
func (l *Logger) File(file string) *Logger {
|
||||
logger := (*Logger)(nil)
|
||||
if l.pr == nil {
|
||||
@ -79,8 +71,6 @@ func (l *Logger) File(file string) *Logger {
|
||||
|
||||
// Level is a chaining function,
|
||||
// which sets logging level for the current logging content output.
|
||||
//
|
||||
// 设置日志打印等级
|
||||
func (l *Logger) Level(level int) *Logger {
|
||||
logger := (*Logger)(nil)
|
||||
if l.pr == nil {
|
||||
@ -94,8 +84,6 @@ func (l *Logger) Level(level int) *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 {
|
||||
@ -112,8 +100,6 @@ func (l *Logger) Backtrace(enabled bool, skip...int) *Logger {
|
||||
|
||||
// StdPrint 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 {
|
||||
@ -127,8 +113,6 @@ func (l *Logger) StdPrint(enabled bool) *Logger {
|
||||
|
||||
// Header is a chaining function,
|
||||
// which enables/disables log header for the current logging content output.
|
||||
//
|
||||
// 是否打印每行日志头信息(默认开启)
|
||||
func (l *Logger) Header(enabled bool) *Logger {
|
||||
logger := (*Logger)(nil)
|
||||
if l.pr == nil {
|
||||
|
||||
18
g/os/glog/glog_logger_writer.go
Normal file
18
g/os/glog/glog_logger_writer.go
Normal file
@ -0,0 +1,18 @@
|
||||
// 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
|
||||
|
||||
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))
|
||||
return len(p), nil
|
||||
}
|
||||
@ -29,7 +29,6 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 视图对象
|
||||
type View struct {
|
||||
mu sync.RWMutex
|
||||
paths *garray.StringArray // 模板查找目录(绝对路径)
|
||||
@ -38,29 +37,32 @@ type View struct {
|
||||
delimiters []string // 模板变量分隔符号
|
||||
}
|
||||
|
||||
// 模板变量
|
||||
// Template params type.
|
||||
type Params = map[string]interface{}
|
||||
|
||||
// 函数映射表
|
||||
// Customized template function map type.
|
||||
type FuncMap = map[string]interface{}
|
||||
|
||||
// 默认的视图对象
|
||||
// Default view object.
|
||||
var defaultViewObj *View
|
||||
|
||||
// 初始化默认的视图对象, 默认加载包不会初始化,使用包方法才会初始化模板引擎对象。
|
||||
// checkAndInitDefaultView checks and initializes the default view object.
|
||||
// The default view object will be initialized just once.
|
||||
func checkAndInitDefaultView() {
|
||||
if defaultViewObj == nil {
|
||||
defaultViewObj = New(gfile.Pwd())
|
||||
}
|
||||
}
|
||||
|
||||
// 直接解析模板内容,返回解析后的内容
|
||||
// ParseContent parses the template content directly using the default view object
|
||||
// and returns the parsed content.
|
||||
func ParseContent(content string, params Params) ([]byte, error) {
|
||||
checkAndInitDefaultView()
|
||||
return defaultViewObj.ParseContent(content, params)
|
||||
}
|
||||
|
||||
// 生成一个视图对象
|
||||
// New returns a new view object.
|
||||
// The parameter <path> specifies the template directory path to load template files.
|
||||
func New(path...string) *View {
|
||||
view := &View {
|
||||
paths : garray.NewStringArray(),
|
||||
@ -92,11 +94,11 @@ func New(path...string) *View {
|
||||
}
|
||||
}
|
||||
view.SetDelimiters("{{", "}}")
|
||||
// 内置变量
|
||||
// default build-in variables.
|
||||
view.data["GF"] = map[string]interface{} {
|
||||
"version" : gf.VERSION,
|
||||
}
|
||||
// 内置方法
|
||||
// default build-in functions.
|
||||
view.BindFunc("text", view.funcText)
|
||||
view.BindFunc("html", view.funcHtmlEncode)
|
||||
view.BindFunc("htmlencode", view.funcHtmlEncode)
|
||||
@ -117,12 +119,13 @@ func New(path...string) *View {
|
||||
return view
|
||||
}
|
||||
|
||||
// 设置模板目录绝对路径
|
||||
// SetPath sets the template directory path for template file search.
|
||||
// The param <path> can be absolute or relative path, but absolute path is suggested.
|
||||
func (view *View) SetPath(path string) error {
|
||||
// 判断绝对路径(或者工作目录下目录)
|
||||
// Absolute path.
|
||||
realPath := gfile.RealPath(path)
|
||||
if realPath == "" {
|
||||
// 判断相对路径
|
||||
// Relative path.
|
||||
view.paths.RLockFunc(func(array []string) {
|
||||
for _, v := range array {
|
||||
if path, _ := gspath.Search(v, path); path != "" {
|
||||
@ -132,7 +135,7 @@ func (view *View) SetPath(path string) error {
|
||||
}
|
||||
})
|
||||
}
|
||||
// 目录不存在错误处理
|
||||
// Path not exist.
|
||||
if realPath == "" {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
if view.paths.Len() > 0 {
|
||||
@ -149,13 +152,13 @@ func (view *View) SetPath(path string) error {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
// 路径必须为目录类型
|
||||
// Should be a directory.
|
||||
if !gfile.IsDir(realPath) {
|
||||
err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path))
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
// 重复判断
|
||||
// Repeated path check.
|
||||
if view.paths.Search(realPath) != -1 {
|
||||
return nil
|
||||
}
|
||||
@ -165,12 +168,12 @@ func (view *View) SetPath(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 添加模板目录搜索路径
|
||||
// AddPath adds a absolute or relative path to the search paths.
|
||||
func (view *View) AddPath(path string) error {
|
||||
// 判断绝对路径(或者工作目录下目录)
|
||||
// Absolute path.
|
||||
realPath := gfile.RealPath(path)
|
||||
if realPath == "" {
|
||||
// 判断相对路径
|
||||
// Relative path.
|
||||
view.paths.RLockFunc(func(array []string) {
|
||||
for _, v := range array {
|
||||
if path, _ := gspath.Search(v, path); path != "" {
|
||||
@ -180,7 +183,7 @@ func (view *View) AddPath(path string) error {
|
||||
}
|
||||
})
|
||||
}
|
||||
// 目录不存在错误处理
|
||||
// Path not exist.
|
||||
if realPath == "" {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
if view.paths.Len() > 0 {
|
||||
@ -197,13 +200,13 @@ func (view *View) AddPath(path string) error {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
// 路径必须为目录类型
|
||||
// realPath should be type of folder.
|
||||
if !gfile.IsDir(realPath) {
|
||||
err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path))
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
// 重复判断
|
||||
// Repeated path check.
|
||||
if view.paths.Search(realPath) != -1 {
|
||||
return nil
|
||||
}
|
||||
@ -212,7 +215,8 @@ func (view *View) AddPath(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
// Assign binds multiple template variables to current view object.
|
||||
// Each goroutine will take effect after the call, so it is concurrent-safe.
|
||||
func (view *View) Assigns(data Params) {
|
||||
view.mu.Lock()
|
||||
for k, v := range data {
|
||||
@ -221,15 +225,18 @@ func (view *View) Assigns(data Params) {
|
||||
view.mu.Unlock()
|
||||
}
|
||||
|
||||
// 绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
// Assign binds a template variable to current view object.
|
||||
// Each goroutine will take effect after the call, so it is concurrent-safe.
|
||||
func (view *View) Assign(key string, value interface{}) {
|
||||
view.mu.Lock()
|
||||
view.data[key] = value
|
||||
view.mu.Unlock()
|
||||
}
|
||||
|
||||
// 解析模板,返回解析后的内容
|
||||
func (view *View) Parse(file string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
|
||||
// ParseContent parses given template file <file>
|
||||
// with given template parameters <params> and function map <funcMap>
|
||||
// and returns the parsed content in []byte.
|
||||
func (view *View) Parse(file string, params Params, funcMap...map[string]interface{}) ([]byte, error) {
|
||||
path := ""
|
||||
view.paths.RLockFunc(func(array []string) {
|
||||
for _, v := range array {
|
||||
@ -254,19 +261,19 @@ func (view *View) Parse(file string, params Params, funcmap...map[string]interfa
|
||||
return nil, errors.New(fmt.Sprintf(`tpl "%s" not found`, file))
|
||||
}
|
||||
content := gfcache.GetContents(path)
|
||||
// 执行模板解析,互斥锁主要是用于funcmap
|
||||
view.mu.RLock()
|
||||
defer view.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
tplobj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
|
||||
if len(funcmap) > 0 {
|
||||
tplobj = tplobj.Funcs(funcmap[0])
|
||||
tplObj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
|
||||
if len(funcMap) > 0 {
|
||||
tplObj = tplObj.Funcs(funcMap[0])
|
||||
}
|
||||
if tpl, err := tplobj.Parse(content); err != nil {
|
||||
if tpl, err := tplObj.Parse(content); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// 注意模板变量赋值不能改变已有的params或者view.data的值,因为这两个变量都是指针
|
||||
// 因此在必要条件下,需要合并两个map的值到一个新的map
|
||||
// Note that the template variable assignment cannot change the value
|
||||
// of the existing <params> or view.data because both variables are pointers.
|
||||
// It's need to merge the values of the two maps into a new map.
|
||||
vars := (map[string]interface{})(nil)
|
||||
if len(view.data) > 0 {
|
||||
if len(params) > 0 {
|
||||
@ -290,21 +297,24 @@ func (view *View) Parse(file string, params Params, funcmap...map[string]interfa
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// 直接解析模板内容,返回解析后的内容
|
||||
func (view *View) ParseContent(content string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
|
||||
// ParseContent parses given template content <content>
|
||||
// with given template parameters <params> and function map <funcMap>
|
||||
// and returns the parsed content in []byte.
|
||||
func (view *View) ParseContent(content string, params Params, funcMap...map[string]interface{}) ([]byte, error) {
|
||||
view.mu.RLock()
|
||||
defer view.mu.RUnlock()
|
||||
name := gconv.String(ghash.BKDRHash64([]byte(content)))
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
tplobj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
|
||||
if len(funcmap) > 0 {
|
||||
tplobj = tplobj.Funcs(funcmap[0])
|
||||
tplObj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
|
||||
if len(funcMap) > 0 {
|
||||
tplObj = tplObj.Funcs(funcMap[0])
|
||||
}
|
||||
if tpl, err := tplobj.Parse(content); err != nil {
|
||||
if tpl, err := tplObj.Parse(content); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// 注意模板变量赋值不能改变已有的params或者view.data的值,因为这两个变量都是指针
|
||||
// 因此在必要条件下,需要合并两个map的值到一个新的map
|
||||
// Note that the template variable assignment cannot change the value
|
||||
// of the existing <params> or view.data because both variables are pointers.
|
||||
// It's need to merge the values of the two maps into a new map.
|
||||
vars := (map[string]interface{})(nil)
|
||||
if len(view.data) > 0 {
|
||||
if len(params) > 0 {
|
||||
@ -328,20 +338,33 @@ func (view *View) ParseContent(content string, params Params, funcmap...map[stri
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// 设置模板变量解析分隔符号
|
||||
// SetDelimiters sets customized delimiters for template parsing.
|
||||
func (view *View) SetDelimiters(left, right string) {
|
||||
view.delimiters[0] = left
|
||||
view.delimiters[1] = right
|
||||
}
|
||||
|
||||
// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
// BindFunc registers customized template function named <name>
|
||||
// with given function <function> to current view object.
|
||||
// The <name> is the function name which can be called in template content.
|
||||
func (view *View) BindFunc(name string, function interface{}) {
|
||||
view.mu.Lock()
|
||||
view.funcmap[name] = function
|
||||
view.mu.Unlock()
|
||||
}
|
||||
|
||||
// 模板内置方法:include
|
||||
// BindFuncMap registers customized template functions by map to current view object.
|
||||
// The key of map is the template function name
|
||||
// and the value of map is the address of customized function.
|
||||
func (view *View) BindFuncMap(funcMap FuncMap) {
|
||||
view.mu.Lock()
|
||||
for k, v := range funcMap {
|
||||
view.funcmap[k] = v
|
||||
}
|
||||
view.mu.Unlock()
|
||||
}
|
||||
|
||||
// Build-in template function: include
|
||||
func (view *View) funcInclude(file string, data...map[string]interface{}) string {
|
||||
var m map[string]interface{} = nil
|
||||
if len(data) > 0 {
|
||||
@ -354,27 +377,27 @@ func (view *View) funcInclude(file string, data...map[string]interface{}) string
|
||||
return string(content)
|
||||
}
|
||||
|
||||
// 模板内置方法:text
|
||||
// Build-in template function: text
|
||||
func (view *View) funcText(html interface{}) string {
|
||||
return ghtml.StripTags(gconv.String(html))
|
||||
}
|
||||
|
||||
// 模板内置方法:html
|
||||
// Build-in template function: html
|
||||
func (view *View) funcHtmlEncode(html interface{}) string {
|
||||
return ghtml.Entities(gconv.String(html))
|
||||
}
|
||||
|
||||
// 模板内置方法:htmldecode
|
||||
// Build-in template function: htmldecode
|
||||
func (view *View) funcHtmlDecode(html interface{}) string {
|
||||
return ghtml.EntitiesDecode(gconv.String(html))
|
||||
}
|
||||
|
||||
// 模板内置方法:url
|
||||
// Build-in template function: url
|
||||
func (view *View) funcUrlEncode(url interface{}) string {
|
||||
return gurl.Encode(gconv.String(url))
|
||||
}
|
||||
|
||||
// 模板内置方法:urldecode
|
||||
// Build-in template function: urldecode
|
||||
func (view *View) funcUrlDecode(url interface{}) string {
|
||||
if content, err := gurl.Decode(gconv.String(url)); err == nil {
|
||||
return content
|
||||
@ -383,7 +406,7 @@ func (view *View) funcUrlDecode(url interface{}) string {
|
||||
}
|
||||
}
|
||||
|
||||
// 模板内置方法:date
|
||||
// Build-in template function: date
|
||||
func (view *View) funcDate(format string, timestamp...interface{}) string {
|
||||
t := int64(0)
|
||||
if len(timestamp) > 0 {
|
||||
@ -395,42 +418,42 @@ func (view *View) funcDate(format string, timestamp...interface{}) string {
|
||||
return gtime.NewFromTimeStamp(t).Format(format)
|
||||
}
|
||||
|
||||
// 模板内置方法:compare
|
||||
// Build-in template function: compare
|
||||
func (view *View) funcCompare(value1, value2 interface{}) int {
|
||||
return strings.Compare(gconv.String(value1), gconv.String(value2))
|
||||
}
|
||||
|
||||
// 模板内置方法:substr
|
||||
// Build-in template function: substr
|
||||
func (view *View) funcSubStr(start, end int, str interface{}) string {
|
||||
return gstr.SubStr(gconv.String(str), start, end)
|
||||
}
|
||||
|
||||
// 模板内置方法:strlimit
|
||||
// Build-in template function: strlimit
|
||||
func (view *View) funcStrLimit(length int, suffix string, str interface{}) string {
|
||||
return gstr.StrLimit(gconv.String(str), length, suffix)
|
||||
}
|
||||
|
||||
// 模板内置方法:highlight
|
||||
// Build-in template function: highlight
|
||||
func (view *View) funcHighlight(key string, color string, str interface{}) string {
|
||||
return gstr.Replace(gconv.String(str), key, fmt.Sprintf(`<span style="color:%s;">%s</span>`, color, key))
|
||||
}
|
||||
|
||||
// 模板内置方法:hidestr
|
||||
// Build-in template function: hidestr
|
||||
func (view *View) funcHideStr(percent int, hide string, str interface{}) string {
|
||||
return gstr.HideStr(gconv.String(str), percent, hide)
|
||||
}
|
||||
|
||||
// 模板内置方法:toupper
|
||||
// Build-in template function: toupper
|
||||
func (view *View) funcToUpper(str interface{}) string {
|
||||
return gstr.ToUpper(gconv.String(str))
|
||||
}
|
||||
|
||||
// 模板内置方法:toupper
|
||||
// Build-in template function: toupper
|
||||
func (view *View) funcToLower(str interface{}) string {
|
||||
return gstr.ToLower(gconv.String(str))
|
||||
}
|
||||
|
||||
// 模板内置方法:nl2br
|
||||
// Build-in template function: nl2br
|
||||
func (view *View) funcNl2Br(str interface{}) string {
|
||||
return gstr.Nl2Br(gconv.String(str))
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ func Export(i...interface{}) string {
|
||||
// 这里强制对所有map进行反射处理转换
|
||||
refValue := reflect.ValueOf(v)
|
||||
if refValue.Kind() == reflect.Map {
|
||||
m := make(map[string]interface{})
|
||||
m := make(map[string]interface{})
|
||||
keys := refValue.MapKeys()
|
||||
for _, k := range keys {
|
||||
m[gconv.String(k.Interface())] = refValue.MapIndex(k).Interface()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gcron"
|
||||
"time"
|
||||
)
|
||||
@ -12,7 +12,9 @@ func test() {
|
||||
|
||||
func main() {
|
||||
_, err := gcron.Add("*/10 * * * * ?", test)
|
||||
fmt.Println(err)
|
||||
fmt.Println(gcron.Entries())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.Dump(gcron.Entries())
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
|
||||
12
geg/os/glog/glog_writer.go
Normal file
12
geg/os/glog/glog_writer.go
Normal file
@ -0,0 +1,12 @@
|
||||
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"))
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gview"
|
||||
)
|
||||
|
||||
// 用于测试的内置函数
|
||||
func funcTest() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
func main() {
|
||||
view := g.View()
|
||||
b, err := view.Parse("index.html", nil, gview.FuncMap{
|
||||
"test": funcTest,
|
||||
})
|
||||
fmt.Println(err)
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
23
geg/os/gview/bind_func/gview_func1.go
Normal file
23
geg/os/gview/bind_func/gview_func1.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gview"
|
||||
)
|
||||
|
||||
// 用于测试的内置函数
|
||||
func funcTest() string {
|
||||
return "test content"
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 解析模板的时候传递模板函数映射Map,仅会在当前模板解析生效
|
||||
parsed, err := g.View().ParseContent(`call build-in function test: {{test}}`, nil, gview.FuncMap {
|
||||
"test": funcTest,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(parsed))
|
||||
}
|
||||
31
geg/os/gview/bind_func/gview_func2.go
Normal file
31
geg/os/gview/bind_func/gview_func2.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
)
|
||||
|
||||
// 用于测试的带参数的内置函数
|
||||
func funcHello(name string) string {
|
||||
return fmt.Sprintf(`Hello %s`, name)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 绑定全局的模板函数
|
||||
g.View().BindFunc("hello", funcHello)
|
||||
|
||||
// 普通方式传参
|
||||
parsed1, err := g.View().ParseContent(`{{hello "GoFrame"}}`, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(parsed1))
|
||||
|
||||
// 通过管道传参
|
||||
parsed2, err := g.View().ParseContent(`{{"GoFrame" | hello}}`, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(parsed2))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user