github.com/waldiirawan/apm-agent-go/v2@v2.2.2/error_test.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_test 19 20 import ( 21 "context" 22 "fmt" 23 "net" 24 "os" 25 "path/filepath" 26 "reflect" 27 "runtime" 28 "strconv" 29 "syscall" 30 "testing" 31 32 "github.com/pkg/errors" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 36 "github.com/waldiirawan/apm-agent-go/v2" 37 "github.com/waldiirawan/apm-agent-go/v2/apmtest" 38 "github.com/waldiirawan/apm-agent-go/v2/model" 39 "github.com/waldiirawan/apm-agent-go/v2/stacktrace" 40 "github.com/waldiirawan/apm-agent-go/v2/transport/transporttest" 41 ) 42 43 func TestErrorID(t *testing.T) { 44 var errorID apm.ErrorID 45 _, _, errors := apmtest.WithTransaction(func(ctx context.Context) { 46 e := apm.CaptureError(ctx, errors.New("boom")) 47 errorID = e.ID 48 e.Send() 49 }) 50 require.Len(t, errors, 1) 51 assert.NotZero(t, errorID) 52 assert.Equal(t, model.TraceID(errorID), errors[0].ID) 53 } 54 55 func TestErrorsStackTrace(t *testing.T) { 56 modelError := sendError(t, &errorsStackTracer{ 57 "zing", newErrorsStackTrace(0, 2), 58 }) 59 exception := modelError.Exception 60 stacktrace := exception.Stacktrace 61 assert.Equal(t, "zing", exception.Message) 62 assert.Equal(t, "github.com/waldiirawan/apm-agent-go/v2_test", exception.Module) 63 assert.Equal(t, "errorsStackTracer", exception.Type) 64 require.Len(t, stacktrace, 2) 65 assert.Equal(t, "newErrorsStackTrace", stacktrace[0].Function) 66 assert.Equal(t, "TestErrorsStackTrace", stacktrace[1].Function) 67 } 68 69 func TestErrorsStackTraceLimit(t *testing.T) { 70 defer os.Unsetenv("ELASTIC_APM_STACK_TRACE_LIMIT") 71 const n = 2 72 for i := -1; i < n; i++ { 73 os.Setenv("ELASTIC_APM_STACK_TRACE_LIMIT", strconv.Itoa(i)) 74 modelError := sendError(t, &errorsStackTracer{ 75 "zing", newErrorsStackTrace(0, n), 76 }) 77 stacktrace := modelError.Exception.Stacktrace 78 if i == -1 { 79 require.Len(t, stacktrace, n) 80 } else { 81 require.Len(t, stacktrace, i) 82 } 83 } 84 } 85 86 func TestInternalStackTrace(t *testing.T) { 87 // Absolute path on both windows (UNC) and *nix 88 abspath := filepath.FromSlash("//abs/path/file.go") 89 modelError := sendError(t, &internalStackTracer{ 90 "zing", []stacktrace.Frame{ 91 {Function: "pkg/path.FuncName"}, 92 {Function: "FuncName2", File: abspath, Line: 123}, 93 {Function: "encoding/json.Marshal"}, 94 }, 95 }) 96 exception := modelError.Exception 97 stacktrace := exception.Stacktrace 98 assert.Equal(t, "zing", exception.Message) 99 assert.Equal(t, "github.com/waldiirawan/apm-agent-go/v2_test", exception.Module) 100 assert.Equal(t, "internalStackTracer", exception.Type) 101 assert.Equal(t, []model.StacktraceFrame{{ 102 Function: "FuncName", 103 Module: "pkg/path", 104 }, { 105 AbsolutePath: abspath, 106 Function: "FuncName2", 107 File: "file.go", 108 Line: 123, 109 }, { 110 Function: "Marshal", 111 Module: "encoding/json", 112 LibraryFrame: true, 113 }}, stacktrace) 114 } 115 116 func TestInternalStackTraceLimit(t *testing.T) { 117 inFrames := []stacktrace.Frame{ 118 {Function: "pkg/path.FuncName"}, 119 {Function: "FuncName2", Line: 123}, 120 {Function: "encoding/json.Marshal"}, 121 } 122 outFrames := []model.StacktraceFrame{{ 123 Function: "FuncName", 124 Module: "pkg/path", 125 }, { 126 Function: "FuncName2", 127 Line: 123, 128 }, { 129 Function: "Marshal", 130 Module: "encoding/json", 131 LibraryFrame: true, 132 }} 133 134 defer os.Unsetenv("ELASTIC_APM_STACK_TRACE_LIMIT") 135 for i := -1; i < len(inFrames); i++ { 136 os.Setenv("ELASTIC_APM_STACK_TRACE_LIMIT", strconv.Itoa(i)) 137 modelError := sendError(t, &internalStackTracer{ 138 "zing", []stacktrace.Frame{ 139 {Function: "pkg/path.FuncName"}, 140 {Function: "FuncName2", Line: 123}, 141 {Function: "encoding/json.Marshal"}, 142 }, 143 }) 144 stacktrace := modelError.Exception.Stacktrace 145 if i == 0 { 146 assert.Nil(t, stacktrace) 147 continue 148 } 149 expect := outFrames 150 if i > 0 { 151 expect = expect[:i] 152 } 153 assert.Equal(t, expect, stacktrace) 154 } 155 } 156 157 func TestRuntimeStackTrace(t *testing.T) { 158 pc := make([]uintptr, 20) 159 n := runtime.Callers(1, pc) 160 pc = pc[:n] 161 162 modelError := sendError(t, &runtimeStackTracer{ 163 message: "zing", 164 trace: pc, 165 }) 166 167 exception := modelError.Exception 168 stacktrace := exception.Stacktrace 169 assert.Equal(t, "zing", exception.Message) 170 assert.Equal(t, "github.com/waldiirawan/apm-agent-go/v2_test", exception.Module) 171 assert.Equal(t, "runtimeStackTracer", exception.Type) 172 assert.Equal(t, 3, len(stacktrace)) 173 174 frame := stacktrace[0] 175 assert.Equal(t, "github.com/waldiirawan/apm-agent-go/v2_test", frame.Module) 176 assert.Equal(t, "error_test.go", frame.File) 177 assert.Equal(t, "TestRuntimeStackTrace", frame.Function) 178 assert.Greater(t, frame.Line, 0) 179 } 180 181 func TestRuntimeStackTraceLimit(t *testing.T) { 182 pc := make([]uintptr, 20) 183 n := runtime.Callers(1, pc) 184 pc = pc[:n] 185 186 funcsInStackTrace := []string{ 187 "TestRuntimeStackTraceLimit", 188 "tRunner", 189 "goexit", 190 } 191 192 defer os.Unsetenv("ELASTIC_APM_STACK_TRACE_LIMIT") 193 for i := -1; i < n; i++ { 194 os.Setenv("ELASTIC_APM_STACK_TRACE_LIMIT", strconv.Itoa(i)) 195 modelError := sendError(t, &runtimeStackTracer{ 196 message: "zing", 197 trace: pc, 198 }) 199 stacktrace := modelError.Exception.Stacktrace 200 if i == 0 { 201 assert.Nil(t, stacktrace) 202 continue 203 } 204 205 expect := funcsInStackTrace 206 if i > 0 { 207 expect = expect[:i] 208 } 209 assert.Equal(t, len(expect), len(stacktrace)) 210 for i, funcName := range expect { 211 assert.Equal(t, funcName, stacktrace[i].Function) 212 } 213 } 214 } 215 216 func TestErrorAutoStackTraceReuse(t *testing.T) { 217 tracer, r := transporttest.NewRecorderTracer() 218 defer tracer.Close() 219 220 err := fmt.Errorf("hullo") // no stacktrace attached 221 for i := 0; i < 1000; i++ { 222 tracer.NewError(err).Send() 223 } 224 tracer.Flush(nil) 225 226 // The previously sent error objects should have 227 // been reset and will be reused. We reuse the 228 // stacktrace slice. See elastic/apm-agent-go#204. 229 for i := 0; i < 1000; i++ { 230 tracer.NewError(err).Send() 231 } 232 tracer.Flush(nil) 233 234 payloads := r.Payloads() 235 assert.NotEmpty(t, payloads.Errors) 236 for _, e := range payloads.Errors { 237 assert.NotEqual(t, "", e.Culprit) 238 assert.NotEmpty(t, e.Exception.Stacktrace) 239 } 240 } 241 242 func TestCaptureErrorNoTransaction(t *testing.T) { 243 // When there's no transaction or span in the context, 244 // CaptureError returns Error with nil ErrorData as it has no tracer with 245 // which it can create the error. 246 e := apm.CaptureError(context.Background(), errors.New("boom")) 247 assert.Nil(t, e.ErrorData) 248 249 // Send is a no-op on a Error with nil ErrorData. 250 e.Send() 251 } 252 253 func TestErrorLogRecord(t *testing.T) { 254 tracer, recorder := transporttest.NewRecorderTracer() 255 defer tracer.Close() 256 257 error_ := tracer.NewErrorLog(apm.ErrorLogRecord{ 258 Message: "log-message", 259 Error: makeError("error-message"), 260 }) 261 error_.SetStacktrace(1) 262 error_.Send() 263 tracer.Flush(nil) 264 265 payloads := recorder.Payloads() 266 require.Len(t, payloads.Errors, 1) 267 err0 := payloads.Errors[0] 268 assert.Equal(t, "log-message", err0.Log.Message) 269 assert.Equal(t, "error-message", err0.Exception.Message) 270 require.NotEmpty(t, err0.Log.Stacktrace) 271 require.NotEmpty(t, err0.Exception.Stacktrace) 272 assert.Equal(t, err0.Log.Stacktrace[0].Function, "TestErrorLogRecord") 273 assert.Equal(t, err0.Exception.Stacktrace[0].Function, "makeError") 274 assert.Equal(t, "makeError", err0.Culprit) // based on exception stacktrace 275 } 276 277 func TestErrorCauserInterface(t *testing.T) { 278 type Causer interface { 279 Cause() error 280 } 281 var e Causer = apm.CaptureError(context.Background(), errors.New("boom")) 282 assert.EqualError(t, e.Cause(), "boom") 283 } 284 285 func TestErrorNilCauser(t *testing.T) { 286 var e *apm.Error 287 assert.Nil(t, e.Cause()) 288 289 e = &apm.Error{} 290 assert.Nil(t, e.Cause()) 291 } 292 293 func TestErrorErrorInterface(t *testing.T) { 294 var e error = apm.CaptureError(context.Background(), errors.New("boom")) 295 assert.EqualError(t, e, "boom") 296 } 297 298 func TestErrorNilError(t *testing.T) { 299 var e *apm.Error 300 assert.EqualError(t, e, "[EMPTY]") 301 302 e = &apm.Error{} 303 assert.EqualError(t, e, "") 304 } 305 306 func TestErrorNotRecording(t *testing.T) { 307 tracer := apmtest.NewRecordingTracer() 308 defer tracer.Close() 309 tracer.SetRecording(false) 310 311 e := tracer.NewError(errors.New("boom")) 312 require.NotNil(t, e) 313 require.NotNil(t, e.ErrorData) 314 e.Send() 315 require.Nil(t, e.ErrorData) 316 tracer.Flush(nil) 317 318 payloads := tracer.Payloads() 319 require.Empty(t, payloads.Errors) 320 } 321 322 func TestErrorTransactionSampled(t *testing.T) { 323 _, _, errors := apmtest.WithTransaction(func(ctx context.Context) { 324 apm.TransactionFromContext(ctx).Type = "foo" 325 apm.CaptureError(ctx, errors.New("boom")).Send() 326 327 span, ctx := apm.StartSpan(ctx, "name", "type") 328 defer span.End() 329 apm.CaptureError(ctx, errors.New("boom")).Send() 330 }) 331 assertErrorTransactionSampled(t, errors[0], true) 332 assertErrorTransactionSampled(t, errors[1], true) 333 assert.Equal(t, "foo", errors[0].Transaction.Type) 334 assert.Equal(t, "name", errors[0].Transaction.Name) 335 assert.Equal(t, "foo", errors[1].Transaction.Type) 336 assert.Equal(t, "name", errors[1].Transaction.Name) 337 338 } 339 340 func TestErrorTransactionNotSampled(t *testing.T) { 341 tracer, recorder := transporttest.NewRecorderTracer() 342 defer tracer.Close() 343 tracer.SetSampler(apm.NewRatioSampler(0)) 344 345 tx := tracer.StartTransaction("name", "type") 346 ctx := apm.ContextWithTransaction(context.Background(), tx) 347 apm.CaptureError(ctx, errors.New("boom")).Send() 348 349 tracer.Flush(nil) 350 payloads := recorder.Payloads() 351 require.Len(t, payloads.Errors, 1) 352 assertErrorTransactionSampled(t, payloads.Errors[0], false) 353 } 354 355 func TestErrorTransactionSampledNoTransaction(t *testing.T) { 356 tracer, recorder := transporttest.NewRecorderTracer() 357 defer tracer.Close() 358 359 tracer.NewError(errors.New("boom")).Send() 360 tracer.Flush(nil) 361 payloads := recorder.Payloads() 362 require.Len(t, payloads.Errors, 1) 363 assert.Nil(t, payloads.Errors[0].Transaction.Sampled) 364 } 365 366 func TestErrorTransactionCustomContext(t *testing.T) { 367 tracer, recorder := transporttest.NewRecorderTracer() 368 defer tracer.Close() 369 370 tx := tracer.StartTransaction("name", "type") 371 tx.Context.SetCustom("k1", "v1") 372 tx.Context.SetCustom("k2", "v2") 373 ctx := apm.ContextWithTransaction(context.Background(), tx) 374 apm.CaptureError(ctx, errors.New("boom")).Send() 375 376 _, ctx = apm.StartSpan(ctx, "foo", "bar") 377 apm.CaptureError(ctx, errors.New("boom")).Send() 378 379 // Create an error with custom context set before setting 380 // the transaction. Such custom context should override 381 // whatever is carried over from the transaction. 382 e := tracer.NewError(errors.New("boom")) 383 e.Context.SetCustom("k1", "!!") 384 e.Context.SetCustom("k3", "v3") 385 e.SetTransaction(tx) 386 e.Send() 387 388 tracer.Flush(nil) 389 payloads := recorder.Payloads() 390 require.Len(t, payloads.Errors, 3) 391 392 assert.Equal(t, model.IfaceMap{ 393 {Key: "k1", Value: "v1"}, 394 {Key: "k2", Value: "v2"}, 395 }, payloads.Errors[0].Context.Custom) 396 397 assert.Equal(t, model.IfaceMap{ 398 {Key: "k1", Value: "v1"}, 399 {Key: "k2", Value: "v2"}, 400 }, payloads.Errors[1].Context.Custom) 401 402 assert.Equal(t, model.IfaceMap{ 403 {Key: "k1", Value: "!!"}, 404 {Key: "k2", Value: "v2"}, 405 {Key: "k3", Value: "v3"}, 406 }, payloads.Errors[2].Context.Custom) 407 } 408 409 func TestErrorDetailer(t *testing.T) { 410 type error1 struct{ error } 411 apm.RegisterTypeErrorDetailer(reflect.TypeOf(error1{}), apm.ErrorDetailerFunc(func(err error, details *apm.ErrorDetails) { 412 details.SetAttr("a", "error1") 413 })) 414 415 type error2 struct{ error } 416 apm.RegisterTypeErrorDetailer(reflect.TypeOf(&error2{}), apm.ErrorDetailerFunc(func(err error, details *apm.ErrorDetails) { 417 details.SetAttr("b", "*error2") 418 })) 419 420 apm.RegisterErrorDetailer(apm.ErrorDetailerFunc(func(err error, details *apm.ErrorDetails) { 421 // NOTE(axw) ErrorDetailers can't be _unregistered_, 422 // so we check the error type so as not to interfere 423 // with other tests. 424 switch err.(type) { 425 case error1, *error2: 426 details.SetAttr("c", "both") 427 } 428 })) 429 430 _, _, errs := apmtest.WithTransaction(func(ctx context.Context) { 431 apm.CaptureError(ctx, error1{errors.New("error1")}).Send() 432 apm.CaptureError(ctx, &error2{errors.New("error2")}).Send() 433 }) 434 require.Len(t, errs, 2) 435 assert.Equal(t, map[string]interface{}{"a": "error1", "c": "both"}, errs[0].Exception.Attributes) 436 assert.Equal(t, map[string]interface{}{"b": "*error2", "c": "both"}, errs[1].Exception.Attributes) 437 } 438 439 func TestStdlibErrorDetailers(t *testing.T) { 440 t.Run("syscall.Errno", func(t *testing.T) { 441 _, _, errs := apmtest.WithTransaction(func(ctx context.Context) { 442 apm.CaptureError(ctx, syscall.Errno(syscall.EAGAIN)).Send() 443 }) 444 require.Len(t, errs, 1) 445 446 if runtime.GOOS == "windows" { 447 // There's currently no equivalent of unix.ErrnoName for Windows. 448 assert.Equal(t, model.ExceptionCode{Number: float64(syscall.EAGAIN)}, errs[0].Exception.Code) 449 } else { 450 assert.Equal(t, model.ExceptionCode{String: "EAGAIN"}, errs[0].Exception.Code) 451 } 452 453 assert.Equal(t, map[string]interface{}{ 454 "temporary": true, 455 "timeout": true, 456 }, errs[0].Exception.Attributes) 457 }) 458 459 cause := errors.New("cause") 460 test := func(err error, expectedAttrs map[string]interface{}) { 461 t.Run(fmt.Sprintf("%T", err), func(t *testing.T) { 462 _, _, errs := apmtest.WithTransaction(func(ctx context.Context) { 463 apm.CaptureError(ctx, err).Send() 464 }) 465 require.Len(t, errs, 1) 466 assert.Equal(t, expectedAttrs, errs[0].Exception.Attributes) 467 require.Len(t, errs[0].Exception.Cause, 1) 468 assert.Equal(t, "cause", errs[0].Exception.Cause[0].Message) 469 }) 470 } 471 type attrmap map[string]interface{} 472 473 test(&net.OpError{ 474 Err: cause, 475 Op: "read", 476 Net: "tcp", 477 Source: &net.TCPAddr{ 478 IP: net.IPv6loopback, 479 Port: 1234, 480 }, 481 }, attrmap{"op": "read", "net": "tcp", "source": "tcp:[::1]:1234"}) 482 483 test(&os.LinkError{ 484 Err: cause, 485 Op: "symlink", 486 Old: "/old", 487 New: "/new", 488 }, attrmap{"op": "symlink", "old": "/old", "new": "/new"}) 489 490 test(&os.PathError{ 491 Err: cause, 492 Op: "open", 493 Path: "/dev/null", 494 }, attrmap{"op": "open", "path": "/dev/null"}) 495 496 test(&os.SyscallError{ 497 Err: cause, 498 Syscall: "connect", 499 }, attrmap{"syscall": "connect"}) 500 } 501 502 func TestErrorCauseCulprit(t *testing.T) { 503 err := errors.WithStack(testErrorCauseCulpritHelper()) 504 505 tracer, recorder := transporttest.NewRecorderTracer() 506 defer tracer.Close() 507 tracer.NewError(err).Send() 508 tracer.Flush(nil) 509 510 payloads := recorder.Payloads() 511 require.Len(t, payloads.Errors, 1) 512 assert.Equal(t, "testErrorCauseCulpritHelper", payloads.Errors[0].Culprit) 513 } 514 515 func testErrorCauseCulpritHelper() error { 516 return errors.Errorf("something happened here") 517 } 518 519 func TestErrorCauseCauser(t *testing.T) { 520 err := &causer{ 521 error: errors.New("error"), 522 cause: errors.New("cause"), 523 } 524 525 tracer, recorder := transporttest.NewRecorderTracer() 526 defer tracer.Close() 527 tracer.NewError(err).Send() 528 tracer.Flush(nil) 529 530 payloads := recorder.Payloads() 531 require.Len(t, payloads.Errors, 1) 532 assert.Equal(t, "TestErrorCauseCauser", payloads.Errors[0].Culprit) 533 534 require.Len(t, payloads.Errors[0].Exception.Cause, 1) 535 assert.Equal(t, "cause", payloads.Errors[0].Exception.Cause[0].Message) 536 } 537 538 func TestErrorCauseCycle(t *testing.T) { 539 err := make(errorslice, 1) 540 err[0] = causer{error: makeError("error"), cause: &err} 541 542 tracer, recorder := transporttest.NewRecorderTracer() 543 defer tracer.Close() 544 tracer.NewError(err).Send() 545 tracer.Flush(nil) 546 547 payloads := recorder.Payloads() 548 require.Len(t, payloads.Errors, 1) 549 assert.Equal(t, "TestErrorCauseCycle", payloads.Errors[0].Culprit) 550 551 require.Len(t, payloads.Errors[0].Exception.Cause, 1) 552 require.Len(t, payloads.Errors[0].Exception.Cause[0].Cause, 1) 553 require.Len(t, payloads.Errors[0].Exception.Cause[0].Cause, 1) // cycle broken 554 assert.Equal(t, "error", payloads.Errors[0].Exception.Cause[0].Message) 555 assert.Equal(t, "errorslice", payloads.Errors[0].Exception.Cause[0].Cause[0].Message) 556 } 557 558 func TestErrorCauseUnwrap(t *testing.T) { 559 err := fmt.Errorf("%w", errors.New("cause")) 560 561 tracer, recorder := transporttest.NewRecorderTracer() 562 defer tracer.Close() 563 tracer.NewError(err).Send() 564 tracer.Flush(nil) 565 566 payloads := recorder.Payloads() 567 require.Len(t, payloads.Errors, 1) 568 assert.Equal(t, "TestErrorCauseUnwrap", payloads.Errors[0].Culprit) 569 570 require.Len(t, payloads.Errors[0].Exception.Cause, 1) 571 assert.Equal(t, "cause", payloads.Errors[0].Exception.Cause[0].Message) 572 } 573 574 func assertErrorTransactionSampled(t *testing.T, e model.Error, sampled bool) { 575 assert.Equal(t, &sampled, e.Transaction.Sampled) 576 if sampled { 577 assert.NotEmpty(t, e.Transaction.Type) 578 } else { 579 assert.Empty(t, e.Transaction.Type) 580 } 581 } 582 583 func makeError(msg string) error { 584 return errors.New(msg) 585 } 586 587 func sendError(t *testing.T, err error, f ...func(*apm.Error)) model.Error { 588 tracer, r := transporttest.NewRecorderTracer() 589 defer tracer.Close() 590 591 error_ := tracer.NewError(err) 592 for _, f := range f { 593 f(error_) 594 } 595 596 error_.Send() 597 tracer.Flush(nil) 598 599 payloads := r.Payloads() 600 return payloads.Errors[0] 601 } 602 603 type errorsStackTracer struct { 604 message string 605 stackTrace errors.StackTrace 606 } 607 608 func (e *errorsStackTracer) Error() string { 609 return e.message 610 } 611 612 func (e *errorsStackTracer) StackTrace() errors.StackTrace { 613 return e.stackTrace 614 } 615 616 func newErrorsStackTrace(skip, n int) errors.StackTrace { 617 callers := make([]uintptr, 2) 618 callers = callers[:runtime.Callers(1, callers)] 619 620 var ( 621 uintptrType = reflect.TypeOf(uintptr(0)) 622 errorsFrameType = reflect.TypeOf(*new(errors.Frame)) 623 runtimeFrameType = reflect.TypeOf(runtime.Frame{}) 624 ) 625 626 var frames []errors.Frame 627 switch { 628 case errorsFrameType.ConvertibleTo(uintptrType): 629 frames = make([]errors.Frame, len(callers)) 630 for i, pc := range callers { 631 reflect.ValueOf(&frames[i]).Elem().Set(reflect.ValueOf(pc).Convert(errorsFrameType)) 632 } 633 case errorsFrameType.ConvertibleTo(runtimeFrameType): 634 fs := runtime.CallersFrames(callers) 635 for { 636 var frame errors.Frame 637 runtimeFrame, more := fs.Next() 638 reflect.ValueOf(&frame).Elem().Set(reflect.ValueOf(runtimeFrame).Convert(errorsFrameType)) 639 frames = append(frames, frame) 640 if !more { 641 break 642 } 643 } 644 default: 645 panic(fmt.Errorf("unhandled errors.Frame type %s", errorsFrameType)) 646 } 647 return errors.StackTrace(frames) 648 } 649 650 type internalStackTracer struct { 651 message string 652 frames []stacktrace.Frame 653 } 654 655 func (e *internalStackTracer) Error() string { 656 return e.message 657 } 658 659 func (e *internalStackTracer) StackTrace() []stacktrace.Frame { 660 return e.frames 661 } 662 663 type causer struct { 664 error 665 cause error 666 } 667 668 func (c causer) Cause() error { 669 return c.cause 670 } 671 672 type errorslice []error 673 674 func (es errorslice) Error() string { 675 return "errorslice" 676 } 677 678 func (es errorslice) Cause() error { 679 return es[0] 680 } 681 682 type runtimeStackTracer struct { 683 message string 684 trace []uintptr 685 } 686 687 func (rst runtimeStackTracer) Error() string { 688 return rst.message 689 } 690 691 func (rst runtimeStackTracer) StackTrace() *runtime.Frames { 692 return runtime.CallersFrames(rst.trace) 693 }