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 }