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 }