Go Integration
ScryWatch accepts logs over HTTP. Use Go’s standard net/http package — no SDK or extra dependencies required. For distributed traces, send OTLP via the OpenTelemetry Go SDK.
What you’ll need
- A ScryWatch project API key (see API Keys & Settings)
- Go 1.21+
Log ingest endpoint
POST https://ingest.scrywatch.com/v1/ingest
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Step 1: Minimal log helper
package scrywatch
import (
"bytes"
"context"
"encoding/json"
"net/http"
"time"
)
// LogEvent is a single log entry sent to ScryWatch.
type LogEvent struct {
Timestamp int64 `json:"timestamp"`
Level string `json:"level"`
Type string `json:"type"`
Message string `json:"message"`
Service string `json:"service,omitempty"`
Environment string `json:"environment,omitempty"`
UserID string `json:"user_id,omitempty"`
SessionID string `json:"session_id,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
// Client sends log events to ScryWatch.
type Client struct {
endpoint string
apiKey string
http *http.Client
}
// NewClient creates a new ScryWatch client.
func NewClient(endpoint, apiKey string) *Client {
return &Client{
endpoint: endpoint,
apiKey: apiKey,
http: &http.Client{Timeout: 5 * time.Second},
}
}
// Log sends a single log event.
func (c *Client) Log(ctx context.Context, level, message string, metadata map[string]any) error {
return c.LogBatch(ctx, []LogEvent{{
Timestamp: time.Now().UnixMilli(),
Level: level,
Type: "custom",
Message: message,
Metadata: metadata,
}})
}
// LogBatch sends multiple log events in one request (up to 250).
func (c *Client) LogBatch(ctx context.Context, events []LogEvent) error {
payload, err := json.Marshal(map[string]any{"events": events})
if err != nil {
return err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpoint+"/v1/ingest", bytes.NewReader(payload))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+c.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.http.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
Step 2: Usage
package main
import (
"context"
"log"
)
func main() {
sw := scrywatch.NewClient("https://ingest.scrywatch.com", "YOUR_API_KEY")
if err := sw.Log(context.Background(), "info", "Server started", map[string]any{
"port": 8080,
}); err != nil {
log.Printf("scrywatch: %v", err)
}
}
Batch events
Use LogBatch to send up to 250 events in a single request, reducing HTTP overhead for high-volume services:
events := []scrywatch.LogEvent{
{Timestamp: time.Now().UnixMilli(), Level: "info", Type: "custom", Message: "Request received", Service: "api"},
{Timestamp: time.Now().UnixMilli(), Level: "error", Type: "custom", Message: "Upstream timeout", Metadata: map[string]any{"target": "payments"}},
}
if err := sw.LogBatch(context.Background(), events); err != nil {
log.Printf("scrywatch batch: %v", err)
}
Step 3: Traces via OpenTelemetry
ScryWatch accepts OTLP/HTTP traces from the standard OpenTelemetry Go SDK.
⚠️ OTLP support is traces-only today. OTLP log ingestion is not yet supported by ScryWatch — use the HTTP ingest above for logs. Also note: the OpenTelemetry Go Logs Bridge API is still experimental as of early 2026; even when ScryWatch adds support, you should evaluate stability before production use.
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/sdk/trace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp \
go.opentelemetry.io/otel/semconv/v1.21.0
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
)
func initTracer(ctx context.Context) (*trace.TracerProvider, error) {
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpoint("api.scrywatch.com"),
otlptracehttp.WithURLPath("/api/traces/otlp"),
otlptracehttp.WithHeaders(map[string]string{
"Authorization": "Bearer YOUR_API_KEY",
}),
)
if err != nil {
return nil, err
}
res, _ := resource.New(ctx,
resource.WithAttributes(semconv.ServiceName("my-go-app")),
)
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(res),
)
otel.SetTracerProvider(tp)
return tp, nil
}
// Call at startup
func main() {
ctx := context.Background()
tp, err := initTracer(ctx)
if err != nil {
log.Fatal(err)
}
defer tp.Shutdown(ctx)
// Now use otel.Tracer("my-service") as normal
}
NDJSON format (alternative)
ScryWatch also accepts NDJSON (one JSON object per line):
req.Header.Set("Content-Type", "application/x-ndjson")
// Body: one JSON log object per line, no wrapping array needed
Production tip: OpenTelemetry Collector
Rather than exporting directly from each service, deploy an OpenTelemetry Collector as a sidecar or DaemonSet and forward to ScryWatch from there. This decouples your services from ScryWatch’s endpoint — if you change backends, only the Collector config changes. See the Kubernetes guide.
Event fields reference
| Field | Type | Required | Description |
|---|---|---|---|
timestamp | number | ✅ | Unix milliseconds — use time.Now().UnixMilli() |
level | string | ✅ | info | warn | error | debug |
type | string | ✅ | custom | crash | session | navigation | api_call |
message | string | ✅ | Log message |
service | string | — | Service name |
environment | string | — | e.g. production, staging |
user_id | string | — | User identifier |
metadata | object | — | Any additional key/value pairs |
See also
- OpenTelemetry guide — OTLP traces setup and support matrix
- Kubernetes guide — deploying an OTel Collector in your cluster
- Distributed Tracing guide — understanding traces in ScryWatch
- API Keys & Settings — getting your project API key