github.com/waldiirawan/apm-agent-go/v2@v2.2.2/error.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package apm // import "github.com/waldiirawan/apm-agent-go/v2" 19 20 import ( 21 "crypto/rand" 22 "fmt" 23 "net" 24 "os" 25 "reflect" 26 "syscall" 27 "time" 28 29 "github.com/waldiirawan/apm-agent-go/v2/model" 30 "github.com/waldiirawan/apm-agent-go/v2/stacktrace" 31 ) 32 33 const ( 34 // maxErrorGraphSize is the maximum number of errors 35 // to report in an error tree. Once this number of 36 // nodes is reached, we will stop recursing through 37 // error causes. 38 maxErrorTreeNodes = 50 39 ) 40 41 // Recovered creates an Error with t.NewError(err), where 42 // err is either v (if v implements error), or otherwise 43 // fmt.Errorf("%v", v). The value v is expected to have 44 // come from a panic. 45 func (t *Tracer) Recovered(v interface{}) *Error { 46 var e *Error 47 switch v := v.(type) { 48 case error: 49 e = t.NewError(v) 50 default: 51 e = t.NewError(fmt.Errorf("%v", v)) 52 } 53 return e 54 } 55 56 // NewError returns a new Error with details taken from err. 57 // NewError will panic if called with a nil error. 58 // 59 // The exception message will be set to err.Error(). 60 // The exception module and type will be set to the package 61 // and type name of the cause of the error, respectively, 62 // where the cause has the same definition as given by 63 // github.com/pkg/errors. 64 // 65 // If err implements 66 // 67 // type interface { 68 // StackTrace() github.com/pkg/errors.StackTrace 69 // } 70 // 71 // or 72 // 73 // type interface { 74 // StackTrace() []stacktrace.Frame 75 // } 76 // 77 // then one of those will be used to set the error 78 // stacktrace. Otherwise, NewError will take a stacktrace. 79 // 80 // If err implements 81 // 82 // type interface {Type() string} 83 // 84 // then that will be used to set the error type. 85 // 86 // If err implements 87 // 88 // type interface {Code() string} 89 // 90 // or 91 // 92 // type interface {Code() float64} 93 // 94 // then one of those will be used to set the error code. 95 func (t *Tracer) NewError(err error) *Error { 96 if err == nil { 97 panic("NewError must be called with a non-nil error") 98 } 99 e := t.newError() 100 e.cause = err 101 e.err = err.Error() 102 if e.recording { 103 rand.Read(e.ID[:]) // ignore error, can't do anything about it 104 initException(&e.exception, err, e.stackTraceLimit) 105 if len(e.exception.stacktrace) == 0 { 106 e.SetStacktrace(2) 107 } 108 } 109 return e 110 } 111 112 // NewErrorLog returns a new Error for the given ErrorLogRecord. 113 // 114 // The resulting Error's stacktrace will not be set. Call the 115 // SetStacktrace method to set it, if desired. 116 // 117 // If r.Message is empty, "[EMPTY]" will be used. 118 func (t *Tracer) NewErrorLog(r ErrorLogRecord) *Error { 119 e := t.newError() 120 e.cause = r.Error 121 e.err = e.log.Message 122 if e.recording { 123 e.log = ErrorLogRecord{ 124 Message: truncateString(r.Message), 125 MessageFormat: truncateString(r.MessageFormat), 126 Level: truncateString(r.Level), 127 LoggerName: truncateString(r.LoggerName), 128 } 129 if e.log.Message == "" { 130 e.log.Message = "[EMPTY]" 131 } 132 rand.Read(e.ID[:]) // ignore error, can't do anything about it 133 if r.Error != nil { 134 initException(&e.exception, r.Error, e.stackTraceLimit) 135 } 136 } 137 return e 138 } 139 140 // newError returns a new Error associated with the Tracer. 141 func (t *Tracer) newError() *Error { 142 e, _ := t.errorDataPool.Get().(*ErrorData) 143 if e == nil { 144 e = &ErrorData{ 145 tracer: t, 146 Context: Context{ 147 captureBodyMask: CaptureBodyErrors, 148 }, 149 } 150 } 151 152 instrumentationConfig := t.instrumentationConfig() 153 e.recording = instrumentationConfig.recording 154 if e.recording { 155 e.Timestamp = time.Now() 156 e.Context.captureHeaders = instrumentationConfig.captureHeaders 157 e.Context.sanitizedFieldNames = instrumentationConfig.sanitizedFieldNames 158 e.stackTraceLimit = instrumentationConfig.stackTraceLimit 159 } 160 161 return &Error{ErrorData: e} 162 } 163 164 // Error describes an error occurring in the monitored service. 165 type Error struct { 166 // ErrorData holds the error data. This field is set to nil when 167 // the error's Send method is called. 168 *ErrorData 169 170 // cause holds the original error. 171 // 172 // It is accessible via the Cause method: 173 // https://godoc.org/github.com/pkg/errors#Cause 174 cause error 175 176 // string holds original error string 177 err string 178 } 179 180 // ErrorData holds the details for an error, and is embedded inside Error. 181 // When the error is sent, its ErrorData field will be set to nil. 182 type ErrorData struct { 183 tracer *Tracer 184 recording bool 185 stackTraceLimit int 186 exception exceptionData 187 log ErrorLogRecord 188 logStacktrace []stacktrace.Frame 189 transactionSampled bool 190 transactionName string 191 transactionType string 192 193 // ID is the unique identifier of the error. This is set by 194 // the various error constructors, and is exposed only so 195 // the error ID can be logged or displayed to the user. 196 ID ErrorID 197 198 // TraceID is the unique identifier of the trace in which 199 // this error occurred. If the error is not associated with 200 // a trace, this will be the zero value. 201 TraceID TraceID 202 203 // TransactionID is the unique identifier of the transaction 204 // in which this error occurred. If the error is not associated 205 // with a transaction, this will be the zero value. 206 TransactionID SpanID 207 208 // ParentID is the unique identifier of the transaction or span 209 // in which this error occurred. If the error is not associated 210 // with a transaction or span, this will be the zero value. 211 ParentID SpanID 212 213 // Culprit is the name of the function that caused the error. 214 // 215 // This is initially unset; if it remains unset by the time 216 // Send is invoked, and the error has a stacktrace, the first 217 // non-library frame in the stacktrace will be considered the 218 // culprit. 219 Culprit string 220 221 // Timestamp records the time at which the error occurred. 222 // This is set when the Error object is created, but may 223 // be overridden any time before the Send method is called. 224 Timestamp time.Time 225 226 // Handled records whether or not the error was handled. This 227 // is ignored by "log" errors with no associated error value. 228 Handled bool 229 230 // Context holds the context for this error. 231 Context Context 232 } 233 234 // Cause returns original error assigned to Error, nil if Error or Error.cause is nil. 235 // https://godoc.org/github.com/pkg/errors#Cause 236 func (e *Error) Cause() error { 237 if e != nil { 238 return e.cause 239 } 240 return nil 241 } 242 243 // Error returns string message for error. 244 // if Error or Error.cause is nil, "[EMPTY]" will be used. 245 func (e *Error) Error() string { 246 if e != nil { 247 return e.err 248 } 249 return "[EMPTY]" 250 } 251 252 // SetTransaction sets TraceID, TransactionID, and ParentID to the transaction's 253 // IDs, and records the transaction's Type and whether or not it was sampled. 254 // 255 // If any custom context has been recorded in tx, it will also be carried across 256 // to e, but will not override any custom context already recorded on e. 257 // 258 // SetTransaction also has the effect of setting tx's default outcome to "failure", 259 // unless it is explicitly specified by the instrumentation. 260 func (e *Error) SetTransaction(tx *Transaction) { 261 tx.mu.RLock() 262 traceContext := tx.traceContext 263 var txType, txName string 264 var custom model.IfaceMap 265 if !tx.ended() { 266 txType = tx.Type 267 txName = tx.Name 268 custom = tx.Context.model.Custom 269 tx.TransactionData.mu.Lock() 270 tx.TransactionData.errorCaptured = true 271 tx.TransactionData.mu.Unlock() 272 } 273 tx.mu.RUnlock() 274 e.setSpanData(traceContext, traceContext.Span, txType, txName, custom) 275 } 276 277 // SetSpan sets TraceID, TransactionID, and ParentID to the span's IDs. 278 // 279 // There is no need to call both SetTransaction and SetSpan. If you do call 280 // both, then SetSpan must be called second in order to set the error's 281 // ParentID correctly. 282 // 283 // If any custom context has been recorded in s's transaction, it will 284 // also be carried across to e, but will not override any custom context 285 // already recorded on e. 286 func (e *Error) SetSpan(s *Span) { 287 var txType, txName string 288 var custom model.IfaceMap 289 if s.tx != nil { 290 s.tx.mu.RLock() 291 if !s.tx.ended() { 292 txType = s.tx.Type 293 txName = s.tx.Name 294 custom = s.tx.Context.model.Custom 295 s.tx.TransactionData.mu.Lock() 296 s.tx.TransactionData.errorCaptured = true 297 s.tx.TransactionData.mu.Unlock() 298 } 299 s.tx.mu.RUnlock() 300 301 s.mu.RLock() 302 if !s.ended() { 303 s.SpanData.mu.Lock() 304 s.SpanData.errorCaptured = true 305 s.SpanData.mu.Unlock() 306 } 307 s.mu.RUnlock() 308 } 309 e.setSpanData(s.traceContext, s.transactionID, txType, txName, custom) 310 } 311 312 func (e *Error) setSpanData( 313 traceContext TraceContext, 314 transactionID SpanID, 315 transactionType, transactionName string, 316 customContext model.IfaceMap, 317 ) { 318 e.TraceID = traceContext.Trace 319 e.ParentID = traceContext.Span 320 e.TransactionID = transactionID 321 e.transactionSampled = traceContext.Options.Recorded() 322 if e.transactionSampled { 323 e.transactionName = transactionName 324 e.transactionType = transactionType 325 } 326 if n := len(customContext); n != 0 { 327 m := len(e.Context.model.Custom) 328 e.Context.model.Custom = append(e.Context.model.Custom, customContext...) 329 // If there was already custom context in e, shift the custom context from 330 // tx to the beginning of the slice so that e's context takes precedence. 331 if m != 0 { 332 copy(e.Context.model.Custom[n:], e.Context.model.Custom[:m]) 333 copy(e.Context.model.Custom[:n], customContext) 334 } 335 } 336 } 337 338 // Send enqueues the error for sending to the Elastic APM server. 339 // 340 // Send will set e.ErrorData to nil, so the error must not be 341 // modified after Send returns. 342 func (e *Error) Send() { 343 if e == nil || e.sent() { 344 return 345 } 346 if e.recording { 347 e.ErrorData.enqueue() 348 } else { 349 e.reset() 350 } 351 e.ErrorData = nil 352 } 353 354 func (e *Error) sent() bool { 355 return e.ErrorData == nil 356 } 357 358 func (e *ErrorData) enqueue() { 359 select { 360 case e.tracer.events <- tracerEvent{eventType: errorEvent, err: e}: 361 default: 362 // Enqueuing an error should never block. 363 e.tracer.stats.accumulate(TracerStats{ErrorsDropped: 1}) 364 e.reset() 365 } 366 } 367 368 func (e *ErrorData) reset() { 369 *e = ErrorData{ 370 tracer: e.tracer, 371 logStacktrace: e.logStacktrace[:0], 372 Context: e.Context, 373 exception: e.exception, 374 } 375 e.Context.reset() 376 e.exception.reset() 377 e.tracer.errorDataPool.Put(e) 378 } 379 380 type exceptionData struct { 381 message string 382 stacktrace []stacktrace.Frame 383 cause []exceptionData 384 ErrorDetails 385 } 386 387 func (e *exceptionData) reset() { 388 *e = exceptionData{ 389 cause: e.cause[:0], 390 stacktrace: e.stacktrace[:0], 391 ErrorDetails: ErrorDetails{ 392 attrs: e.ErrorDetails.attrs, 393 Cause: e.ErrorDetails.Cause[:0], 394 }, 395 } 396 for k := range e.attrs { 397 delete(e.attrs, k) 398 } 399 } 400 401 func initException(e *exceptionData, err error, stackTraceLimit int) { 402 b := exceptionDataBuilder{stackTraceLimit: stackTraceLimit} 403 b.init(e, err) 404 } 405 406 type exceptionDataBuilder struct { 407 stackTraceLimit int 408 errorCount int 409 pointerErrors map[uintptr]struct{} 410 } 411 412 func (b *exceptionDataBuilder) init(e *exceptionData, err error) bool { 413 b.errorCount++ 414 reflectValue := reflect.ValueOf(err) 415 reflectType := reflectValue.Type() 416 switch reflectType.Kind() { 417 case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: 418 // Prevent infinite recursion due to cyclic error causes. 419 ptrVal := reflectValue.Pointer() 420 if b.pointerErrors == nil { 421 b.pointerErrors = map[uintptr]struct{}{ptrVal: struct{}{}} 422 } else { 423 if _, ok := b.pointerErrors[ptrVal]; ok { 424 return false 425 } 426 b.pointerErrors[ptrVal] = struct{}{} 427 } 428 } 429 430 e.message = truncateString(err.Error()) 431 if e.message == "" { 432 e.message = "[EMPTY]" 433 } 434 435 namedType := reflectType 436 if reflectType.Name() == "" && reflectType.Kind() == reflect.Ptr { 437 namedType = reflectType.Elem() 438 } 439 e.Type.Name = namedType.Name() 440 e.Type.PackagePath = namedType.PkgPath() 441 442 // If the error implements Type, use that to 443 // override the type name determined through 444 // reflection. 445 if err, ok := err.(interface { 446 Type() string 447 }); ok { 448 e.Type.Name = err.Type() 449 } 450 451 // If the error implements a Code method, use 452 // that to set the exception code. 453 switch err := err.(type) { 454 case interface { 455 Code() string 456 }: 457 e.Code.String = err.Code() 458 case interface { 459 Code() float64 460 }: 461 e.Code.Number = err.Code() 462 } 463 464 // If the error implements an Unwrap or Cause method, use that to set the cause error. 465 // Unwrap is defined by errors wrapped using fmt.Errorf, while Cause is defined by 466 // errors wrapped using pkg/errors.Wrap. 467 switch err := err.(type) { 468 case interface{ Unwrap() error }: 469 if cause := err.Unwrap(); cause != nil { 470 e.ErrorDetails.Cause = append(e.ErrorDetails.Cause, cause) 471 } 472 case interface{ Cause() error }: 473 if cause := err.Cause(); cause != nil { 474 e.ErrorDetails.Cause = append(e.ErrorDetails.Cause, cause) 475 } 476 } 477 478 // Run registered ErrorDetailers over the error. 479 for _, ed := range typeErrorDetailers[reflectType] { 480 ed.ErrorDetails(err, &e.ErrorDetails) 481 } 482 for _, ed := range errorDetailers { 483 ed.ErrorDetails(err, &e.ErrorDetails) 484 } 485 486 e.Code.String = truncateString(e.Code.String) 487 e.Type.Name = truncateString(e.Type.Name) 488 e.Type.PackagePath = truncateString(e.Type.PackagePath) 489 e.stacktrace = stacktrace.AppendErrorStacktrace(e.stacktrace, err, b.stackTraceLimit) 490 491 for _, err := range e.ErrorDetails.Cause { 492 if b.errorCount >= maxErrorTreeNodes { 493 break 494 } 495 var data exceptionData 496 if b.init(&data, err) { 497 e.cause = append(e.cause, data) 498 } 499 } 500 return true 501 } 502 503 // SetStacktrace sets the stacktrace for the error, 504 // skipping the first skip number of frames, excluding 505 // the SetStacktrace function. 506 func (e *Error) SetStacktrace(skip int) { 507 out := &e.exception.stacktrace 508 if e.log.Message != "" { 509 out = &e.logStacktrace 510 } 511 *out = stacktrace.AppendStacktrace((*out)[:0], skip+1, e.stackTraceLimit) 512 } 513 514 // ErrorLogRecord holds details of an error log record. 515 type ErrorLogRecord struct { 516 // Message holds the message for the log record, 517 // e.g. "failed to connect to %s". 518 // 519 // If this is empty, "[EMPTY]" will be used. 520 Message string 521 522 // MessageFormat holds the non-interpolated format 523 // of the log record, e.g. "failed to connect to %s". 524 // 525 // This is optional. 526 MessageFormat string 527 528 // Level holds the severity level of the log record. 529 // 530 // This is optional. 531 Level string 532 533 // LoggerName holds the name of the logger used. 534 // 535 // This is optional. 536 LoggerName string 537 538 // Error is an error associated with the log record. 539 // 540 // This is optional. 541 Error error 542 } 543 544 // ErrorID uniquely identifies an error. 545 type ErrorID TraceID 546 547 // String returns id in its hex-encoded format. 548 func (id ErrorID) String() string { 549 return TraceID(id).String() 550 } 551 552 func init() { 553 RegisterErrorDetailer(ErrorDetailerFunc(func(err error, details *ErrorDetails) { 554 if errTemporary(err) { 555 details.SetAttr("temporary", true) 556 } 557 if errTimeout(err) { 558 details.SetAttr("timeout", true) 559 } 560 })) 561 RegisterTypeErrorDetailer(reflect.TypeOf(&net.OpError{}), ErrorDetailerFunc(func(err error, details *ErrorDetails) { 562 opErr := err.(*net.OpError) 563 details.SetAttr("op", opErr.Op) 564 details.SetAttr("net", opErr.Net) 565 if opErr.Source != nil { 566 if addr := opErr.Source; addr != nil { 567 details.SetAttr("source", fmt.Sprintf("%s:%s", addr.Network(), addr.String())) 568 } 569 } 570 if opErr.Addr != nil { 571 if addr := opErr.Addr; addr != nil { 572 details.SetAttr("addr", fmt.Sprintf("%s:%s", addr.Network(), addr.String())) 573 } 574 } 575 details.Cause = append(details.Cause, opErr.Err) 576 })) 577 RegisterTypeErrorDetailer(reflect.TypeOf(&os.LinkError{}), ErrorDetailerFunc(func(err error, details *ErrorDetails) { 578 linkErr := err.(*os.LinkError) 579 details.SetAttr("op", linkErr.Op) 580 details.SetAttr("old", linkErr.Old) 581 details.SetAttr("new", linkErr.New) 582 details.Cause = append(details.Cause, linkErr.Err) 583 })) 584 RegisterTypeErrorDetailer(reflect.TypeOf(&os.PathError{}), ErrorDetailerFunc(func(err error, details *ErrorDetails) { 585 pathErr := err.(*os.PathError) 586 details.SetAttr("op", pathErr.Op) 587 details.SetAttr("path", pathErr.Path) 588 details.Cause = append(details.Cause, pathErr.Err) 589 })) 590 RegisterTypeErrorDetailer(reflect.TypeOf(&os.SyscallError{}), ErrorDetailerFunc(func(err error, details *ErrorDetails) { 591 syscallErr := err.(*os.SyscallError) 592 details.SetAttr("syscall", syscallErr.Syscall) 593 details.Cause = append(details.Cause, syscallErr.Err) 594 })) 595 RegisterTypeErrorDetailer(reflect.TypeOf(syscall.Errno(0)), ErrorDetailerFunc(func(err error, details *ErrorDetails) { 596 errno := err.(syscall.Errno) 597 details.Code.String = errnoName(errno) 598 if details.Code.String == "" { 599 details.Code.Number = float64(errno) 600 } 601 })) 602 } 603 604 func errTemporary(err error) bool { 605 type temporaryError interface { 606 Temporary() bool 607 } 608 terr, ok := err.(temporaryError) 609 return ok && terr.Temporary() 610 } 611 612 func errTimeout(err error) bool { 613 type timeoutError interface { 614 Timeout() bool 615 } 616 terr, ok := err.(timeoutError) 617 return ok && terr.Timeout() 618 } 619 620 // RegisterTypeErrorDetailer registers e to be called for any error with 621 // the concrete type t. 622 // 623 // Each ErrorDetailer registered in this way will be called, in the order 624 // registered, for each error of type t created via Tracer.NewError or 625 // Tracer.NewErrorLog. 626 // 627 // RegisterTypeErrorDetailer must not be called during tracer operation; 628 // it is intended to be called at package init time. 629 func RegisterTypeErrorDetailer(t reflect.Type, e ErrorDetailer) { 630 typeErrorDetailers[t] = append(typeErrorDetailers[t], e) 631 } 632 633 // RegisterErrorDetailer registers e in the global list of ErrorDetailers. 634 // 635 // Each ErrorDetailer registered in this way will be called, in the order 636 // registered, for each error created via Tracer.NewError or Tracer.NewErrorLog. 637 // 638 // RegisterErrorDetailer must not be called during tracer operation; it is 639 // intended to be called at package init time. 640 func RegisterErrorDetailer(e ErrorDetailer) { 641 errorDetailers = append(errorDetailers, e) 642 } 643 644 var ( 645 typeErrorDetailers = make(map[reflect.Type][]ErrorDetailer) 646 errorDetailers []ErrorDetailer 647 ) 648 649 // ErrorDetails holds details of an error, which can be altered or 650 // extended by registering an ErrorDetailer with RegisterErrorDetailer 651 // or RegisterTypeErrorDetailer. 652 type ErrorDetails struct { 653 attrs map[string]interface{} 654 655 // Type holds information about the error type, initialized 656 // with the type name and type package path using reflection. 657 Type struct { 658 // Name holds the error type name. 659 Name string 660 661 // PackagePath holds the error type package path. 662 PackagePath string 663 } 664 665 // Code holds an error code. 666 Code struct { 667 // String holds a string-based error code. If this is set, then Number is ignored. 668 // 669 // This field will be initialized to the result of calling an error's Code method, 670 // if the error implements the following interface: 671 // 672 // type interface StringCoder { 673 // Code() string 674 // } 675 String string 676 677 // Number holds a numerical error code. This is ignored if String is set. 678 // 679 // This field will be initialized to the result of calling an error's Code 680 // method, if the error implements the following interface: 681 // 682 // type interface NumberCoder { 683 // Code() float64 684 // } 685 Number float64 686 } 687 688 // Cause holds the errors that were the cause of this error. 689 Cause []error 690 } 691 692 // SetAttr sets the attribute with key k to value v. 693 func (d *ErrorDetails) SetAttr(k string, v interface{}) { 694 if d.attrs == nil { 695 d.attrs = make(map[string]interface{}) 696 } 697 d.attrs[k] = v 698 } 699 700 // ErrorDetailer defines an interface for altering or extending the ErrorDetails for an error. 701 // 702 // ErrorDetailers can be registered using the package-level functions RegisterErrorDetailer and 703 // RegisterTypeErrorDetailer. 704 type ErrorDetailer interface { 705 // ErrorDetails is called to update or alter details for err. 706 ErrorDetails(err error, details *ErrorDetails) 707 } 708 709 // ErrorDetailerFunc is a function type implementing ErrorDetailer. 710 type ErrorDetailerFunc func(error, *ErrorDetails) 711 712 // ErrorDetails calls f(err, details). 713 func (f ErrorDetailerFunc) ErrorDetails(err error, details *ErrorDetails) { 714 f(err, details) 715 }