github.com/influx6/npkg@v0.8.8/nframes/nframes.go (about)

     1  package nframes
     2  
     3  import (
     4  	"path"
     5  	"runtime"
     6  	"strings"
     7  
     8  	"github.com/influx6/npkg"
     9  )
    10  
    11  // series of possible levels
    12  const (
    13  	FATAL     Level = 0x10
    14  	ERROR           = 0x8
    15  	DEBUG           = 0x4
    16  	WARNING         = 0x2
    17  	INFO            = 0x1
    18  	ALL             = INFO | WARNING | DEBUG | ERROR | FATAL
    19  	STACKABLE       = DEBUG | ERROR | FATAL
    20  )
    21  
    22  //**************************************************************
    23  // Level
    24  //**************************************************************
    25  
    26  // Level defines a int type which represent the a giving level of entry for a giving entry.
    27  type Level uint8
    28  
    29  // Text2Level returns Level value for the giving string.
    30  //
    31  // It returns ALL as a default value if it does not know the level string.
    32  func Text2Level(lvl string) Level {
    33  	switch strings.ToLower(lvl) {
    34  	case "FATAL", "fatal":
    35  		return FATAL
    36  	case "warning", "WARNING":
    37  		return WARNING
    38  	case "debug", "DEBUG":
    39  		return DEBUG
    40  	case "error", "ERROR":
    41  		return ERROR
    42  	case "info", "INFO":
    43  		return INFO
    44  	}
    45  	return ALL
    46  }
    47  
    48  // String returns the string version of the Level.
    49  func (l Level) String() string {
    50  	switch l {
    51  	case FATAL:
    52  		return "FATAL"
    53  	case DEBUG:
    54  		return "DEBUG"
    55  	case WARNING:
    56  		return "WARNING"
    57  	case ERROR:
    58  		return "ERROR"
    59  	case INFO:
    60  		return "INFO"
    61  	}
    62  	return "UNKNOWN"
    63  }
    64  
    65  //************************************************************
    66  // Stack Frames
    67  //************************************************************
    68  
    69  func GetCallerName() string {
    70  	pc, _, _, _ := runtime.Caller(1)
    71  	return runtime.FuncForPC(pc).Name()
    72  }
    73  
    74  func GetLineNumberWith(skip int) int {
    75  	_, _, line, _ := runtime.Caller(skip)
    76  	return line
    77  }
    78  
    79  func GetFileNameNameWith(skip int) string {
    80  	_, file, _, _ := runtime.Caller(skip)
    81  	return file
    82  }
    83  
    84  func GetCallerNameWith(skip int) string {
    85  	pc, _, _, _ := runtime.Caller(skip)
    86  	return runtime.FuncForPC(pc).Name()
    87  }
    88  
    89  // GetFrameDetails uses runtime.CallersFrames instead of runtime.FuncForPC
    90  // which in go1.12 misses certain frames, to ensure backward compatibility
    91  // with the previous version, this method is written to provide alternative
    92  // setup that uses the recommended way of go1.12.
    93  func GetFrameDetails(skip int, size int) []FrameDetail {
    94  	var frames = make([]uintptr, size)
    95  	var written = runtime.Callers(skip, frames)
    96  	if written == 0 {
    97  		return nil
    98  	}
    99  
   100  	var details = make([]FrameDetail, 0, len(frames))
   101  
   102  	frames = frames[:written]
   103  	var rframes = runtime.CallersFrames(frames)
   104  	for {
   105  		frame, more := rframes.Next()
   106  		if !more {
   107  			break
   108  		}
   109  
   110  		var detail FrameDetail
   111  		detail.File = frame.File
   112  		detail.Line = frame.Line
   113  		detail.Method = frame.Function
   114  		detail.FileName, detail.Package = fileToPackageAndFilename(frame.File)
   115  
   116  		details = append(details, detail)
   117  	}
   118  	return details
   119  }
   120  
   121  // GetFrames returns a slice of stack frames for a giving size, skipping the provided
   122  // `skip` count.
   123  func GetFrames(skip int, size int) []uintptr {
   124  	var frames = make([]uintptr, size)
   125  	var written = runtime.Callers(skip, frames)
   126  	return frames[:written]
   127  }
   128  
   129  // Frames is a slice of pointer uints.
   130  type Frames []uintptr
   131  
   132  // Details returns a slice of FrameDetails describing with a snapshot
   133  // of giving stack frame pointer details.
   134  func (f Frames) Details() []FrameDetail {
   135  	var details = make([]FrameDetail, len(f))
   136  	for ind, ptr := range f {
   137  		details[ind] = Frame(ptr).Detail()
   138  	}
   139  	return details
   140  }
   141  
   142  // Encode encodes all Frames within slice into provided object encoder with keyname "_stack_frames".
   143  func (f Frames) Encode(encoder npkg.ObjectEncoder) {
   144  	encoder.ListFor("_stack_frames", f.EncodeList)
   145  }
   146  
   147  // EncodeList encodes all Frames within slice into provided list encoder.
   148  func (f Frames) EncodeList(encoder npkg.ListEncoder) {
   149  	for _, frame := range f {
   150  		var fr = Frame(frame)
   151  		encoder.AddObject(fr)
   152  	}
   153  }
   154  
   155  // Frame represents a program counter inside a stack frame.
   156  // For historical reasons if Frame is interpreted as a uintptr
   157  // its value represents the program counter + 1.
   158  type Frame uintptr
   159  
   160  // FrameDetail represent the snapshot description of a
   161  // Frame pointer.
   162  type FrameDetail struct {
   163  	Line     int
   164  	Method   string
   165  	File     string
   166  	Package  string
   167  	FileName string
   168  }
   169  
   170  const srcSub = "/src/"
   171  
   172  // EncodeObject encodes giving frame into provided encoder.
   173  func (f Frame) EncodeObject(encode npkg.ObjectEncoder) {
   174  	fn := runtime.FuncForPC(f.Pc())
   175  	if fn == nil {
   176  		return
   177  	}
   178  
   179  	encode.String("method", fn.Name())
   180  
   181  	var file, line = fn.FileLine(f.Pc())
   182  	if line >= 0 {
   183  		encode.Int("line", line)
   184  	}
   185  
   186  	if file != "" && file != "???" {
   187  		encode.String("file", file)
   188  
   189  		var fileName, pkgName = fileToPackageAndFilename(file)
   190  		encode.String("file_name", fileName)
   191  		encode.String("package", pkgName)
   192  	}
   193  }
   194  
   195  const winSlash = '\\'
   196  
   197  func toSlash(s string) string {
   198  	for index, item := range s {
   199  		if item == winSlash {
   200  			s = s[:index] + "/" + s[index+1:]
   201  		}
   202  	}
   203  	return s
   204  }
   205  
   206  func fileToPackageAndFilename(file string) (filename, pkg string) {
   207  	if runtime.GOOS == "windows" {
   208  		file = toSlash(file)
   209  	}
   210  
   211  	var pkgIndex = strings.Index(file, srcSub)
   212  	if pkgIndex != -1 {
   213  		var pkgFileBase = file[pkgIndex+5:]
   214  		if lastSlash := strings.LastIndex(pkgFileBase, "/"); lastSlash != -1 {
   215  			filename = pkgFileBase[lastSlash+1:]
   216  			pkg = pkgFileBase[:lastSlash]
   217  		}
   218  	}
   219  	return
   220  }
   221  
   222  // Pc returns the program counter for this frame;
   223  // multiple frames may have the same PC value.
   224  func (f Frame) Pc() uintptr { return uintptr(f) - 1 }
   225  
   226  // Detail returns the detail for giving Frame pointer.
   227  func (f Frame) Detail() FrameDetail {
   228  	var detail = FrameDetail{
   229  		Method: "Unknown",
   230  		File:   "...",
   231  		Line:   -1,
   232  	}
   233  
   234  	fn := runtime.FuncForPC(f.Pc())
   235  	if fn == nil {
   236  		return detail
   237  	}
   238  
   239  	detail.Method = fn.Name()
   240  	var file, line = fn.FileLine(f.Pc())
   241  	if line >= 0 {
   242  		detail.Line = line
   243  	}
   244  	if file != "" && file != "???" {
   245  		detail.File = file
   246  	}
   247  	if detail.File != "..." {
   248  		pkgPieces := strings.SplitAfter(detail.File, "/src/")
   249  		var pkgFileBase string
   250  		if len(pkgPieces) > 1 {
   251  			pkgFileBase = pkgPieces[1]
   252  		}
   253  
   254  		detail.Package = path.Dir(pkgFileBase)
   255  		detail.FileName = path.Base(pkgFileBase)
   256  	}
   257  	return detail
   258  }
   259  
   260  // File returns the full path to the file that contains the
   261  // function for this Frame's pc.
   262  func (f Frame) File() string {
   263  	fn := runtime.FuncForPC(f.Pc())
   264  	if fn == nil {
   265  		return "unknown"
   266  	}
   267  	file, _ := fn.FileLine(f.Pc())
   268  	return file
   269  }
   270  
   271  // Line returns the line number of source code of the
   272  // function for this Frame's pc.
   273  func (f Frame) Line() int {
   274  	fn := runtime.FuncForPC(f.Pc())
   275  	if fn == nil {
   276  		return 0
   277  	}
   278  	_, line := fn.FileLine(f.Pc())
   279  	return line
   280  }
   281  
   282  // Name returns the name of this function, if known.
   283  func (f Frame) Name() string {
   284  	fn := runtime.FuncForPC(f.Pc())
   285  	if fn == nil {
   286  		return "unknown"
   287  	}
   288  	return fn.Name()
   289  }