Go
| Traces | Metrics | App Logs | Custom Logs | Profiling |
|---|---|---|---|---|
| ✅ | ✅ | ✖ | ✅ | ✅ |
1. Prerequisites
Go 1.17+ Check with:
go version- Middleware Host Agent on the same machine (host-based only). If your app is in containers/Kubernetes, set
MW_AGENT_SERVICEso your app can reach the Host Agent.- Docker:
MW_AGENT_SERVICEshould point to the Docker bridge gateway (default172.17.0.1). - Kubernetes:
MW_AGENT_SERVICE=mw-service.mw-agent-ns.svc.cluster.local.
- Docker:
2. Install the Go APM package
go get github.com/middleware-labs/golang-apm@latest
Add imports at your entry point (first import block):
import (
track "github.com/middleware-labs/golang-apm/tracker"
)The accessToken is your MW account key (see Installation page in the app).
3. Instrumentation
Pick Host (with the Host Agent) or Serverless (send directly to Middleware). In both cases, you initialise once at startup with track.Track(...).
// main.go (in func main)
config, err := track.Track(
track.WithConfigTag("service", "YOUR_SERVICE_NAME"),
track.WithConfigTag("accessToken", "<MW_API_KEY>"), // optional in Host mode
)
if err != nil { panic(err) }
defer func() { _ = config.Tp.Shutdown(context.Background()) }()- When the Host Agent is on the same host/namespace, you generally don’t need a target URL; the SDK exports to the agent.
- If your app is in a container and the agent is on the host or another Pod, set
MW_AGENT_SERVICEso the SDK reaches it (e.g.,172.17.0.1on Docker, service DNS on Kubernetes).
Send directly to your tenant by adding a target and your API key:
config, err := track.Track(
track.WithConfigTag("service", "YOUR_SERVICE_NAME"),
track.WithConfigTag("accessToken", "<MW_API_KEY>"),
track.WithConfigTag("target", "https://<MW_UID>.middleware.io:443"),
)
if err != nil { panic(err) }
defer func() { _ = config.Tp.Shutdown(context.Background()) }()4. Framework quick-starts
Use these only if you use the corresponding library.
net/http
go get github.com/middleware-labs/golang-apm-http
import (
"net/http"
track "github.com/middleware-labs/golang-apm/tracker"
mwhttp "github.com/middleware-labs/golang-apm-http/http"
)
func main() {
go track.Track(
track.WithConfigTag(track.Service, "your service"),
track.WithConfigTag(track.Token, "<MW_API_KEY>"),
)
http.Handle("/hello", mwhttp.MiddlewareHandler(http.HandlerFunc(hello), "hello"))
}Gin (v1.1.18+)
go get github.com/middleware-labs/golang-apm-gin@latest
import (
"github.com/gin-gonic/gin"
track "github.com/middleware-labs/golang-apm/tracker"
mwgin "github.com/middleware-labs/golang-apm-gin/gin"
)
func main() {
r := gin.Default()
config, _ := track.Track(
track.WithConfigTag("service","<apm-service>"),
track.WithConfigTag("accessToken","<MW_API_KEY>"),
)
r.Use(mwgin.Middleware(config))
}Gorilla Mux (v1.8.0)
go get github.com/middleware-labs/golang-apm-mux@latest
import (
"github.com/gorilla/mux"
track "github.com/middleware-labs/golang-apm/tracker"
mwmux "github.com/middleware-labs/golang-apm-mux/mux"
)
func main() {
r := mux.NewRouter()
config, _ := track.Track(
track.WithConfigTag("service","<apm-service>"),
track.WithConfigTag("accessToken","<MW_API_KEY>"),
)
r.Use(mwmux.Middleware(config))
}Chi
Legacy (chi v1.5.4+)
go get github.com/middleware-labs/agent-apm-go-chi-legacy@latest
import (
"github.com/go-chi/chi"
track "github.com/middleware-labs/golang-apm/tracker"
mwchi "github.com/middleware-labs/agent-apm-go-chi-legacy"
)
func main() {
r := chi.NewRouter()
config, _ := track.Track(
track.WithConfigTag("service","<apm-service>"),
track.WithConfigTag("accessToken","<MW_API_KEY>"),
)
r.Use(mwchi.Middleware("<my-server>", mwchi.WithChiRoutes(r)))
}Chi v5 (v5.0.8+)
go get github.com/middleware-labs/agent-apm-go-chi@latest
import (
"github.com/go-chi/chi/v5"
track "github.com/middleware-labs/golang-apm/tracker"
mwchi "github.com/middleware-labs/agent-apm-go-chi"
)Beego (v1) / Beego v2
go get github.com/middleware-labs/golang-apm-beego@latest
import (
track "github.com/middleware-labs/golang-apm/tracker"
mwbeego "github.com/middleware-labs/golang-apm-beego/beego"
)database/sql (mysql driver v1.7.1+)
go get github.com/middleware-labs/golang-apm-sql@latest
import (
track "github.com/middleware-labs/golang-apm/tracker"
mwsql "github.com/middleware-labs/golang-apm-sql/sql"
)
db, err := mwsql.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/dbname")go-pg/pg (v10.11.1+)
go get github.com/middleware-labs/golang-apm-pg@latest
GORM
- jinzhu/gorm (v1.9.16+): use golang-apm-sql as above.
- gorm.io/gorm (v1.25.1+):
go get github.com/middleware-labs/go-agent-gorm@latest import ( "gorm.io/driver/sqlite" "gorm.io/gorm" track "github.com/middleware-labs/golang-apm/tracker" mwgorm "github.com/middleware-labs/go-agent-gorm/gorm" )
MongoDB (mongo-driver v1.12.1+)
go get github.com/middleware-labs/golang-apm-mongo@latest
import (
"go.mongodb.org/mongo-driver/mongo/options"
track "github.com/middleware-labs/golang-apm/tracker"
mwmongo "github.com/middleware-labs/golang-apm-mongo/mongo"
)
opts := options.Client()
opts.Monitor = mwmongo.NewMonitor()gRPC (google.golang.org/grpc v1.53.0+)
go get github.com/middleware-labs/golang-apm-grpc@latest
import (
"google.golang.org/grpc"
mwgrpc "github.com/middleware-labs/golang-apm-grpc/grpc"
track "github.com/middleware-labs/golang-apm/tracker"
)
server := grpc.NewServer(
grpc.UnaryInterceptor(mwgrpc.UnaryServerInterceptor()),
grpc.StreamInterceptor(mwgrpc.StreamServerInterceptor()),
)5. Sending custom data
A. Custom logs (choose your logger)
All adapters below forward structured logs to Middleware with active trace/span context.
- log/slog:
import ( "log/slog" "github.com/middleware-labs/golang-apm/mwotelslog" track "github.com/middleware-labs/golang-apm/tracker" ) config, _ := track.Track( track.WithConfigTag(track.Service, "svc"), track.WithConfigTag(track.Token, "<MW_API_KEY>"), ) logger := mwotelslog.NewMWOTelLogger(config, mwotelslog.WithDefaultConsoleLog(), mwotelslog.WithName("otelslog"), ) slog.SetDefault(logger) - Zap:
import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" mwotelzap "github.com/middleware-labs/golang-apm/mwotelzap" track "github.com/middleware-labs/golang-apm/tracker" ) config, _ := track.Track( track.WithConfigTag(track.Service, "svc"), track.WithConfigTag(track.Token, "<MW_API_KEY>"), ) logger := zap.New(zapcore.NewTee(/* consoleCore, fileCore, */ mwotelzap.NewMWOTelCore(config, mwotelzap.WithName("otelzaplog")))) zap.ReplaceGlobals(logger) - zerolog
import (
"github.com/rs/zerolog/log"
mwotelzerolog "github.com/middleware-labs/golang-apm/mwotelzerolog"
track "github.com/middleware-labs/golang-apm/tracker"
)
config, _ := track.Track(
track.WithConfigTag(track.Service, "svc"),
track.WithConfigTag(track.Token, "<MW_API_KEY>"),
)
hook := mwotelzerolog.NewMWOTelHook(config)
logger := log.Hook(hook)- logrus
import (
log "github.com/sirupsen/logrus"
otellog "github.com/middleware-labs/golang-apm/mwotellogrus"
track "github.com/middleware-labs/golang-apm/tracker"
)
config, _ := track.Track(
track.WithConfigTag(track.Service, "svc"),
track.WithConfigTag(track.Token, "<MW_API_KEY>"),
)
logHook := otellog.NewMWOTelHook(config, otellog.WithLevels(log.AllLevels), otellog.WithName("otellogrus"))
log.AddHook(logHook)
log.SetFormatter(&log.JSONFormatter{})B. Record errors (stack traces)
err := errors.New("something went wrong")
track.ErrorRecording(ctx, err)C. Get current span
span := track.SpanFromContext(ctx)D. Custom spans, events & attributes (quick taste)
config, _ := track.Track(
track.WithConfigTag("service", "<APM-SERVICE-NAME>"),
track.WithConfigTag("accessToken", "<MW_API_KEY>"),
)
tp := config.Tp
ctx, span := tp.Tracer("example-tracer").Start(ctx, "example-span")
span.AddEvent("starting work")
track.SetAttribute(ctx, "user.id", "123")
defer span.End()6. Continuous profiling
Profiling starts automatically once the tracker is configured (Step 2). Use the Continuous Profiling section in the app to explore CPU, memory, goroutines, etc.
7. Telemetry & debug options
- Pause default runtime metrics only
track.WithConfigTag(track.PauseDefaultMetrics, true) - Pause metrics / traces / logs / profiling(You can also use the matching MW_APM_COLLECT_* env vars below.)
track.WithConfigTag(track.PauseMetrics|PauseTraces|PauseLogs|PauseProfiling, true) - Debug to console
track.WithConfigTag(track.Debug, true) - Debug to files
track.WithConfigTag(track.Debug, true) + track.WithConfigTag(track.DebugLogFile, true)
8. Environment variables (quick reference)
| Variable | Where it's used | What it does | Example |
|---|---|---|---|
MW_AGENT_SERVICE | Host-based | Address/DNS of Middleware Host Agent for SDK to reach the agent | 172.17.0.1 (Docker), mw-service.mw-agent-ns.svc.cluster.local (Kubernetes) |
MW_APM_COLLECT_METRICS | Both | Enable/disable metrics (overrides default) | false to pause metrics |
MW_APM_COLLECT_TRACES | Both | Enable/disable traces | false to pause traces |
MW_APM_COLLECT_LOGS | Both | Enable/disable logs | false to pause logs |
- In serverless, prefer setting
track.WithConfigTag("target", "https://<MW_UID>.middleware.io:443"). - App access token is typically passed via code:
track.WithConfigTag("accessToken", "<MW_API_KEY>").
9. View your data
After a short delay, you’ll see telemetry in the Middleware app:
- APM: service overview, traces, errors
- Logs: any logs forwarded via adapters above
- Continuous Profiling: CPU/mem/goroutine flamegraphs
See “Viewing Data” for where these live in the UI.10. Troubleshooting
- No data from containers/K8s → verify
MW_AGENT_SERVICE(Docker bridge gateway is often172.17.0.1; use the K8s service DNS shown above). - High local noise → enable Debug to see what’s emitted (
track.Debug, track.DebugLogFile). - Too much overhead → pause what you don’t need (
PauseMetrics/DefaultMetrics/Traces/Logs/Profilingor the env vars).
Need assistance or want to learn more about Middleware? Contact our support team at [email protected] or join our Slack channel.