func()

in module/apmslog/handler.go [69:156]


func (h *ApmHandler) Handle(ctx context.Context, r slog.Record) error {
	// attempt to extract any available trace info from context
	var traceId apm.TraceID
	var transactionId apm.SpanID
	var parentId apm.SpanID
	if tx := apm.TransactionFromContext(ctx); tx != nil {
		traceId = tx.TraceContext().Trace
		transactionId = tx.TraceContext().Span
		parentId = tx.TraceContext().Span
		// add trace/transaction ids to slog record to be logged
		r.Add(FieldKeyTraceID, traceId)
		r.Add(FieldKeyTransactionID, transactionId)
	}
	if span := apm.SpanFromContext(ctx); span != nil {
		parentId = span.TraceContext().Span
		// add span id to slog record to be logged
		r.Add(FieldKeySpanID, parentId)
	}

	// report record as APM error
	if h.tracer != nil && h.tracer.Recording() && slices.Contains(h.reportLevels, r.Level) {

		// attempt to find error attributes
		// slog doesnt have a standard way of attaching an
		// error to a record, so attempting to grab any attribute
		// that has error/err keys OR keys user has defined as reportable
		// and extracting the values seems like a likely way to do it.
		errorsToAttach := []error{}
		r.Attrs(func(a slog.Attr) bool {
			if slices.Contains(h.errorRecordAttrs, a.Key) {
				var err error
				// first check if value is of error type to retain as much info as possible
				if v, ok := a.Value.Any().(error); ok {
					errorsToAttach = append(errorsToAttach, v)
					// else just convert reportable error value as string
				} else {
					errorsToAttach = append(errorsToAttach, errors.Join(err, fmt.Errorf("%s", a.Value.String())))
				}
			}
			return true
		})

		// If there are multiple reportable error attributes, create a new
		// apm.ErrorLogRecord for each. Otherwise just create one apm.ErrorLogRecord
		// with no Error.
		errLogRecords := []apm.ErrorLogRecord{}
		if len(errorsToAttach) == 0 {
			errRecord := apm.ErrorLogRecord{
				Message: r.Message,
				Level:   strings.ToLower(r.Level.String()),
			}
			errLogRecords = append(errLogRecords, errRecord)
		} else {
			for _, err := range errorsToAttach {
				errRecord := apm.ErrorLogRecord{
					Message: r.Message,
					Level:   strings.ToLower(r.Level.String()),
					Error:   err,
				}
				errLogRecords = append(errLogRecords, errRecord)
			}
		}

		// for each errRecord, send to apm
		for _, errRecord := range errLogRecords {
			errlog := h.tracer.NewErrorLog(errRecord)
			errlog.Handled = true
			errlog.Timestamp = r.Time.UTC()
			errlog.SetStacktrace(2)

			// add available trace info if not zero type
			if traceId != (apm.TraceID{}) {
				errlog.TraceID = traceId
			}
			if transactionId != (apm.SpanID{}) {
				errlog.TransactionID = transactionId
			}
			if parentId != (apm.SpanID{}) {
				errlog.ParentID = parentId
			}
			// send error to APM
			errlog.Send()

		}
	}

	return h.handler.Handle(ctx, r)
}