reporter

package module
v1.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 6, 2026 License: MIT Imports: 9 Imported by: 0

README

Reporter

reporter is a small Go package for turning ordinary errors into structured error reports. It captures the caller file path, line number, function name, error description, raw error text, service name, environment, and timestamp. In production, the same structured payload can be published to Kafka so another service can forward the alert to Telegram or any other notification channel.

What It Produces

Each wrapped error is represented as CustomError:

{
  "timestamp": "2026-06-04 14:35:12",
  "environment": "production",
  "service": "payment-service",
  "error_type": "DATABASE_CONSTRAINT",
  "description": "Failed to save data because of a duplicate data conflict",
  "raw_error": "duplicate key value violates unique constraint",
  "file": "service/order.go",
  "line": 42,
  "function": "service.CreateOrder"
}

In non-production environments, err.Error() returns a colored terminal-friendly message. In production, err.Error() returns JSON.

Configuration

Call reporter.Init(config) once during service startup. Configuration is passed explicitly through reporter.Config, so this package does not read environment variables directly.

reporter.Init(reporter.Config{
    AppName:          "payment-service",
    AppEnv:           "development",
    EnablePublishing: false,
})
defer reporter.Close()

Config fields:

Field Required Description
AppName No Service name included in every report. Defaults to unknown-service.
AppEnv No Runtime environment. Defaults to development. production makes Error() return JSON.
KafkaBrokers Kafka Kafka broker list, for example []string{"kafka-1:9092", "kafka-2:9092"}.
KafkaTopic Kafka Kafka topic used for alert messages.
EnablePublishing No Enables Kafka publishing when KafkaBrokers and KafkaTopic are also provided. Defaults to false.

Kafka publishing is non-blocking. When EnablePublishing=true and Kafka configuration is complete, the package sends the JSON payload in a background goroutine. If Kafka publishing fails, the failure is written to stderr so the original error is not lost.

If your application stores config in environment variables, read and map them in your own service before calling Init:

reporter.Init(reporter.Config{
    AppName:          os.Getenv("APP_NAME"),
    AppEnv:           os.Getenv("APP_ENV"),
    KafkaBrokers:     strings.Split(os.Getenv("KAFKA_BROKERS"), ","),
    KafkaTopic:       os.Getenv("KAFKA_TOPIC"),
    EnablePublishing: os.Getenv("APP_ENV") == "production",
})

Installation

go get github.com/learncodexx/reporter

If this package is used from a private/local module, replace the module path with your repository import path.

Basic Usage

package main

import (
    "errors"
    "fmt"

    "github.com/learncodexx/reporter"
)

func main() {
    reporter.Init(reporter.Config{
        AppName: "example-service",
        AppEnv:  "development",
    })
    defer reporter.Close()

    err := doWork()
    if err != nil {
        wrappedErr := reporter.AutoWrap(err)
        fmt.Println(wrappedErr.Error())
        return
    }
}

func doWork() error {
    return errors.New("connection refused")
}

AutoWrap inspects the raw error text and assigns a useful error_type and description when it recognizes common patterns.

Custom Description

Use Wrap when the application already knows the business context and you want to provide a specific description.

err := repository.SaveOrder(order)
if err != nil {
    return reporter.Wrap(err, "Failed to save checkout order after payment was confirmed")
}

This keeps the original error in raw_error while adding a human-readable explanation in description.

Automatic Error Classification

AutoWrap currently recognizes these common error families:

Error Type Matched Text Description Purpose
INFRASTRUCTURE_ERROR connection refused, timeout, dial tcp Network, database, or third-party connectivity failures.
DATABASE_CONSTRAINT duplicate key, violates unique constraint Duplicate or uniqueness conflicts while saving data.
TIMEOUT_ERROR context deadline exceeded Work stopped because the execution deadline was reached.
DATA_NOT_FOUND no rows in result set, not found Requested data does not exist.
GENERAL_ERROR Anything else Fallback for errors that do not match known patterns.

Production Kafka Example

package main

import (
    "log"

    "github.com/learncodexx/reporter"
)

func main() {
    reporter.Init(reporter.Config{
        AppName:          "payment-service",
        AppEnv:           "production",
        KafkaBrokers:     []string{"kafka-1:9092", "kafka-2:9092"},
        KafkaTopic:       "service-alerts",
        EnablePublishing: true,
    })
    defer reporter.Close()

    if err := run(); err != nil {
        log.Println(reporter.AutoWrap(err))
    }
}

The Kafka message value is the JSON CustomError payload. The message key is the service name, which helps consumers group alerts by service. A Telegram alert worker can consume KAFKA_TOPIC, decode the JSON, and format a Telegram message using service, environment, file, line, error_type, description, and raw_error.

Telegram Alert Message Example

A Kafka consumer can convert the JSON payload into a message like this:

[production] payment-service
DATABASE_CONSTRAINT
Failed to save data because of a duplicate data conflict

Location: service/order.go:42
Function: service.CreateOrder
Raw error: duplicate key value violates unique constraint
Time: 2026-06-04 14:35:12

API Summary

type Config struct {
    AppName          string
    AppEnv           string
    KafkaBrokers     []string
    KafkaTopic       string
    EnablePublishing bool
}

func Init(cfg Config)
func Close()
func AutoWrap(err error) error
func Wrap(err error, customDesc string) error
  • Init(cfg) stores reporter configuration and prepares Kafka publishing when EnablePublishing, KafkaBrokers, and KafkaTopic are complete.
  • Close() closes the Kafka writer during graceful shutdown.
  • AutoWrap(err) returns nil for nil input, otherwise returns a structured CustomError with automatic classification.
  • Wrap(err, customDesc) returns nil for nil input, otherwise returns a structured CustomError using your custom description.

The returned error can be type-asserted to *reporter.CustomError when you need direct access to fields such as ErrorType, File, Line, or FunctionName:

err := reporter.AutoWrap(rawErr)
if customErr, ok := err.(*reporter.CustomError); ok {
    log.Println(customErr.ErrorType, customErr.File, customErr.Line)
}

Internal Helpers

The package also has several unexported helper functions used internally:

Function Purpose
newError Builds CustomError, captures caller metadata, prints it, and triggers Kafka publishing when enabled.
sendToKafka Serializes CustomError to JSON and writes it to Kafka.
containsAny Checks whether a string contains at least one expected substring.
byteContains Performs byte-level substring matching for internal checks.
jsonErrTextLower Converts ASCII uppercase letters to lowercase for internal normalization.

These helpers are not exported, so application code should use only Init, Close, AutoWrap, Wrap, Config, and CustomError.

Notes

  • Always call Init(reporter.Config{...}) before wrapping errors if you want service, environment, and Kafka publishing to be configured correctly.
  • Always call Close() during shutdown in services that publish to Kafka.
  • Do not use this package as a replacement for normal application error handling. It is intended for reporting and alerting.

Documentation

Overview

Package reporter turns ordinary Go errors into structured reports that are suitable for local logs, production JSON logs, and Kafka-based alerting.

A report includes the caller file path, line number, function name, service, environment, timestamp, raw error, error type, and human-readable description. In production, reports can be published to Kafka so a separate worker can forward the alert to Telegram or another notification channel.

Initialize the package once during application startup:

reporter.Init(reporter.Config{
	AppName: "payment-service",
	AppEnv:  "development",
})
defer reporter.Close()

Use AutoWrap when reporter should classify the error from its text:

if err := run(); err != nil {
	return reporter.AutoWrap(err)
}

Use Wrap when the application can provide better business context:

if err := repository.SaveOrder(order); err != nil {
	return reporter.Wrap(err, "Failed to save checkout order after payment was confirmed")
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AutoWrap

func AutoWrap(err error) error

AutoWrap converts an ordinary error into a structured report with automatic classification.

AutoWrap returns nil when err is nil. Otherwise it inspects err.Error() and assigns an error type and description for known patterns such as connection failures, duplicate keys, deadline timeouts, and missing data. The returned error captures the caller file path, line number, and function name. In production, the report is also published to Kafka when Init configured a writer.

Example:

if err := repository.FindUser(id); err != nil {
	return reporter.AutoWrap(err)
}

func Close

func Close()

Close releases the Kafka writer used by reporter.

Call Close during graceful shutdown after Init has been called. It is safe to call even when Kafka publishing is not enabled.

Example:

defer reporter.Close()

func Init

func Init(cfg Config)

Init applies reporter configuration for the current service.

Call Init once during application startup before using AutoWrap or Wrap. It stores the service name, environment, and optional Kafka settings. When EnablePublishing is true and Kafka settings are complete, Init prepares a Kafka writer so every reported error can be published asynchronously.

Example:

reporter.Init(reporter.Config{
	AppName: "payment-service",
	AppEnv:  "development",
})
defer reporter.Close()

func Wrap

func Wrap(err error, customDesc string) error

Wrap converts an ordinary error into a structured report with a custom description.

Wrap returns nil when err is nil. Use Wrap when the application can provide better business context than automatic classification. The original error is preserved in RawError, while customDesc is stored in Description. The returned error captures the caller file path, line number, and function name. In production, the report is also published to Kafka when Init configured a writer.

Example:

if err := repository.SaveOrder(order); err != nil {
	return reporter.Wrap(err, "Failed to save checkout order after payment was confirmed")
}

Types

type Config added in v1.0.4

type Config struct {
	AppName          string
	AppEnv           string
	KafkaBrokers     []string
	KafkaTopic       string
	EnablePublishing bool
}

Config holds all the configuration parameters required to initialize the reporter. Passing this struct explicitly via function parameters provides better flexibility and decouples the package from direct environment variable access.

type CustomError

type CustomError struct {
	Timestamp    string `json:"timestamp"`
	Environment  string `json:"environment"`
	Service      string `json:"service"`
	ErrorType    string `json:"error_type"`
	Description  string `json:"description"`
	RawError     string `json:"raw_error"`
	File         string `json:"file"`
	Line         int    `json:"line"`
	FunctionName string `json:"function"`
}

CustomError is the structured error payload produced by this package.

It contains the information needed for logs and alerts: timestamp, environment, service name, error type, human-readable description, raw error text, caller file path, caller line number, and caller function name. In production, this structure is serialized to JSON and can be published to Kafka for downstream alert delivery, such as Telegram notifications.

func (*CustomError) Error

func (e *CustomError) Error() string

Error returns a formatted representation of the structured error.

In non-production environments it returns a colored terminal-friendly string containing the timestamp, file, line, error type, description, and raw error. In production it returns the JSON representation of CustomError, which is suitable for logs, Kafka messages, and alert consumers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL