github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/log/field.go (about)

     1  package log
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
    11  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
    12  	"github.com/ydb-platform/ydb-go-sdk/v3/trace"
    13  )
    14  
    15  const nilPtr = "<nil>"
    16  
    17  // Field represents typed log field (a key-value pair). Adapters should determine
    18  // Field's type based on Type and use the corresponding getter method to retrieve
    19  // the value:
    20  //
    21  //	switch f.Type() {
    22  //	case logs.IntType:
    23  //	    var i int = f.Int()
    24  //	    // handle int value
    25  //	case logs.StringType:
    26  //	    var s string = f.String()
    27  //	    // handle string value
    28  //	//...
    29  //	}
    30  //
    31  // Getter methods must not be called on fields with wrong Type (e.g. calling String()
    32  // on fields with Type != StringType).
    33  // Field must not be initialized directly as a struct literal.
    34  type Field struct {
    35  	ftype FieldType
    36  	key   string
    37  
    38  	vint int64
    39  	vstr string
    40  	vany interface{}
    41  }
    42  
    43  func (f Field) Type() FieldType {
    44  	return f.ftype
    45  }
    46  
    47  func (f Field) Key() string {
    48  	return f.key
    49  }
    50  
    51  // StringValue is a value getter for fields with StringType type
    52  func (f Field) StringValue() string {
    53  	f.checkType(StringType)
    54  
    55  	return f.vstr
    56  }
    57  
    58  // IntValue is a value getter for fields with IntType type
    59  func (f Field) IntValue() int {
    60  	f.checkType(IntType)
    61  
    62  	return int(f.vint)
    63  }
    64  
    65  // Int64Value is a value getter for fields with Int64Type type
    66  func (f Field) Int64Value() int64 {
    67  	f.checkType(Int64Type)
    68  
    69  	return f.vint
    70  }
    71  
    72  // BoolValue is a value getter for fields with BoolType type
    73  func (f Field) BoolValue() bool {
    74  	f.checkType(BoolType)
    75  
    76  	return f.vint != 0
    77  }
    78  
    79  // DurationValue is a value getter for fields with DurationType type
    80  func (f Field) DurationValue() time.Duration {
    81  	f.checkType(DurationType)
    82  
    83  	return time.Nanosecond * time.Duration(f.vint)
    84  }
    85  
    86  // StringsValue is a value getter for fields with StringsType type
    87  func (f Field) StringsValue() []string {
    88  	f.checkType(StringsType)
    89  	if f.vany == nil {
    90  		return nil
    91  	}
    92  
    93  	return f.vany.([]string)
    94  }
    95  
    96  // ErrorValue is a value getter for fields with ErrorType type
    97  func (f Field) ErrorValue() error {
    98  	f.checkType(ErrorType)
    99  	if f.vany == nil {
   100  		return nil
   101  	}
   102  
   103  	return f.vany.(error)
   104  }
   105  
   106  // AnyValue is a value getter for fields with AnyType type
   107  func (f Field) AnyValue() interface{} {
   108  	switch f.ftype {
   109  	case AnyType:
   110  		return f.vany
   111  	case IntType:
   112  		return f.IntValue()
   113  	case Int64Type:
   114  		return f.Int64Value()
   115  	case StringType:
   116  		return f.StringValue()
   117  	case BoolType:
   118  		return f.BoolValue()
   119  	case DurationType:
   120  		return f.DurationValue()
   121  	case StringsType:
   122  		return f.StringsValue()
   123  	case ErrorType:
   124  		return f.ErrorValue()
   125  	case StringerType:
   126  		return f.Stringer()
   127  	default:
   128  		panic(fmt.Sprintf("unknown FieldType %d", f.ftype))
   129  	}
   130  }
   131  
   132  // Stringer is a value getter for fields with StringerType type
   133  func (f Field) Stringer() fmt.Stringer {
   134  	f.checkType(StringerType)
   135  	if f.vany == nil {
   136  		return nil
   137  	}
   138  
   139  	return f.vany.(fmt.Stringer)
   140  }
   141  
   142  // Panics on type mismatch
   143  func (f Field) checkType(want FieldType) {
   144  	if f.ftype != want {
   145  		panic(fmt.Sprintf("bad type. have: %s, want: %s", f.ftype, want))
   146  	}
   147  }
   148  
   149  // Returns default string representation of Field value.
   150  // It should be used by adapters that don't support f.Type directly.
   151  func (f Field) String() string {
   152  	switch f.ftype {
   153  	case IntType, Int64Type:
   154  		return strconv.FormatInt(f.vint, 10)
   155  	case StringType:
   156  		return f.vstr
   157  	case BoolType:
   158  		return strconv.FormatBool(f.BoolValue())
   159  	case DurationType:
   160  		return f.DurationValue().String()
   161  	case StringsType:
   162  		return fmt.Sprintf("%v", f.StringsValue())
   163  	case ErrorType:
   164  		if f.vany == nil || f.vany.(error) == nil {
   165  			return "<nil>"
   166  		}
   167  
   168  		return f.ErrorValue().Error()
   169  	case AnyType:
   170  		if f.vany == nil {
   171  			return nilPtr
   172  		}
   173  		if v := reflect.ValueOf(f.vany); v.Type().Kind() == reflect.Ptr {
   174  			if v.IsNil() {
   175  				return nilPtr
   176  			}
   177  
   178  			return v.Type().String() + "(" + fmt.Sprint(v.Elem()) + ")"
   179  		}
   180  
   181  		return fmt.Sprint(f.vany)
   182  	case StringerType:
   183  		return f.Stringer().String()
   184  	default:
   185  		panic(fmt.Sprintf("unknown FieldType %d", f.ftype))
   186  	}
   187  }
   188  
   189  // String constructs Field with StringType
   190  func String(k, v string) Field {
   191  	return Field{
   192  		ftype: StringType,
   193  		key:   k,
   194  		vstr:  v,
   195  	}
   196  }
   197  
   198  // Int constructs Field with IntType
   199  func Int(k string, v int) Field {
   200  	return Field{
   201  		ftype: IntType,
   202  		key:   k,
   203  		vint:  int64(v),
   204  	}
   205  }
   206  
   207  func Int64(k string, v int64) Field {
   208  	return Field{
   209  		ftype: Int64Type,
   210  		key:   k,
   211  		vint:  v,
   212  	}
   213  }
   214  
   215  // Bool constructs Field with BoolType
   216  func Bool(key string, value bool) Field {
   217  	var vint int64
   218  	if value {
   219  		vint = 1
   220  	} else {
   221  		vint = 0
   222  	}
   223  
   224  	return Field{
   225  		ftype: BoolType,
   226  		key:   key,
   227  		vint:  vint,
   228  	}
   229  }
   230  
   231  // Duration constructs Field with DurationType
   232  func Duration(key string, value time.Duration) Field {
   233  	return Field{
   234  		ftype: DurationType,
   235  		key:   key,
   236  		vint:  value.Nanoseconds(),
   237  	}
   238  }
   239  
   240  // Strings constructs Field with StringsType
   241  func Strings(key string, value []string) Field {
   242  	return Field{
   243  		ftype: StringsType,
   244  		key:   key,
   245  		vany:  value,
   246  	}
   247  }
   248  
   249  // NamedError constructs Field with ErrorType
   250  func NamedError(key string, value error) Field {
   251  	return Field{
   252  		ftype: ErrorType,
   253  		key:   key,
   254  		vany:  value,
   255  	}
   256  }
   257  
   258  // Error is the same as NamedError("error", value)
   259  func Error(value error) Field {
   260  	return NamedError("error", value)
   261  }
   262  
   263  // Any constructs untyped Field.
   264  func Any(key string, value interface{}) Field {
   265  	return Field{
   266  		ftype: AnyType,
   267  		key:   key,
   268  		vany:  value,
   269  	}
   270  }
   271  
   272  // Stringer constructs Field with StringerType. If value is nil,
   273  // resulting Field will be of AnyType instead of StringerType.
   274  func Stringer(key string, value fmt.Stringer) Field {
   275  	if value == nil {
   276  		return Any(key, nil)
   277  	}
   278  
   279  	return Field{
   280  		ftype: StringerType,
   281  		key:   key,
   282  		vany:  value,
   283  	}
   284  }
   285  
   286  // FieldType indicates type info about the Field. This enum might be extended in future releases.
   287  // Adapters that don't support some FieldType value should use Field.Fallback() for marshaling.
   288  type FieldType int
   289  
   290  const (
   291  	// InvalidType indicates that Field was not initialized correctly. Adapters
   292  	// should either ignore such field or issue an error. No value getters should
   293  	// be called on field with such type.
   294  	InvalidType FieldType = iota
   295  
   296  	IntType
   297  	Int64Type
   298  	StringType
   299  	BoolType
   300  	DurationType
   301  
   302  	// StringsType corresponds to []string
   303  	StringsType
   304  
   305  	ErrorType
   306  	// AnyType indicates that the Field is untyped. Adapters should use
   307  	// reflection-based approached to marshal this field.
   308  	AnyType
   309  
   310  	// StringerType corresponds to fmt.Stringer
   311  	StringerType
   312  
   313  	endType
   314  )
   315  
   316  func (ft FieldType) String() (typeName string) {
   317  	switch ft {
   318  	case InvalidType:
   319  		typeName = "invalid"
   320  	case IntType:
   321  		typeName = "int"
   322  	case Int64Type:
   323  		typeName = "int64"
   324  	case StringType:
   325  		typeName = "string"
   326  	case BoolType:
   327  		typeName = "bool"
   328  	case DurationType:
   329  		typeName = "time.Duration"
   330  	case StringsType:
   331  		typeName = "[]string"
   332  	case ErrorType:
   333  		typeName = "error"
   334  	case AnyType:
   335  		typeName = "any"
   336  	case StringerType:
   337  		typeName = "stringer"
   338  	case endType:
   339  		typeName = "endtype"
   340  	default:
   341  		panic("not implemented")
   342  	}
   343  
   344  	return typeName
   345  }
   346  
   347  // latencyField creates Field "latency": time.Since(start)
   348  func latencyField(start time.Time) Field {
   349  	return Duration("latency", time.Since(start))
   350  }
   351  
   352  // versionField creates Field "version": version.Version
   353  func versionField() Field {
   354  	return String("version", version.Version)
   355  }
   356  
   357  type endpoints []trace.EndpointInfo
   358  
   359  func (ee endpoints) String() string {
   360  	b := xstring.Buffer()
   361  	defer b.Free()
   362  	b.WriteByte('[')
   363  	for i, e := range ee {
   364  		if i != 0 {
   365  			b.WriteByte(',')
   366  		}
   367  		b.WriteString(e.String())
   368  	}
   369  	b.WriteByte(']')
   370  
   371  	return b.String()
   372  }
   373  
   374  type metadata map[string][]string
   375  
   376  func (m metadata) String() string {
   377  	b, err := json.Marshal(m)
   378  	if err != nil {
   379  		return fmt.Sprintf("error:%s", err)
   380  	}
   381  
   382  	return xstring.FromBytes(b)
   383  }
   384  
   385  func appendFieldByCondition(condition bool, ifTrueField Field, fields ...Field) []Field {
   386  	if condition {
   387  		fields = append(fields, ifTrueField)
   388  	}
   389  
   390  	return fields
   391  }