Skip to content

Usage Guide

This guide walks you through configuring a Go project that consumes the github.com/bitstep-ie/mango-go/pkg/... packages. It covers end-to-end setup plus focused examples for each utility package so you can copy, paste, and start shipping features quickly.


1. Project Setup

go env -w GO111MODULE=on
go get github.com/bitstep-ie/mango-go@latest

Inside your module, import the packages you need:

import (
    mangoenv "github.com/bitstep-ie/mango-go/pkg/env"
    mangoio "github.com/bitstep-ie/mango-go/pkg/io"
    mangolog "github.com/bitstep-ie/mango-go/pkg/logger"
    mangonet "github.com/bitstep-ie/mango-go/pkg/net"
    mangorand "github.com/bitstep-ie/mango-go/pkg/random"
    mangoslices "github.com/bitstep-ie/mango-go/pkg/slices"
    mangotime "github.com/bitstep-ie/mango-go/pkg/time"
    testutils "github.com/bitstep-ie/mango-go/pkg/testutils"
)

Tip: keep the mango- prefixes when aliasing packages to avoid name collisions with the stdlib (io, time, slices, etc.).


2. Bootstrapping the Mango Logger

The logger wraps log/slog and supports friendly CLI output, JSON file output (via lumberjack rotation), and syslog simultaneously.

package main

import (
    "context"
    "log/slog"
    mangolog "github.com/bitstep-ie/mango-go/pkg/logger"
)

func main() {
    cfg := &mangolog.LogConfig{
        MangoConfig: &mangolog.MangoConfig{
            Strict: true,
            CorrelationId: &mangolog.CorrelationIdConfig{
                Strict:       true,
                AutoGenerate: true,
            },
        },
        Out: &mangolog.OutConfig{
            Enabled: true,
            Cli: &mangolog.CliConfig{
                Enabled:       true,
                Friendly:      true,
                Verbose:       true,
                FriendlyFormat: ``"[\(.level)] \(.application)::\(.operation) - \(.message)"``,
            },
            File: &mangolog.FileOutputConfig{
                Enabled:    true,
                Debug:      false,
                Path:       "/var/log/my-service.log",
                MaxSize:    25,
                MaxBackups: 5,
                MaxAge:     14,
                Compress:   true,
            },
            Syslog: &mangolog.SyslogConfig{
                Facility: mangolog.SyslogFacilityLocal0,
            },
        },
    }

    handler := mangolog.NewMangoLogger(cfg)
    logger := slog.New(handler)

    ctx := context.WithValue(context.Background(), mangolog.OPERATION, "checkout")
    ctx = context.WithValue(ctx, mangolog.APPLICATION, "orders-api")
    ctx = context.WithValue(ctx, mangolog.TYPE, mangolog.BusinessType)

    logger.InfoContext(ctx, "order created",
        slog.Int("orderID", 42),
        slog.String("status", "submitted"),
    )
}

Key points:

  • Set MangoConfig.Strict to enforce type, application, and operation context values.
  • Enable CorrelationId.AutoGenerate to avoid panics when downstream middleware forgets to set it.
  • CLI output uses jq expressions (DefaultFriendlyFormat) so you can tailor human logs without touching JSON/file/syslog payloads.

3. Managing Environment Variables

package config

import mangoenv "github.com/bitstep-ie/mango-go/pkg/env"

type Config struct {
    Port        int
    EnableHTTPS bool
    DBURL       string
}

func Load() Config {
    port, _ := mangoenv.IntDefault("PORT", 8080)
    enableHTTPS, _ := mangoenv.BoolDefault("ENABLE_HTTPS", false)

    return Config{
        Port:        port,
        EnableHTTPS: enableHTTPS,
        DBURL:       mangoenv.MustString("DATABASE_URL"),
    }
}
  • Use *Default helpers (for example StringDefault, IntDefault, BoolDefault) for soft defaults.
  • Prefer Must* variants for critical values so misconfiguration fails fast during startup.
  • Deprecated legacy helpers (EnvOrDefault, MustEnv, EnvAsInt, MustEnvAsInt, EnvAsBool, MustEnvAsBool) are scheduled for removal in v1.0.0.

4. Network Helpers (pkg/net)

Use pkg/net when inputs may carry IP literals (allow-lists, headers, webhook payloads, or config fields) and you need quick syntactic checks.

package validate

import mangonet "github.com/bitstep-ie/mango-go/pkg/net"

func IsAllowedIP(ip string) bool {
    return mangonet.IsValidIP(ip)
}

func IsValidWebhookURL(url string) bool {
    return mangonet.IsValidURL(url)
}
  • IsValidIP accepts both IPv4 and IPv6.
  • IsValidIPv4 accepts only valid IPv4 addresses.
  • IsValidIPv6 accepts only valid IPv6 addresses (including IPv4-mapped forms like ::ffff:192.168.1.1).
  • IsValidURL accepts URLs with a required scheme and host, rejecting permissive edge cases.
  • Both helpers are format checks only; they do not verify DNS/host reachability.

5. File Maintenance Helpers (pkg/io)

package backup

import mangoio "github.com/bitstep-ie/mango-go/pkg/io"

func RotateConfigs(dir string) error {
    if err := mangoio.BackupFilesWithExt(dir, []string{".yaml", ".json"}); err != nil {
        return err
    }

    // run your migration/update logic here

    return mangoio.RestoreAllBakFiles(dir)
}

DeleteFileWithExt works the same way and is handy for cleaning generated artifacts (.tmp, .bak, etc.). The helpers walk the entire tree, so point them at the narrowest directory possible.


6. Random Data Recipes (pkg/random)

package demo

import (
    "time"
    mangorand "github.com/bitstep-ie/mango-go/pkg/random"
)

func init() {
    rand.Seed(time.Now().UnixNano()) // only needed once per process
}

func buildFixtures() {
    run := mangorand.Number[int](1, 5)
    coinFlip := mangorand.Bool()
    promoCode := mangorand.String(10)
    securePassword := mangorand.Password(24, mangorand.PasswordOptions{
        Letters: true,
        Digits:  true,
        Symbols: true,
        Exclude: "O0l1",
    })
    randomRunAt := mangorand.Date(time.Now().Add(-30*24*time.Hour), time.Now())

    _ = []any{run, coinFlip, promoCode, securePassword, randomRunAt}
}

The package mixes math/rand conveniences with crypto-safe building blocks (Byte, Password). Always seed math/rand in your main package if you rely on Number, Bool, Choice, etc.


7. Slice Utilities

events := []string{"created", "updated", "deleted", "deleted"}
unique := mangoslices.Unique(events) // ["created","updated","deleted"]

if mangoslices.Contains(events, "created") {
    idx := mangoslices.IndexOf(events, "deleted") // first match
    copy := append([]string(nil), events...)
    mangoslices.Reverse(copy)
}

pairs := mangoslices.EqualsIgnoreOrder(
    []int{1, 2, 2, 3},
    []int{2, 1, 3, 2},
) // true

batched := mangoslices.Chunk(events, 2)

Generics keep these helpers type-safe and allocation-friendly. Chunk panics when size <= 0; validate user input before calling it.


8. Working with Time

now := time.Now()
start := mangotime.StartOfDay(now)
end := mangotime.EndOfDay(now)

if mangotime.IsToday(now) {
    fmt.Println("still today")
}

deadline, err := mangotime.ParseDuration("1w12h30m")
if err != nil { /* handle */ }

fmt.Println(mangotime.TimeAgo(time.Now().Add(-90 * time.Minute))) // "1 hour ago"

ParseDuration extends time.ParseDuration with d and w suffixes (supports floats like 1.5d). TimeAgo returns human-friendly English strings for UI messaging.


9. Test Utilities

func TestProcessor(t *testing.T) {
    tmp := testutils.MustMakeTempFile(t, t.TempDir(), "payload-*.json")

    payload := uuid.NewString()
    require.NoError(t, os.WriteFile(tmp.Name(), []byte(payload), 0o600))

    runProcessor(tmp.Name())

    testutils.AssertValidUUID(t, got.ID, "logId")
    testutils.ContainsAllRunes(t, got.Token, "!@#", "token should include special chars")
}
  • MustMakeTempFile simplifies fixture creation; it will close the handle automatically.
  • AssertValidUUID and ContainsAllRunes piggyback on stretchr/testify for idiomatic assertions.

10. Putting It All Together

Here’s a minimal HTTP handler showcasing multiple packages:

type Server struct {
    logger *slog.Logger
    cfg    Config
}

func NewServer(cfg Config) *Server {
    handler := mangolog.NewMangoLogger(buildLogConfig(cfg))
    return &Server{
        logger: slog.New(handler),
        cfg:    cfg,
    }
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    ctx := context.WithValue(r.Context(), mangolog.APPLICATION, "billing-api")
    ctx = context.WithValue(ctx, mangolog.OPERATION, "invoice-create")
    ctx = context.WithValue(ctx, mangolog.TYPE, mangolog.BusinessType)

    clientIP := r.Header.Get("X-Forwarded-For")
    if clientIP != "" && !mangonet.IsValidIPv4(clientIP) && !mangonet.IsValidIPv6(clientIP) {
        s.logger.WarnContext(ctx, "invalid client ip header", slog.String("clientIP", clientIP))
    }

    reqID := r.Header.Get("X-Request-ID")
    if reqID == "" {
        reqID = mangorand.String(12)
    }
    ctx = context.WithValue(ctx, mangolog.CORRELATION_ID, reqID)

    s.logger.InfoContext(ctx, "processing request",
        slog.String("method", r.Method),
        slog.String("path", r.URL.Path),
        slog.String("receivedAt", mangotime.StartOfDay(time.Now()).Format(mangolog.RFC3339NanoMC)),
    )

    w.WriteHeader(http.StatusAccepted)
}

This pattern scales: standardize context values at the edge, use Mango Logger for structured telemetry, derive configuration via pkg/env, and lean on the helper packages for the rest.


11. Next Steps

  1. Browse docs/mango-go/packages/*.md for deeper API details.
  2. Wire these helpers into your project’s scaffolding (CLI, HTTP server, worker, etc.).
  3. Contribute improvements or new helpers by following contributing.md.