Skip to content

A Go (Golang) package for rich, structured error handling with full stack-trace support, error wrapping, classification, and formatting.

License

Notifications You must be signed in to change notification settings

hueristiq/hq-go-errors

Repository files navigation

hq-go-errors

made with go go report card license maintenance open issues closed issues contribution

hq-go-errors is a Go (Golang) package for rich, structured error handling with full stack-trace support, error wrapping, classification, and formatting.

Resource

Features

  • Full Stack Traces: Capture detailed call stacks at error creation and wrap points, with customizable formatting (e.g., reverse order, separators).
  • Error Chaining: Wrap errors to add context while preserving the original stack trace and error details.
  • Error Classification: Assign ErrorType values to categorize errors for programmatic handling.
  • Structured Fields: Attach arbitrary key-value metadata (e.g., request IDs, parameters) to errors for enhanced debugging.
  • Multi-Error Support: Join multiple errors into a single error object with a shared stack trace.
  • Flexible Formatting: Render errors as human-readable strings or JSON-like maps, with options to include/exclude stack traces, invert chain order, or handle external errors.
  • Standards-Compliant: Implements Go’s standard error, Unwrap, Is, and As interfaces, plus additional helpers like Cause for root cause analysis.

Installation

To install hq-go-errors, run the following command in your Go project:

go get -v -u github.com/hueristiq/hq-go-errors

Make sure your Go environment is set up properly (Go 1.13 or later is recommended).

Usage

Creating Errors

Use New to create a root error with a full call stack captured at the point of invocation.

package main

import (
	"fmt"

	hqgoerrors "github.com/hueristiq/hq-go-errors"
)

func main() {
	err := hqgoerrors.New("failed to initialize database")
	if err != nil {
		fmt.Println(err.Error())
	}
}

Wrapping Errors

Use Wrap to add context to an existing error, capturing a single stack frame at the wrap point while preserving the original error’s stack trace.

package main

import (
	"fmt"

	hqgoerrors "github.com/hueristiq/hq-go-errors"
)

func loadConfig() error {
	return hqgoerrors.New("cannot read config file")
}

func main() {
	if err := loadConfig(); err != nil {
		err = hqgoerrors.Wrap(err, "failed to load configuration")

		fmt.Println(err.Error())
	}
}

Joining Multiple Errors

Use Join to combine multiple errors into a single error object, capturing a stack trace at the join point.

package main

import (
	"fmt"

	hqgoerrors "github.com/hueristiq/hq-go-errors"
)

func task1() error {
	return hqgoerrors.New("task 1 failed")
}

func task2() error {
	return hqgoerrors.New("task 2 failed")
}

func main() {
	err1 := task1()
	err2 := task2()

	err := hqgoerrors.Join(err1, err2)
	if err != nil {
		fmt.Println(err.Error())
	}
}

Structured Types & Fields

You can classify errors and attach structured data:

err := hqgoerrors.New("payment declined",
	hqgoerrors.WithType("PaymentError"),
	hqgoerrors.WithField("order_id", 1234),
	hqgoerrors.WithField("amount", 49.95),
)
  • Retrieve:

     if e, ok := err.(hqgoerrors.Error); ok {
     	fmt.Println("Type:", e.Type())
     	fmt.Println("Fields:", e.Fields())
     }

Unwrapping, Is, As, and Cause

  • Standard Unwrap:

     next := hqgoerrors.Unwrap(err)
  • Deep equality:

     if hqgoerrors.Is(err, targetErr) { … }
  • Type assertion:

     var myErr *hqgoerrors.Error
    
     if hqgoerrors.As(err, &myErr) {
     	fmt.Println("Got:", myErr.Msg)
     }
  • Root cause:

     cause := hqgoerrors.Cause(err)

Formatting Errors

... to String

package main

import (
	"fmt"

	hqgoerrors "github.com/hueristiq/hq-go-errors"
)

func main() {
	err := hqgoerrors.New("root error example!", hqgoerrors.WithType("ERROR_TYPE"), hqgoerrors.WithField("FIELD_KEY_1", "FIELD_VALUE_1"), hqgoerrors.WithField("FIELD_KEY_2", "FIELD_VALUE_2"))

	err = hqgoerrors.Wrap(err, "wrap error example 1!")
	err = hqgoerrors.Wrap(err, "wrap error example 2!", hqgoerrors.WithType("ERROR_TYPE_2"), hqgoerrors.WithField("FIELD_KEY_1", "FIELD_VALUE_1"), hqgoerrors.WithField("FIELD_KEY_2", "FIELD_VALUE_2"))

	formattedStr := hqgoerrors.ToString(err, hqgoerrors.FormatWithTrace())

	fmt.Println(formattedStr)
}

output:

[ERROR_TYPE_2] wrap error example 2!

Fields:
  FIELD_KEY_1: FIELD_VALUE_1
  FIELD_KEY_2: FIELD_VALUE_2

wrap Trace:
  main.main (/home/.../hq-go-errors/examples/basic/main.go:13)

wrap error example 1!

wrap Trace:
  main.main (/home/.../hq-go-errors/examples/basic/main.go:12)

[ERROR_TYPE] root error example!

Fields:
  FIELD_KEY_1: FIELD_VALUE_1
  FIELD_KEY_2: FIELD_VALUE_2

root Trace:
  main.main (/home/.../hq-go-errors/examples/basic/main.go:10)
  main.main (/home/.../hq-go-errors/examples/basic/main.go:12)
  main.main (/home/.../hq-go-errors/examples/basic/main.go:13)

... to JSON

package main

import (
	"encoding/json"
	"fmt"

	hqgoerrors "github.com/hueristiq/hq-go-errors"
)

func main() {
	err := hqgoerrors.New("root error example!", hqgoerrors.WithType("ERROR_TYPE"), hqgoerrors.WithField("FIELD_KEY_1", "FIELD_VALUE_1"), hqgoerrors.WithField("FIELD_KEY_2", "FIELD_VALUE_2"))

	err = hqgoerrors.Wrap(err, "wrap error example 1!")
	err = hqgoerrors.Wrap(err, "wrap error example 2!", hqgoerrors.WithType("ERROR_TYPE_2"), hqgoerrors.WithField("FIELD_KEY_1", "FIELD_VALUE_1"), hqgoerrors.WithField("FIELD_KEY_2", "FIELD_VALUE_2"))

	formattedJSON := hqgoerrors.ToJSON(err, hqgoerrors.FormatWithTrace())

	bytes, _ := json.Marshal(formattedJSON)

	fmt.Println(string(bytes))
}

output:

{
  "chain": [
    {
      "fields": {
        "FIELD_KEY_1": "FIELD_VALUE_1",
        "FIELD_KEY_2": "FIELD_VALUE_2"
      },
      "message": "wrap error example 2!",
      "stack": [
        {
          "file": "/home/.../hq-go-errors/examples/basic/main.go",
          "function": "main.main",
          "line": 13
        }
      ],
      "type": "ERROR_TYPE_2"
    },
    {
      "message": "wrap error example 1!",
      "stack": [
        {
          "file": "/home/.../hq-go-errors/examples/basic/main.go",
          "function": "main.main",
          "line": 12
        }
      ]
    }
  ],
  "root": {
    "fields": {
      "FIELD_KEY_1": "FIELD_VALUE_1",
      "FIELD_KEY_2": "FIELD_VALUE_2"
    },
    "message": "root error example!",
    "stack": [
      {
        "file": "/home/.../hq-go-errors/examples/basic/main.go",
        "function": "main.main",
        "line": 10
      },
      {
        "file": "/home/.../hq-go-errors/examples/basic/main.go",
        "function": "main.main",
        "line": 12
      },
      {
        "file": "/home/.../hq-go-errors/examples/basic/main.go",
        "function": "main.main",
        "line": 13
      }
    ],
    "type": "ERROR_TYPE"
  }
}

Contributing

Contributions are welcome and encouraged! Feel free to submit Pull Requests or report Issues. For more details, check out the contribution guidelines.

A big thank you to all the contributors for your ongoing support!

contributors

Licensing

This package is licensed under the MIT license. You are free to use, modify, and distribute it, as long as you follow the terms of the license. You can find the full license text in the repository - Full MIT license text.

About

A Go (Golang) package for rich, structured error handling with full stack-trace support, error wrapping, classification, and formatting.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •