LogstackLogstack

Go SDK

Send structured logs from Go services with the official Logstack Go SDK.

Go SDK

The official Go client for Logstack. Batch shipping, context-aware methods, optional stdlib log capture, and graceful shutdown.

Modulegithub.com/mosesedem/logstack/packages/logstack-go-sdk
Versionv1.0.3
Default APIhttps://api.logstack.tech
Ingest pathPOST {APIURL}/v1/logs

Installation

go get github.com/mosesedem/logstack/packages/logstack-go-sdk@v1.0.3

The module lives in a monorepo subdirectory. Tags are packages/logstack-go-sdk/vX.Y.Z (not a root-module tag).

Quick start

package main
 
import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"
 
	logstack "github.com/mosesedem/logstack/packages/logstack-go-sdk"
)
 
func main() {
	client := logstack.NewClient(logstack.Config{
		APIKey:      os.Getenv("LOGSTACK_API_KEY"),
		// APIURL defaults to https://api.logstack.tech
		// For local: "http://localhost:8080"
		Environment: "production",
	})
	defer client.Close()
 
	ctx := context.Background()
 
	_ = client.Info(ctx, "Application started", map[string]interface{}{
		"version": "1.0.0",
	})
 
	// Stdlib log.Print* is captured by default (source: "go-log")
	log.Printf("legacy logger output is shipped automatically")
 
	// Graceful shutdown
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
	<-stop
}

Configuration

client := logstack.NewClient(logstack.Config{
	// Required
	APIKey: "ls_live_xxx",
 
	// Host only — SDK appends /v1/logs. Trailing /v1 is stripped if present.
	APIURL: "https://api.logstack.tech",
 
	// Label stored with every batch: production | staging | development | test
	Environment: "production",
 
	// Buffer size before auto-flush (default 100)
	BatchSize: 100,
 
	// Background flush ticker (default 5s)
	FlushInterval: 5 * time.Second,
 
	// Forward stdlib log package (default true). Disable with Bool(false).
	CaptureStdLog: logstack.Bool(true),
 
	// Called when a send fails (after the attempt)
	OnError: func(err error, logs []logstack.LogEntry) {
		log.Printf("logstack flush failed: %v (%d entries)", err, len(logs))
	},
})
FieldDefaultDescription
APIKeyProject ingest key (ls_live_…)
APIURLhttps://api.logstack.techAPI host (no /v1 required)
EnvironmentproductionEnvironment label on the batch
BatchSize100Max buffered entries before flush
FlushInterval5sBackground flush interval
CaptureStdLogtrueAuto-forward log.Print* / log.Printf
OnErrornilFlush failure callback

Self-hosted:

client := logstack.NewClient(logstack.Config{
	APIKey: os.Getenv("LOGSTACK_API_KEY"),
	APIURL: "https://logs.your-domain.com", // or http://localhost:8080
})

Logging API

All methods take context.Context and optional metadata:

ctx := context.Background()
meta := map[string]interface{}{
	"userId": "user_123",
	"orderId": "ord_456",
}
 
_ = client.Debug(ctx, "cache miss", meta)
_ = client.Info(ctx, "order placed", meta)
_ = client.Warn(ctx, "rate limit approaching", meta)
_ = client.Error(ctx, "payment failed", meta)
_ = client.Critical(ctx, "db primary down", meta)
_ = client.Fatal(ctx, "unrecoverable", meta) // also flushes immediately
LevelMethodNotes
debugDebugVerbose diagnostics
infoInfoNormal operations
warnWarnRecoverable issues
errorErrorFailures
criticalCriticalSevere failures
fatalFatalLogs then flushes

Explicit SDK calls use source: "go-sdk". Auto-captured stdlib lines use source: "go-log".

Stdlib log capture

With CaptureStdLog enabled (default), the SDK redirects log.SetOutput to a passthrough writer:

  • Original destination (usually stderr) is preserved
  • Each line is shipped as an info log with source: "go-log"
  • Re-entrancy is guarded (safe if OnError itself logs)
  • Close() restores the previous writer
// Disable automatic capture
client := logstack.NewClient(logstack.Config{
	APIKey:        os.Getenv("LOGSTACK_API_KEY"),
	CaptureStdLog: logstack.Bool(false),
})

Flush and shutdown

// Manual flush
if err := client.Flush(); err != nil {
	log.Printf("flush: %v", err)
}
 
// Cancellable flush
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = client.FlushContext(ctx)
 
// Always close on exit — flushes remaining buffer, stops ticker, restores log writer
_ = client.Close()

Close() is idempotent (safe to call more than once).

HTTP service example

package main
 
import (
	"context"
	"net/http"
	"os"
	"time"
 
	logstack "github.com/mosesedem/logstack/packages/logstack-go-sdk"
)
 
func main() {
	client := logstack.NewClient(logstack.Config{
		APIKey:      os.Getenv("LOGSTACK_API_KEY"),
		Environment: "production",
	})
	defer client.Close()
 
	mux := http.NewServeMux()
	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(`{"ok":true}`))
 
		_ = client.Info(r.Context(), "GET /health", map[string]interface{}{
			"status":   200,
			"duration": time.Since(start).Milliseconds(),
			"path":     r.URL.Path,
		})
	})
 
	_ = http.ListenAndServe(":8080", mux)
}

Version

import logstack "github.com/mosesedem/logstack/packages/logstack-go-sdk"
 
fmt.Println(logstack.Version) // e.g. "1.0.3"

Troubleshooting

SymptomCheck
No logs in dashboardValid APIKey, network to API host, OnError callback
client is closedLogging after Close() — keep the client open for the process lifetime
Double /v1Pass host only (https://api…); SDK normalizes trailing /v1
Stdlib logs missingEnsure CaptureStdLog is not Bool(false)

On this page