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  }