A Go client library for sending structured logs to the Streamlogia Log Ingestor API.
- Structured log ingestion (DEBUG, INFO, WARN, ERROR)
- Real-time streaming — every entry is sent immediately, no buffering
log/sloghandler integration- HTTP middleware for automatic request/response logging
- Multi-handler fan-out (e.g. stdout + ingestor simultaneously)
- Zero external dependencies — standard library only
Go 1.21 or later (uses log/slog).
go get github.com/streamlogia/go-sdkpackage main
import (
"context"
"github.com/streamlogia/go-sdk/logingestor"
)
func main() {
client := logingestor.New(
"<api-key>",
"<project-id>",
logingestor.WithSource("my-service"),
)
defer client.Close()
ctx := context.Background()
client.Info(ctx, "application started", nil)
client.Info(ctx, "user signed in", map[string]any{"userId": "u_123"}, "auth", "web")
}The Client exposes convenience methods for each log level:
client.Debug(ctx, "cache miss", map[string]any{"key": "session:42"})
client.Info(ctx, "order placed", map[string]any{"orderId": "ord_99", "total": 49.95}, "orders")
client.Warn(ctx, "rate limit approaching", map[string]any{"remaining": 5})
client.Error(ctx, "payment failed", map[string]any{"error": err.Error()}, "payments")Each method accepts:
ctx context.Contextmessage stringmeta map[string]any— optional key/value metadata (passnilif unused)tags ...string— optional tags for filtering
Wrap the client as a slog.Handler to use the standard library logging API:
import "log/slog"
logger := slog.New(logingestor.NewSlogHandler(client))
slog.SetDefault(logger)
slog.Info("payment processed", "amount", 99.99, "currency", "USD")
slog.Error("db query failed", "table", "orders", "error", err)Use MultiHandler to route logs to multiple destinations simultaneously:
logger := slog.New(logingestor.MultiHandler{
slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}),
logingestor.NewSlogHandler(client),
})
slog.SetDefault(logger)Wrap any http.Handler to automatically log every request:
mux := http.NewServeMux()
mux.HandleFunc("/api/orders", ordersHandler)
http.ListenAndServe(":8080", client.HTTPMiddleware(mux))Each request logs method, path, status code, duration, remote address, user agent, and the X-Request-Id header (if present). The log level is determined by the response status code:
| Status range | Level |
|---|---|
| 2xx / 3xx | INFO |
| 4xx | WARN |
| 5xx | ERROR |
Fiber uses fasthttp instead of net/http, so client.HTTPMiddleware is not compatible. Write a small middleware that calls the client directly — *fasthttp.RequestCtx implements context.Context, so it passes straight through.
func streamlogiaMiddleware(client *logingestor.Client) fiber.Handler {
return func(c *fiber.Ctx) error {
start := time.Now()
err := c.Next()
status := c.Response().StatusCode()
meta := map[string]any{
"method": c.Method(),
"path": c.Path(),
"status": status,
"durationMs": time.Since(start).Milliseconds(),
"remoteAddr": c.IP(),
"userAgent": c.Get(fiber.HeaderUserAgent),
}
if rid := c.Get("X-Request-Id"); rid != "" {
meta["requestId"] = rid
}
msg := fmt.Sprintf("%s %s %d", c.Method(), c.Path(), status)
ctx := c.Context() // *fasthttp.RequestCtx satisfies context.Context
switch {
case status >= 500:
client.Error(ctx, msg, meta)
case status >= 400:
client.Warn(ctx, msg, meta)
default:
client.Info(ctx, msg, meta)
}
return err
}
}See examples/fiber/main.go for the full runnable version.
func streamlogiaMiddleware(client *logingestor.Client) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
status := c.Writer.Status()
meta := map[string]any{
"method": c.Request.Method,
"path": c.Request.URL.Path,
"status": status,
"durationMs": time.Since(start).Milliseconds(),
"remoteAddr": c.ClientIP(),
"userAgent": c.Request.UserAgent(),
}
if rid := c.GetHeader("X-Request-Id"); rid != "" {
meta["requestId"] = rid
}
msg := fmt.Sprintf("%s %s %d", c.Request.Method, c.Request.URL.Path, status)
ctx := c.Request.Context()
switch {
case status >= 500:
client.Error(ctx, msg, meta)
case status >= 400:
client.Warn(ctx, msg, meta)
default:
client.Info(ctx, msg, meta)
}
}
}Register it with gin.New() (not gin.Default()) to avoid duplicate logging from Gin's built-in logger:
r := gin.New()
r.Use(streamlogiaMiddleware(client))See examples/gin/main.go for the full runnable version.
func streamlogiaMiddleware(client *logingestor.Client) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
err := next(c)
status := c.Response().Status
req := c.Request()
meta := map[string]any{
"method": req.Method,
"path": req.URL.Path,
"status": status,
"durationMs": time.Since(start).Milliseconds(),
"remoteAddr": c.RealIP(),
"userAgent": req.UserAgent(),
}
if rid := req.Header.Get("X-Request-Id"); rid != "" {
meta["requestId"] = rid
}
msg := fmt.Sprintf("%s %s %d", req.Method, req.URL.Path, status)
ctx := req.Context()
switch {
case status >= 500:
client.Error(ctx, msg, meta)
case status >= 400:
client.Warn(ctx, msg, meta)
default:
client.Info(ctx, msg, meta)
}
return err
}
}
}See examples/echo/main.go for the full runnable version.
Each example is a self-contained module under examples/. Run any of them with:
cd examples/stdlib # or fiber, gin, echo
LOGINGESTOR_API_KEY=<key> LOGINGESTOR_PROJECT_ID=<id> go run .Pass option functions to logingestor.New:
client := logingestor.New(
apiKey,
projectID,
logingestor.WithSource("order-service"),
logingestor.WithHTTPClient(&http.Client{Timeout: 5 * time.Second}),
)| Option | Default | Description |
|---|---|---|
WithSource(s string) |
"unknown" |
Default source field on every log entry |
WithHTTPClient(hc *http.Client) |
default client | Custom HTTP client (timeouts, transport, etc.) |
Call client.Close() (or defer client.Close()) before your process exits. It waits for all in-flight HTTP requests to complete so no entries are dropped.
client := logingestor.New(...)
defer client.Close()type Entry struct {
ProjectID string
Level Level
Message string
Source string
Timestamp *time.Time
Tags []string
Meta map[string]any
}
type IngestResponse struct {
Ingested int
IDs []string
}logingestor.LevelDebug // "DEBUG"
logingestor.LevelInfo // "INFO"
logingestor.LevelWarn // "WARN"
logingestor.LevelError // "ERROR"| Method | Description |
|---|---|
New(apiKey, projectID, opts...) |
Create a new client |
Debug/Info/Warn/Error(ctx, msg, meta, tags...) |
Log at the given level |
Ingest(ctx, entries) |
Send entries immediately, bypassing the queue |
Flush(ctx) |
Drain the internal queue |
Close() |
Flush and stop the background goroutine |
HTTPMiddleware(next http.Handler) |
HTTP middleware for automatic request logging |