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  }