Local debugging guide — Go
This guide covers everything specific to debugging an Extend Service Extension app written in Go. For concepts that apply to all languages — architecture, environment variables, log format, and common issues — see the main debugging guide.
Prerequisites
-
Go 1.24 or later — verify with:
go version
# Expected: go version go1.24.x ... -
VS Code Go extension (
golang.go) — install from the VS Code marketplace or accept the recommended extensions prompt when you open the repository. -
Delve (Go debugger) — the Go VS Code extension installs Delve automatically. Verify with:
dlv version -
AccelByte credentials —
AB_BASE_URL,AB_CLIENT_ID,AB_CLIENT_SECRET, andBASE_PATH. See Environment setup in the main guide.
Project structure
| File / Package | What it does |
|---|---|
main.go | Entry point — wires together gRPC server, gRPC-Gateway, metrics, tracing, and auth. |
pkg/service/myService.go | Your business logic — implements the gRPC service methods. |
pkg/storage/storage.go | Talks to AccelByte CloudSave to persist and retrieve data. |
pkg/common/authServerInterceptor.go | Validates every incoming request's IAM token and permission. |
pkg/common/logging.go | Bridges the gRPC middleware logger to Go's slog. |
pkg/common/tracerProvider.go | Sets up OpenTelemetry distributed tracing (Zipkin exporter). |
pkg/proto/service.proto | Defines the gRPC API — endpoints, request/response shapes, and required permissions. |
pkg/pb/ | Auto-generated Go code from .proto. Do not edit directly. |
Port numbers (constants in main.go):
| Port | Purpose |
|---|---|
6565 | gRPC server (internal — used by gRPC-Gateway) |
8000 | gRPC-Gateway HTTP/REST — call this from a browser, Postman, or curl |
8080 | Prometheus metrics endpoint (/metrics) |
Running the service locally
From the terminal
# Export all variables from your .env file
export $(grep -v '^#' .env | xargs)
go run main.go
From VS Code
Use Terminal → Run Task → "Run: Service".
This task is defined in .vscode/tasks.json and prompts you for BASE_PATH if it is not
already set.
Confirming the service is up
You should see logs like:
{"time":"...","level":"INFO","msg":"app server started","service":"extend-app-service-extension"}
{"time":"...","level":"INFO","msg":"starting gRPC-Gateway HTTP server","port":8000}
{"time":"...","level":"INFO","msg":"serving prometheus metrics","port":8080,"endpoint":"/metrics"}
Open http://localhost:8000<BASE_PATH>/apidocs/ in your browser to verify Swagger UI is live.
Attaching the debugger
VS Code (recommended)
The repository ships with a ready-to-use launch configuration in .vscode/launch.json:
{
"name": "Debug: Service",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
}
Steps:
- Fill in
.env(see Environment setup). - Open the Run and Debug panel (
Ctrl+Shift+D/Cmd+Shift+D). - Select "Debug: Service" from the dropdown.
- Press F5.
The Go extension uses Delve under the hood. You do not need to configure it separately.
Other IDEs — Delve headless mode
Start Delve in headless mode so your IDE can connect to it:
dlv debug --headless --listen=:2345 --api-version=2 .
Then configure your IDE to attach to localhost:2345 via DAP (Debug Adapter Protocol).
Where to put breakpoints
| What you want to investigate | File and location |
|---|---|
| A specific REST endpoint being called | pkg/service/myService.go — top of the relevant method |
| Auth/token validation failure | pkg/common/authServerInterceptor.go — NewUnaryAuthServerIntercept |
| Data not saving or loading correctly | pkg/storage/storage.go — SaveGuildProgress / GetGuildProgress |
| Service not starting at all | main.go — just before os.Exit(1) calls |
Conditional breakpoint syntax
Right-click a breakpoint → Edit Breakpoint → enter a Go expression, for example:
req.GuildId == "guild_001"
The debugger only pauses when the condition is true.
Reading logs
Pipe output through jq for readable logs during local runs:
# Pretty-print everything
go run main.go 2>&1 | jq '.'
# Show only ERROR lines
go run main.go 2>&1 | jq 'select(.level == "ERROR")'
# Follow logs and highlight errors (requires jq + grep)
go run main.go 2>&1 | jq -r '. | "\(.level) \(.msg)"'
Testing endpoints manually
curl
# Create or update guild progress
curl -s -X POST \
"http://localhost:8000/guild/v1/admin/namespace/mygame/progress" \
-H "Content-Type: application/json" \
-d '{"guildProgress": {"guildId": "guild_001", "namespace": "mygame"}}' | jq .
# Get guild progress
curl -s "http://localhost:8000/guild/v1/admin/namespace/mygame/progress/guild_001" | jq .
Add -H "Authorization: Bearer <your-token>" when PLUGIN_GRPC_SERVER_AUTH_ENABLED=true.
grpcurl (gRPC layer directly)
# List available services
grpcurl -plaintext localhost:6565 list
# Call a method directly
grpcurl -plaintext \
-d '{"namespace":"mygame","guildProgress":{"guildId":"guild_001"}}' \
localhost:6565 service.Service/CreateOrUpdateGuildProgress
Go-specific troubleshooting
Proto changes have no effect
Symptom: You edited pkg/proto/service.proto but nothing changed at runtime.
Cause: The generated stubs in pkg/pb/ have not been regenerated.
Fix: Run the "Proto: Generate" VS Code task, or:
./proto.sh
Then restart the service.
go run vs the debugger launch config
go run main.go compiles without debug information. When you need to attach a debugger, always
use the VS Code launch config (or dlv debug) rather than go run.
Checking for port conflicts
If you see "address already in use":
ss -tlnp | grep -E '6565|8000|8080'
Kill the stale process and start again.
AI assistance
The app template ships with a Claude agent skill at
.claude/skills/debugging-guide/SKILL.md.
Copy the full skill file into your own repository and activate it in your AI assistant.
For the full skill content and prompting tips, see the AI assistance section in the main guide.