完成Web Server路由表打印/获取特性

This commit is contained in:
john
2018-10-18 13:43:00 +08:00
parent 3b3b6ac35d
commit 18c1d84ff5
12 changed files with 121 additions and 58 deletions

View File

@ -80,7 +80,7 @@ func View(name...string) *gview.View {
}
view := gview.Get(path)
// 添加基于源码的搜索目录检索地址,常用于开发环境调试,只添加入口文件目录
if p := gfile.MainPkgPath(); gfile.Exists(p) {
if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) {
view.AddPath(p)
}
// 框架内置函数
@ -107,7 +107,7 @@ func Config(file...string) *gcfg.Config {
}
config := gcfg.New(path, configFile)
// 添加基于源码的搜索目录检索地址,常用于开发环境调试,只添加入口文件目录
if p := gfile.MainPkgPath(); gfile.Exists(p) {
if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) {
config.AddPath(p)
}
return config

1
g/g.go
View File

@ -21,4 +21,3 @@ type List = []Map
// 常用slice数据结构(使用别名)
type Slice = []interface{}
type Array = Slice

View File

@ -216,7 +216,7 @@ func (s *Server) Start() error {
s.paths.Set(s.config.ServerRoot)
}
s.paths.Add(gfile.SelfDir())
if p := gfile.MainPkgPath(); gfile.Exists(p) {
if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) {
s.paths.Add(p)
}
@ -271,11 +271,16 @@ func (s *Server) Start() error {
// 打印展示路由表
func (s *Server) DumpRoutesMap() {
fmt.Println(s.GetRoutesMap())
if s.config.DumpRouteMap {
// (等待一定时间后)当所有框架初始化信息打印完毕之后才打印路由表信息
gtime.SetTimeout(50*time.Millisecond, func() {
glog.Header(false).Println(fmt.Sprintf("\n%s\n", s.GetRouteMap()))
})
}
}
// 获得路由表(格式化字符串)
func (s *Server) GetRoutesMap() string {
func (s *Server) GetRouteMap() string {
type tableItem struct {
hook string
domain string
@ -286,7 +291,7 @@ func (s *Server) GetRoutesMap() string {
buf := bytes.NewBuffer(nil)
table := tablewriter.NewWriter(buf)
table.SetHeader([]string{"DOMAIN", "METHOD", "ROUTE", "HANDLER", "HOOK"})
table.SetHeader([]string{"SERVER", "ADDRESS", "DOMAIN", "METHOD", "ROUTE", "HANDLER", "HOOK"})
table.SetRowLine(true)
table.SetBorder(false)
table.SetCenterSeparator("|")
@ -319,15 +324,17 @@ func (s *Server) GetRoutesMap() string {
m[item.domain].Add(item)
}
for _, a := range m {
s := make([]string, 5)
data := make([]string, 7)
for _, v := range a.Slice() {
item := v.(*tableItem)
s[0] = item.domain
s[1] = item.method
s[2] = item.route
s[3] = item.handler
s[4] = item.hook
table.Append(s)
data[0] = s.name
data[1] = s.config.Addr
data[2] = item.domain
data[3] = item.method
data[4] = item.route
data[5] = item.handler
data[6] = item.hook
table.Append(data)
}
}
table.Render()

View File

@ -76,6 +76,7 @@ type ServerConfig struct {
// 其他设置
NameToUriType int // 服务注册时对象和方法名称转换为URI时的规则
GzipContentTypes []string // 允许进行gzip压缩的文件类型
DumpRouteMap bool // 是否在程序启动时默认打印路由表信息
}
// 默认HTTP Server
@ -102,6 +103,8 @@ var defaultServerConfig = ServerConfig {
ErrorLogEnabled : true,
GzipContentTypes : defaultGzipContentTypes,
DumpRouteMap : true,
}
// 获取默认的http server设置
@ -290,6 +293,14 @@ func (s *Server) SetNameToUriType(t int) {
s.config.NameToUriType = t
}
// 是否在程序启动时打印路由表信息
func (s *Server) SetDumpRouteMap(enabled bool) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
}
s.config.DumpRouteMap = enabled
}
// 添加静态文件搜索目录,必须给定目录的绝对路径
func (s *Server) AddSearchPath(path string) error {
return s.paths.Add(path)

View File

@ -20,11 +20,11 @@ import (
"os/user"
"runtime"
"path/filepath"
"gitee.com/johng/gf/g/util/gregex"
"gitee.com/johng/gf/g/container/gtype"
"sort"
"gitee.com/johng/gf/g/util/gconv"
"gitee.com/johng/gf/g/os/gfpool"
"gitee.com/johng/gf/g/util/gregex"
)
// 封装了常用的文件操作方法如需更详细的文件控制请查看官方os包
@ -530,7 +530,7 @@ func GetBinContentByTwoOffsets(file *os.File, start int64, end int64) []byte {
}
// 获取入口函数文件所在目录(main包文件目录)
// **仅对源码开发环境有效即仅对生成该可执行文件的系统下有效**
// **仅对源码开发环境有效(即仅对生成该可执行文件的系统下有效)**
func MainPkgPath() string {
path := mainPkgPath.Val()
if path != "" {
@ -542,9 +542,12 @@ func MainPkgPath() string {
if strings.EqualFold("<autogenerated>", file) {
// 如果是通过init包方法进入那么无法得到准确的文件路径
f = ""
} else if !gregex.IsMatchString("^" + GoRootOfBuild(), file) {
// 不包含go源码路径
f = file
} else {
goroot := GoRootOfBuild()
if goroot != "" && !gregex.IsMatchString("^" + GoRootOfBuild(), file) {
// 不包含go源码路径
f = file
}
}
} else {
break
@ -558,7 +561,8 @@ func MainPkgPath() string {
return ""
}
// 编译时环境的GOROOT数值
// 编译时环境的GOROOT数值(对init初始化方法调用时无效获取不了ROOT值)
// 注意:可能返回空
func GoRootOfBuild() string {
if v := goRootOfBuild.Val(); v != "" {
return v

View File

@ -102,6 +102,10 @@ func StdPrint(enabled bool) *Logger {
return logger.StdPrint(enabled)
}
// 是否打印每行日志头信息(默认开启)
func Header(enabled bool) *Logger {
return logger.Header(enabled)
}
func Print(v ...interface{}) {
logger.Print(v ...)
}

View File

@ -32,6 +32,7 @@ type Logger struct {
level *gtype.Int // 日志输出等级
btSkip *gtype.Int // 错误产生时的backtrace回调信息skip条数
btEnabled *gtype.Bool // 是否当打印错误时同时开启backtrace打印
printHeader *gtype.Bool // 是否不打印前缀信息(时间,级别等)
alsoStdPrint *gtype.Bool // 控制台打印开关,当输出到文件/自定义输出时也同时打印到终端
}
@ -40,8 +41,12 @@ const (
gDEFAULT_FILE_POOL_FLAGS = os.O_CREATE|os.O_WRONLY|os.O_APPEND
)
// 默认的日志换行符
var ln = "\n"
var (
// 默认的日志换行符
ln = "\n"
// 标准输出互斥锁,防止标准输出串日志
stdMu = sync.RWMutex{}
)
// 初始化日志换行符
func init() {
@ -59,6 +64,7 @@ func New() *Logger {
level : gtype.NewInt(defaultLevel.Val()),
btSkip : gtype.NewInt(),
btEnabled : gtype.NewBool(true),
printHeader : gtype.NewBool(true),
alsoStdPrint : gtype.NewBool(true),
}
}
@ -67,12 +73,13 @@ func New() *Logger {
func (l *Logger) Clone() *Logger {
return &Logger {
pr : l,
io : l.GetIO(),
io : l.GetWriter(),
path : l.path.Clone(),
file : l.file.Clone(),
level : l.level.Clone(),
btSkip : l.btSkip.Clone(),
btEnabled : l.btEnabled.Clone(),
printHeader : l.printHeader.Clone(),
alsoStdPrint : l.alsoStdPrint.Clone(),
}
}
@ -106,14 +113,14 @@ func (l *Logger) SetBacktraceSkip(skip int) {
}
// 可自定义IO接口IO可以是文件输出、标准输出、网络输出
func (l *Logger) SetIO(w io.Writer) {
func (l *Logger) SetWriter(w io.Writer) {
l.mu.Lock()
l.io = w
l.mu.Unlock()
}
// 返回自定义的IO默认为nil
func (l *Logger) GetIO() io.Writer {
func (l *Logger) GetWriter() io.Writer {
l.mu.RLock()
r := l.io
l.mu.RUnlock()
@ -166,35 +173,44 @@ func (l *Logger) SetStdPrint(enabled bool) {
}
// 这里的写锁保证统一时刻只会写入一行日志,防止串日志的情况
// 注意这里的std io.Writer为值赋值会造成标准输出串日志的情况
func (l *Logger) print(std io.Writer, s string) {
// 优先使用自定义的IO输出
str := l.format(s)
writer := l.GetIO()
if l.printHeader.Val() {
s = l.format(s)
}
writer := l.GetWriter()
if writer == nil {
// 如果设置的IO为空,那么其次判断是否有文件输出设置
// 内部使用了内存锁保证在glog中对同一个日志文件的并发写入不会串日志
// 如果设置的writer为空,那么其次判断是否有文件输出设置
// 内部使用了内存锁保证在glog中对同一个日志文件的并发写入不会串日志(并发安全)
if f := l.getFilePointer(); f != nil {
defer f.Close()
key := l.path.Val()
gmlock.Lock(key)
_, err := io.WriteString(f, str)
_, err := io.WriteString(f, s)
gmlock.Unlock(key)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
}
}
} else {
if _, err := io.WriteString(writer, str); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
}
l.doStdLockPrint(writer, s)
}
// 是否允许输出到标准输出
if l.alsoStdPrint.Val() {
if _, err := io.WriteString(std, str); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
}
l.doStdLockPrint(std, s)
}
}
// 并发安全打印到标准输出
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()
}
// 核心打印数据方法(标准输出)
func (l *Logger) stdPrint(s string) {
l.print(os.Stdout, s)
@ -214,7 +230,8 @@ func (l *Logger) errPrint(s string) {
}
}
}
l.print(os.Stderr, s)
// 防止串日志情况这里不使用stderr而是使用stdout
l.print(os.Stdout, s)
}
// 直接打印回溯信息参数skip表示调用端往上多少级开始回溯
@ -241,10 +258,11 @@ func (l *Logger) GetBacktrace(skip...int) string {
}
}
// 从业务文件开始位置根据自定义的skip开始backtrace
goroot := gfile.GoRootOfBuild()
for i := from + customSkip + l.btSkip.Val(); i < 10000; i++ {
if _, cfile, cline, ok := runtime.Caller(i); ok {
if _, cfile, cline, ok := runtime.Caller(i); ok && cfile != "" {
// 不打印出go源码路径及glog包文件路径日志打印必须从业务源码文件开始且从glog包文件开始检索
if !gregex.IsMatchString("^" + gfile.GoRootOfBuild(), cfile) && !gregex.IsMatchString(`<autogenerated>`, cfile) {
if (goroot == "" || !gregex.IsMatchString("^" + goroot, cfile)) && !gregex.IsMatchString(`<autogenerated>`, cfile) {
backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, cfile, cline, ln)
index++
}

View File

@ -73,4 +73,16 @@ func (l *Logger) StdPrint(enabled bool) *Logger {
}
logger.SetStdPrint(enabled)
return logger
}
// 是否打印每行日志头信息(默认开启)
func (l *Logger) Header(enabled bool) *Logger {
logger := (*Logger)(nil)
if l.pr == nil {
logger = l.Clone()
} else {
logger = l
}
logger.printHeader.Set(enabled)
return logger
}

View File

@ -50,7 +50,7 @@ func (sp *SPath) Set(path string) error {
glog.Debug("gspath.SetPath:", r)
return nil
}
glog.Debug("gspath.SetPath failed:", path)
glog.Warning("gspath.SetPath failed:", path)
return errors.New("invalid path:" + path)
}
@ -71,7 +71,7 @@ func (sp *SPath) Add(path string) error {
glog.Debug("gspath.Add:", r)
return nil
}
glog.Debug("gspath.Add failed:", path)
glog.Warning("gspath.Add failed:", path)
return errors.New("invalid path:" + path)
}

View File

@ -13,6 +13,8 @@ import (
"encoding/json"
"reflect"
"gitee.com/johng/gf/g/util/gconv"
"runtime"
"gitee.com/johng/gf/g/os/glog"
)
// 格式化打印变量(类似于PHP-vardump)
@ -48,3 +50,18 @@ func Dump(i...interface{}) {
}
}
// 打印完整的调用回溯信息
func PrintBacktrace() {
index := 1
buffer := bytes.NewBuffer(nil)
for i := 0; i < 10000; i++ {
if _, cfile, cline, ok := runtime.Caller(i); ok {
buffer.WriteString(fmt.Sprintf(`%d. %s:%d%s`, index, cfile, cline, "\n"))
index++
} else {
break
}
}
glog.Header(false).Print(buffer.String())
}

View File

@ -7,6 +7,9 @@ import (
)
func main() {
//g.Server().SetDumpRouteMap(false)
g.Server().SetPort(8199)
g.Server().Run()
}

View File

@ -1,26 +1,14 @@
package main
import (
"github.com/olekukonko/tablewriter"
"os"
"fmt"
"gitee.com/johng/gf/g/os/gfile"
)
func init() {
fmt.Println(gfile.GoRootOfBuild())
}
func main() {
data := [][]string{
[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
[]string{"1/4/2014", "February Extra Bandwidth1111111111111111111111111111111111", "2233", "$30.00"},
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
table.SetCenterSeparator("|")
table.SetAutoMergeCells(true)
table.SetRowLine(true)
table.SetBorder(false)
table.AppendBulk(data)
table.Render()
}