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