Compare commits

..

17 Commits

32 changed files with 1002 additions and 792 deletions

View File

@ -1,5 +1,6 @@
# GoFrame
<img align="right" height="150px" src="https://goframe.org/cover.png">
<div align=center>
<img src="https://goframe.org/logo.png" width="100"/>
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
@ -8,12 +9,15 @@
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
</div>
<!--
GoFrame is a modular, loose-coupled, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: cache, logging, containers, timer, validator, database orm, etc. Supporting web server integrated with router, cookie, session, logger, configure, template, https, hooks, rewrites and many more features.
-->
`GF(GoFrame)` is a modular, loose-coupled, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: memcache, configure, validator, logging, array/queue/set/map containers, timer/timing tasks, file/memory lock, object pool, database ORM, etc. Supporting web server integrated with router, cookie, session, logger, template, https, hooks, rewrites and many more features.
# Installation
```
go get -u github.com/gogf/gf
@ -25,7 +29,7 @@ require github.com/gogf/gf latest
# Limitation
```
golang version >= 1.9.2
golang version >= 1.10
```
# Documentation
@ -64,7 +68,6 @@ func main() {
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
# Contributors
- [aloncn](https://github.com/aloncn)
@ -87,6 +90,8 @@ func main() {
# Donators
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](https://goframe.org/images/donate.png)?
- [flyke-xu](https://gitee.com/flyke-xu)
- [hailaz](https://gitee.com/hailaz)
- [ireadx](https://github.com/ireadx)
@ -102,3 +107,12 @@ func main() {
# Sponsor
We appreciate any kind of sponsorship for `GF` development. If you've got some interested, please contact john@goframe.org.

View File

@ -1,5 +1,6 @@
# GoFrame
<img align="right" height="150px" src="https://goframe.org/cover.png">
<div align=center>
<img src="https://goframe.org/logo.png" width="100"/>
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
@ -8,6 +9,8 @@
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
</div>
`GF(Go Frame)`是一款模块化、松耦合、生产级Go应用开发框架。提供了常用的核心开发组件缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
并发安全容器等等。并提供了Web服务开发的系列核心组件Router、Cookie、Session、服务注册、配置管理、模板引擎等等支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
@ -33,7 +36,7 @@ require github.com/gogf/gf latest
# 限制
```shell
golang版本 >= 1.9.2
golang版本 >= 1.10
```
# 架构
@ -76,12 +79,12 @@ func main() {
# 捐赠
捐赠支持`GF`框架的研发,
请在捐赠时备注您的`github`/`gitee`账号名称。
如果您喜欢`GF`,要不[给开发者来杯咖啡吧](https://goframe.org/images/donate.png)
请在捐赠时备注您的`github`/`gitee`账号名称。
<a href="https://goframe.org/images/donate.png" target="_blank">
<img src="https://goframe.org/images/donate.png" width="300"/>
</a>
# 赞助
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 john@goframe.org 。
# 贡献者

View File

@ -64,29 +64,44 @@ func (j *Json) GetMap(pattern string, def...interface{}) map[string]interface{}
}
// GetJson gets the value by specified <pattern>,
// 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 <pattern>,
// 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 <pattern>,
// 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 <pattern>,
// and converts it to a slice of []interface{}.
func (j *Json) GetArray(pattern string, def...interface{}) []interface{} {

View File

@ -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")
})
}

View File

@ -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,

View File

@ -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日志功能

View File

@ -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)
}
// 处理服务错误信息主要是panichttp请求的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)
}

View File

@ -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
}

View File

@ -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, <font color=red>red</font>"))
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,<font color=red>red</font>"))
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))
}

View File

@ -24,7 +24,7 @@ type Conn struct {
const (
gDEFAULT_RETRY_INTERVAL = 100 // (毫秒)默认重试时间间隔
gDEFAULT_READ_BUFFER_SIZE = 1024 // 默认数据读取缓冲区大小
gDEFAULT_READ_BUFFER_SIZE = 64 // (KB)默认数据读取缓冲区大小
gRECV_ALL_WAIT_TIMEOUT = time.Millisecond // 读取全部缓冲数据时,没有缓冲数据时的等待间隔
)
@ -88,7 +88,8 @@ func (c *Conn) Send(data []byte, retry...Retry) error {
}
}
// 接收数据
// 接收数据.
// 注意UDP协议存在消息边界因此使用 length<=0 可以获取缓冲区所有消息包数据,即一个完整包。
func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
var err error // 读取错误
var size int // 读取长度

View File

@ -1,115 +0,0 @@
// Copyright 2019 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 gudp
import (
"encoding/binary"
"errors"
"fmt"
"time"
)
const (
// 允许最大的简单协议包大小(byte), 15MB
PKG_MAX_SIZE = 0xFFFFFF
// 消息包头大小: "总长度"3字节+"校验码"4字节
PKG_HEADER_SIZE = 7
)
// 根据简单协议发送数据包。
// 简单协议数据格式:总长度(24bit)|校验码(32bit)|数据(变长)。
// 注意:
// 1. "总长度"包含自身3字节及"校验码"4字节。
// 2. 由于"总长度"为3字节并且使用的BigEndian字节序因此最后返回的buffer使用了buffer[1:]。
func (c *Conn) SendPkg(data []byte, retry...Retry) error {
length := uint32(len(data))
if length > PKG_MAX_SIZE - PKG_HEADER_SIZE {
return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, PKG_MAX_SIZE - PKG_HEADER_SIZE))
}
buffer := make([]byte, PKG_HEADER_SIZE + 1 + len(data))
copy(buffer[PKG_HEADER_SIZE + 1 : ], data)
binary.BigEndian.PutUint32(buffer[0 : ], PKG_HEADER_SIZE + length)
binary.BigEndian.PutUint32(buffer[4 : ], Checksum(data))
//fmt.Println("SendPkg:", buffer[1:])
return c.Send(buffer[1:], retry...)
}
// 简单协议: 带超时时间的数据发送
func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
c.SetSendDeadline(time.Now().Add(timeout))
defer c.SetSendDeadline(time.Time{})
return c.SendPkg(data, retry...)
}
// 简单协议: 发送数据并等待接收返回数据
func (c *Conn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkg(retry...)
} else {
return nil, err
}
}
// 简单协议: 发送数据并等待接收返回数据(带返回超时等待时间)
func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
if err := c.SendPkg(data, retry...); err == nil {
return c.RecvPkgWithTimeout(timeout, retry...)
} else {
return nil, err
}
}
// 简单协议: 获取一个数据包。
func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) {
var temp []byte
var length uint32
for {
// 先根据对象的缓冲区数据进行计算
for {
if len(c.buffer) >= PKG_HEADER_SIZE {
// 注意"总长度"为3个字节不满足4个字节的uint32类型因此这里"低位"补0
length = binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]})
// 解析的大小是否符合规范
if length == 0 || length + PKG_HEADER_SIZE > PKG_MAX_SIZE {
c.buffer = c.buffer[1:]
continue
}
// 不满足包大小,需要继续读取
if uint32(len(c.buffer)) < length {
break
}
// 数据校验
if binary.BigEndian.Uint32(c.buffer[3 : PKG_HEADER_SIZE]) != Checksum(c.buffer[PKG_HEADER_SIZE : length]) {
c.buffer = c.buffer[1:]
continue
}
result = c.buffer[PKG_HEADER_SIZE : length]
c.buffer = c.buffer[length: ]
return
} else {
break
}
}
// 读取系统socket缓冲区的完整数据
temp, err = c.Recv(-1, retry...)
if err != nil {
break
}
if len(temp) > 0 {
c.buffer = append(c.buffer, temp...)
}
//fmt.Println("RecvPkg:", c.buffer)
}
return
}
// 简单协议: 带超时时间的消息包获取
func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) {
c.SetRecvDeadline(time.Now().Add(timeout))
defer c.SetRecvDeadline(time.Time{})
return c.RecvPkg(retry...)
}

View File

@ -1,49 +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 gudp
import "time"
// 简单协议: (面向短链接)发送消息包
func SendPkg(addr string, data []byte, retry...Retry) error {
conn, err := NewConn(addr)
if err != nil {
return err
}
defer conn.Close()
return conn.SendPkg(data, retry...)
}
// 简单协议: (面向短链接)发送数据并等待接收返回数据
func SendRecvPkg(addr string, data []byte, retry...Retry) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecvPkg(data, retry...)
}
// 简单协议: (面向短链接)带超时时间的数据发送
func SendPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) error {
conn, err := NewConn(addr)
if err != nil {
return err
}
defer conn.Close()
return conn.SendPkgWithTimeout(data, timeout, retry...)
}
// 简单协议: (面向短链接)发送数据并等待接收返回数据(带返回超时等待时间)
func SendRecvPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
conn, err := NewConn(addr)
if err != nil {
return nil, err
}
defer conn.Close()
return conn.SendRecvPkgWithTimeout(data, timeout, retry...)
}

View File

@ -5,8 +5,6 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gfile provides easy-to-use operations for file system.
//
// 文件管理.
package gfile
import (
@ -15,6 +13,7 @@ import (
"fmt"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"io"
"os"
@ -452,11 +451,24 @@ func MainPkgPath() string {
}
for i := 1; i < 10000; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if gregex.IsMatchString(`package\s+main`, GetContents(file)) {
path = Dir(file)
mainPkgPath.Set(path)
return path
if gstr.Contains(file, "/gf/g/") {
continue
}
if Ext(file) != ".go" {
continue
}
path = file
for path != "/" && gstr.Contains(path, "/") {
files, _ := ScanDir(path, "*.go")
for _, v := range files {
if gregex.IsMatchString(`package\s+main`, GetContents(v)) {
mainPkgPath.Set(path)
return path
}
}
path = Dir(path)
}
} else {
break
}

View File

@ -653,8 +653,8 @@ func TestMkdir(t *testing.T) {
func TestStat(t *testing.T) {
gtest.Case(t, func() {
var (
tpath1 string = "/testfile_t1.txt"
tpath2 string = "./testfile_t1_no.txt"
tpath1 = "/testfile_t1.txt"
tpath2 = "./testfile_t1_no.txt"
err error
fileiofo os.FileInfo
)
@ -675,12 +675,7 @@ func TestStat(t *testing.T) {
func TestMainPkgPath(t *testing.T) {
gtest.Case(t, func() {
var (
reads string
)
reads = gfile.MainPkgPath()
reads := gfile.MainPkgPath()
gtest.Assert(reads, "")
})
}

View File

@ -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 <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
@ -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 <writer> 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 <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)
}
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...)
}

162
g/os/glog/glog_api.go Normal file
View File

@ -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 <v> with newline using fmt.Sprintln.
// The param <v> can be multiple variables.
func Print(v ...interface{}) {
logger.Print(v ...)
}
// Printf prints <v> with format <format> using fmt.Sprintf.
// The param <v> 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 <v> with newline and format <format> using fmt.Sprintf.
// The param <v> 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...)
}

View File

@ -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 <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)
}
// 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 <long> specified whether print the long absolute file path, eg: /a/b/c/d.go:23.
func Line(long...bool) *Logger {
return logger.Line(long...)
}

View File

@ -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 <writer> for logging.
@ -132,26 +144,21 @@ func (l *Logger) SetBacktraceSkip(skip int) {
// Developer can use customized logging <writer> 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 <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)
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 <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) {
// 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 <s> to <std> 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 <s> without backtrace.
func (l *Logger) stdPrint(s string) {
// printStd prints content <s> without backtrace.
func (l *Logger) printStd(s string) {
l.print(os.Stdout, s)
}
// stdPrint prints content <s> with backtrace check.
func (l *Logger) errPrint(s string) {
if l.btStatus.Val() == 1 {
// printStd prints content <s> 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(`<autogenerated>`, 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(`<autogenerated>`, 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 <level> could be output.
func (l *Logger) checkLevel(level int) bool {
return l.level.Val() & level > 0
}

View File

@ -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 <v> with newline using fmt.Sprintln.
// The param <v> can be multiple variables.
func (l *Logger) Print(v ...interface{}) {
l.printStd(fmt.Sprintln(v...))
}
// Printf prints <v> with format <format> using fmt.Sprintf.
// The param <v> 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 <v> with newline and format <format> using fmt.Sprintf.
// The param <v> 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 <level> could be output.
func (l *Logger) checkLevel(level int) bool {
return l.level & level > 0
}

View File

@ -15,7 +15,7 @@ import (
// which redirects current logging content output to the specified <writer>.
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 <path> 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 <category> 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 <pattern> 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 <long> 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
}

View File

@ -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
}

View File

@ -11,7 +11,7 @@ func main() {
l := glog.New()
path := "/tmp/glog"
l.SetPath(path)
l.SetStdPrint(false)
l.SetStdoutPrint(false)
// 使用默认文件名称格式
l.Println("标准文件名称格式,使用当前时间时期")
// 通过SetFile设置文件名称格式

15
geg/os/glog/glog_flags.go Normal file
View File

@ -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")
}

10
geg/os/glog/glog_line.go Normal file
View File

@ -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")
}

14
geg/os/glog/glog_line2.go Normal file
View File

@ -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")
}

View File

@ -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")
}

View File

@ -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()
}

View File

@ -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"))
}

View File

@ -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")
//}

View File

@ -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")
}

View File

@ -4,9 +4,17 @@ import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
"log"
"os"
)
func main() {
var mylog = log.New(os.Stdout, "[Api] ", log.LstdFlags|log.Lshortfile)
mylog.Println(123)
return
a := []int{1,2,3}
fmt.Println(a[:0])
return
type Person struct{
Name string
}

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.6.13"
const VERSION = "v1.6.15"
const AUTHORS = "john<john@goframe.org>"