github.com/eframework-cn/EP.GO.UTIL@v1.0.0/xrun/xrun.go (about)

     1  //-----------------------------------------------------------------------//
     2  //                     GNU GENERAL PUBLIC LICENSE                        //
     3  //                        Version 2, June 1991                           //
     4  //                                                                       //
     5  // Copyright (C) EFramework, https://eframework.cn, All rights reserved. //
     6  // Everyone is permitted to copy and distribute verbatim copies          //
     7  // of this license document, but changing it is not allowed.             //
     8  //                   SEE LICENSE.md FOR MORE DETAILS.                    //
     9  //-----------------------------------------------------------------------//
    10  
    11  // 协程上层封装,包括异常捕捉、闪退重启、消耗时间、调用堆栈等功能.
    12  package xrun
    13  
    14  import (
    15  	"bytes"
    16  	"fmt"
    17  	"os"
    18  	"reflect"
    19  	"runtime"
    20  
    21  	"github.com/eframework-cn/EP.GO.UTIL/xfs"
    22  	"github.com/eframework-cn/EP.GO.UTIL/xlog"
    23  	"github.com/eframework-cn/EP.GO.UTIL/xtime"
    24  )
    25  
    26  const (
    27  	UNKONWN_SOURCE = "[?]"
    28  )
    29  
    30  // 获取函数调用堆栈信息
    31  //	stack: 堆栈层级
    32  //	fullpath: 全路径
    33  func Caller(stack int, fullpath bool) string {
    34  	if pc, file, line, ok := runtime.Caller(stack + 1); ok {
    35  		if fullpath {
    36  			return fmt.Sprintf("[%s:%d (0x%v)]", file, line, pc)
    37  		} else {
    38  			return fmt.Sprintf("[%s:%d (0x%v)]", runtime.FuncForPC(pc).Name(), line, pc)
    39  		}
    40  	}
    41  	return UNKONWN_SOURCE
    42  }
    43  
    44  // 获取错误堆栈信息
    45  //	stack: 堆栈层级
    46  //	err: 错误信息
    47  func StackTrace(stack int, err interface{}) (string, int) {
    48  	buf := new(bytes.Buffer)
    49  	fmt.Fprintf(buf, "%v\n", err)
    50  	start := stack + 1
    51  	count := stack
    52  	fmt.Fprintf(buf, "    skip %v\n", stack)
    53  	for i := start; ; i++ {
    54  		line := Caller(i, true)
    55  		if line == UNKONWN_SOURCE {
    56  			break
    57  		}
    58  		count++
    59  		fmt.Fprintf(buf, "    %v\n", line)
    60  	}
    61  	return buf.String(), count
    62  }
    63  
    64  // 计算函数执行消耗的时间,在起始处调用defer Elapse(stack)()
    65  //	stack: 堆栈层级(0为当前层)
    66  func Elapse(stack int, callback ...func()) func() {
    67  	start := xtime.GetMillisecond()
    68  	return func() {
    69  		end := xtime.GetMillisecond()
    70  		elapse := end - start
    71  		if stack < 0 {
    72  			stack = 0
    73  		}
    74  		caller := Caller(stack+1, false)
    75  		xlog.Notice("xrun.Elapse%v: start-%v, end-%v, elapsed-%vms", caller, start, end, elapse)
    76  		if len(callback) == 1 {
    77  			callback[0]()
    78  		}
    79  	}
    80  }
    81  
    82  func doCaught(exit bool, stack int, handler ...func(string, int)) {
    83  	if err := recover(); err != nil {
    84  		str, count := StackTrace(stack+1, err)
    85  		fname := fmt.Sprintf("log/crash/%v.log", xtime.Format(xtime.GetTimestamp(), xtime.STRING_TIME_FORMAT_FILE))
    86  		xfs.PathExist(xfs.Directory(fname), true)
    87  		xfs.WriteString(fname, str)
    88  		xlog.Critical(str)
    89  		if len(handler) == 1 {
    90  			handler[0](str, count)
    91  		}
    92  		if exit {
    93  			xlog.Critical("exit now!")
    94  			xlog.Close()
    95  			os.Exit(1)
    96  		}
    97  	}
    98  }
    99  
   100  // 捕捉goroutine的异常
   101  //	exit: 是否结束进程
   102  //	handler: 回调
   103  func Caught(exit bool, handler ...func(string, int)) {
   104  	doCaught(exit, 0, handler...)
   105  }
   106  
   107  // 协程封装器(panic不会引起crash,recover后该goroutine结束)
   108  func Exec(fun interface{}, params ...interface{}) {
   109  	defer Caught(false)
   110  	vf := reflect.ValueOf(fun)
   111  	vps := make([]reflect.Value, len(params))
   112  	for i := 0; i < len(params); i++ {
   113  		param := params[i]
   114  		vps[i] = reflect.ValueOf(param)
   115  	}
   116  	vf.Call(vps)
   117  }
   118  
   119  func doRun(fun interface{}, stack int, params ...interface{}) {
   120  	defer doCaught(false, stack, func(s string, i int) {
   121  		doRun(fun, i, params...)
   122  	})
   123  	vf := reflect.ValueOf(fun)
   124  	vps := make([]reflect.Value, len(params))
   125  	for i := 0; i < len(params); i++ {
   126  		vps[i] = reflect.ValueOf(params[i])
   127  	}
   128  	vf.Call(vps)
   129  }
   130  
   131  // 协程封装器(panic不会引起crash,recover后重启该goroutine)
   132  func Run(fun interface{}, params ...interface{}) {
   133  	doRun(fun, 0, params...)
   134  }