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