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