github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/stack/record.go (about) 1 package stack 2 3 import ( 4 "fmt" 5 "runtime" 6 "strings" 7 8 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring" 9 ) 10 11 type recordOptions struct { 12 packagePath bool 13 packageName bool 14 structName bool 15 functionName bool 16 fileName bool 17 line bool 18 lambdas bool 19 } 20 21 type recordOption func(opts *recordOptions) 22 23 func PackageName(b bool) recordOption { 24 return func(opts *recordOptions) { 25 opts.packageName = b 26 } 27 } 28 29 func FunctionName(b bool) recordOption { 30 return func(opts *recordOptions) { 31 opts.functionName = b 32 } 33 } 34 35 func FileName(b bool) recordOption { 36 return func(opts *recordOptions) { 37 opts.fileName = b 38 } 39 } 40 41 func Line(b bool) recordOption { 42 return func(opts *recordOptions) { 43 opts.line = b 44 } 45 } 46 47 func StructName(b bool) recordOption { 48 return func(opts *recordOptions) { 49 opts.structName = b 50 } 51 } 52 53 func Lambda(b bool) recordOption { 54 return func(opts *recordOptions) { 55 opts.lambdas = b 56 } 57 } 58 59 func PackagePath(b bool) recordOption { 60 return func(opts *recordOptions) { 61 opts.packagePath = b 62 } 63 } 64 65 var _ caller = call{} 66 67 type call struct { 68 function uintptr 69 file string 70 line int 71 } 72 73 func Call(depth int) (c call) { 74 c.function, c.file, c.line, _ = runtime.Caller(depth + 1) 75 76 return c 77 } 78 79 func (c call) Record(opts ...recordOption) string { 80 optionsHolder := recordOptions{ 81 packagePath: true, 82 packageName: true, 83 structName: true, 84 functionName: true, 85 fileName: true, 86 line: true, 87 lambdas: true, 88 } 89 for _, opt := range opts { 90 opt(&optionsHolder) 91 } 92 name := runtime.FuncForPC(c.function).Name() 93 var ( 94 pkgPath string 95 pkgName string 96 structName string 97 funcName string 98 file = c.file 99 ) 100 if i := strings.LastIndex(file, "/"); i > -1 { 101 file = file[i+1:] 102 } 103 if i := strings.LastIndex(name, "/"); i > -1 { 104 pkgPath, name = name[:i], name[i+1:] 105 } 106 split := strings.Split(name, ".") 107 lambdas := make([]string, 0, len(split)) 108 for i := range split { 109 elem := split[len(split)-i-1] 110 if !strings.HasPrefix(elem, "func") { 111 break 112 } 113 lambdas = append(lambdas, elem) 114 } 115 split = split[:len(split)-len(lambdas)] 116 if len(split) > 0 { 117 pkgName = split[0] 118 } 119 if len(split) > 1 { 120 funcName = split[len(split)-1] 121 } 122 if len(split) > 2 { 123 structName = split[1] 124 } 125 126 buffer := xstring.Buffer() 127 defer buffer.Free() 128 if optionsHolder.packagePath { 129 buffer.WriteString(pkgPath) 130 } 131 if optionsHolder.packageName { 132 if buffer.Len() > 0 { 133 buffer.WriteByte('/') 134 } 135 buffer.WriteString(pkgName) 136 } 137 if optionsHolder.structName && len(structName) > 0 { 138 if buffer.Len() > 0 { 139 buffer.WriteByte('.') 140 } 141 buffer.WriteString(structName) 142 } 143 if optionsHolder.functionName { 144 if buffer.Len() > 0 { 145 buffer.WriteByte('.') 146 } 147 buffer.WriteString(funcName) 148 if optionsHolder.lambdas { 149 for i := range lambdas { 150 buffer.WriteByte('.') 151 buffer.WriteString(lambdas[len(lambdas)-i-1]) 152 } 153 } 154 } 155 if optionsHolder.fileName { 156 var closeBrace bool 157 if buffer.Len() > 0 { 158 buffer.WriteByte('(') 159 closeBrace = true 160 } 161 buffer.WriteString(file) 162 if optionsHolder.line { 163 buffer.WriteByte(':') 164 fmt.Fprintf(buffer, "%d", c.line) 165 } 166 if closeBrace { 167 buffer.WriteByte(')') 168 } 169 } 170 171 return buffer.String() 172 } 173 174 func (c call) FunctionID() string { 175 return c.Record(Lambda(false), FileName(false)) 176 } 177 178 func Record(depth int, opts ...recordOption) string { 179 return Call(depth + 1).Record(opts...) 180 }