Go
| Traces | Metrics | App Logs | Custom Logs | Profiling |
|---|---|---|---|---|
| ✅ | ✅ | ✖ | ✅ | ✅ |
1. Prerequisites
Go 1.17+ Check with:
1go 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
1go get github.com/middleware-labs/golang-apm@latest
2
3Add imports at your entry point (first import block):
4import (
5 track "github.com/middleware-labs/golang-apm/tracker"
6)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(...).
1// main.go (in func main)
2config, err := track.Track(
3 track.WithConfigTag("service", "YOUR_SERVICE_NAME"),
4 track.WithConfigTag("accessToken", "<MW_API_KEY>"), // optional in Host mode
5)
6if err != nil { panic(err) }
7defer 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:
1config, err := track.Track(
2 track.WithConfigTag("service", "YOUR_SERVICE_NAME"),
3 track.WithConfigTag("accessToken", "<MW_API_KEY>"),
4 track.WithConfigTag("target", "https://<MW_UID>.middleware.io:443"),
5)
6if err != nil { panic(err) }
7defer func() { _ = config.Tp.Shutdown(context.Background()) }()4. Framework quick-starts
Use these only if you use the corresponding library.
net/http
1go get github.com/middleware-labs/golang-apm-http
2
3import (
4 "net/http"
5 track "github.com/middleware-labs/golang-apm/tracker"
6 mwhttp "github.com/middleware-labs/golang-apm-http/http"
7)
8
9func main() {
10 go track.Track(
11 track.WithConfigTag(track.Service, "your service"),
12 track.WithConfigTag(track.Token, "<MW_API_KEY>"),
13 )
14 http.Handle("/hello", mwhttp.MiddlewareHandler(http.HandlerFunc(hello), "hello"))
15}Gin (v1.1.18+)
1go get github.com/middleware-labs/golang-apm-gin@latest
2
3import (
4 "github.com/gin-gonic/gin"
5 track "github.com/middleware-labs/golang-apm/tracker"
6 mwgin "github.com/middleware-labs/golang-apm-gin/gin"
7)
8
9func main() {
10 r := gin.Default()
11 config, _ := track.Track(
12 track.WithConfigTag("service","<apm-service>"),
13 track.WithConfigTag("accessToken","<MW_API_KEY>"),
14 )
15 r.Use(mwgin.Middleware(config))
16}Gorilla Mux (v1.8.0)
1go get github.com/middleware-labs/golang-apm-mux@latest
2
3import (
4 "github.com/gorilla/mux"
5 track "github.com/middleware-labs/golang-apm/tracker"
6 mwmux "github.com/middleware-labs/golang-apm-mux/mux"
7)
8
9func main() {
10 r := mux.NewRouter()
11 config, _ := track.Track(
12 track.WithConfigTag("service","<apm-service>"),
13 track.WithConfigTag("accessToken","<MW_API_KEY>"),
14 )
15 r.Use(mwmux.Middleware(config))
16}Chi
Legacy (chi v1.5.4+)
1go get github.com/middleware-labs/agent-apm-go-chi-legacy@latest
2
3import (
4 "github.com/go-chi/chi"
5 track "github.com/middleware-labs/golang-apm/tracker"
6 mwchi "github.com/middleware-labs/agent-apm-go-chi-legacy"
7)
8
9func main() {
10 r := chi.NewRouter()
11 config, _ := track.Track(
12 track.WithConfigTag("service","<apm-service>"),
13 track.WithConfigTag("accessToken","<MW_API_KEY>"),
14 )
15 r.Use(mwchi.Middleware("<my-server>", mwchi.WithChiRoutes(r)))
16}Chi v5 (v5.0.8+)
1go get github.com/middleware-labs/agent-apm-go-chi@latest
2
3import (
4 "github.com/go-chi/chi/v5"
5 track "github.com/middleware-labs/golang-apm/tracker"
6 mwchi "github.com/middleware-labs/agent-apm-go-chi"
7)Beego (v1) / Beego v2
1go get github.com/middleware-labs/golang-apm-beego@latest
2
3import (
4 track "github.com/middleware-labs/golang-apm/tracker"
5 mwbeego "github.com/middleware-labs/golang-apm-beego/beego"
6)database/sql (mysql driver v1.7.1+)
1go get github.com/middleware-labs/golang-apm-sql@latest
2
3import (
4 track "github.com/middleware-labs/golang-apm/tracker"
5 mwsql "github.com/middleware-labs/golang-apm-sql/sql"
6)
7db, 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+):
1go get github.com/middleware-labs/go-agent-gorm@latest 2 3 import ( 4 "gorm.io/driver/sqlite" 5 "gorm.io/gorm" 6 track "github.com/middleware-labs/golang-apm/tracker" 7 mwgorm "github.com/middleware-labs/go-agent-gorm/gorm" 8 )
MongoDB (mongo-driver v1.12.1+)
1go get github.com/middleware-labs/golang-apm-mongo@latest
2
3import (
4 "go.mongodb.org/mongo-driver/mongo/options"
5 track "github.com/middleware-labs/golang-apm/tracker"
6 mwmongo "github.com/middleware-labs/golang-apm-mongo/mongo"
7)
8opts := options.Client()
9opts.Monitor = mwmongo.NewMonitor()gRPC (google.golang.org/grpc v1.53.0+)
1go get github.com/middleware-labs/golang-apm-grpc@latest
2
3import (
4 "google.golang.org/grpc"
5 mwgrpc "github.com/middleware-labs/golang-apm-grpc/grpc"
6 track "github.com/middleware-labs/golang-apm/tracker"
7)
8
9server := grpc.NewServer(
10 grpc.UnaryInterceptor(mwgrpc.UnaryServerInterceptor()),
11 grpc.StreamInterceptor(mwgrpc.StreamServerInterceptor()),
12)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:
1import ( 2 "log/slog" 3 "github.com/middleware-labs/golang-apm/mwotelslog" 4 track "github.com/middleware-labs/golang-apm/tracker" 5 ) 6 7 config, _ := track.Track( 8 track.WithConfigTag(track.Service, "svc"), 9 track.WithConfigTag(track.Token, "<MW_API_KEY>"), 10 ) 11 12 logger := mwotelslog.NewMWOTelLogger(config, 13 mwotelslog.WithDefaultConsoleLog(), 14 mwotelslog.WithName("otelslog"), 15 ) 16 slog.SetDefault(logger) - Zap:
1import ( 2 "go.uber.org/zap" 3 "go.uber.org/zap/zapcore" 4 mwotelzap "github.com/middleware-labs/golang-apm/mwotelzap" 5 track "github.com/middleware-labs/golang-apm/tracker" 6 ) 7 8 config, _ := track.Track( 9 track.WithConfigTag(track.Service, "svc"), 10 track.WithConfigTag(track.Token, "<MW_API_KEY>"), 11 ) 12 13 logger := zap.New(zapcore.NewTee(/* consoleCore, fileCore, */ mwotelzap.NewMWOTelCore(config, mwotelzap.WithName("otelzaplog")))) 14 zap.ReplaceGlobals(logger) - zerolog
1import (
2 "github.com/rs/zerolog/log"
3 mwotelzerolog "github.com/middleware-labs/golang-apm/mwotelzerolog"
4 track "github.com/middleware-labs/golang-apm/tracker"
5)
6
7config, _ := track.Track(
8 track.WithConfigTag(track.Service, "svc"),
9 track.WithConfigTag(track.Token, "<MW_API_KEY>"),
10)
11hook := mwotelzerolog.NewMWOTelHook(config)
12logger := log.Hook(hook)- logrus
1import (
2 log "github.com/sirupsen/logrus"
3 otellog "github.com/middleware-labs/golang-apm/mwotellogrus"
4 track "github.com/middleware-labs/golang-apm/tracker"
5)
6
7config, _ := track.Track(
8 track.WithConfigTag(track.Service, "svc"),
9 track.WithConfigTag(track.Token, "<MW_API_KEY>"),
10)
11logHook := otellog.NewMWOTelHook(config, otellog.WithLevels(log.AllLevels), otellog.WithName("otellogrus"))
12log.AddHook(logHook)
13log.SetFormatter(&log.JSONFormatter{})B. Record errors (stack traces)
1err := errors.New("something went wrong")
2track.ErrorRecording(ctx, err)C. Get current span
1span := track.SpanFromContext(ctx)D. Custom spans, events & attributes (quick taste)
1config, _ := track.Track(
2 track.WithConfigTag("service", "<APM-SERVICE-NAME>"),
3 track.WithConfigTag("accessToken", "<MW_API_KEY>"),
4)
5tp := config.Tp
6
7ctx, span := tp.Tracer("example-tracer").Start(ctx, "example-span")
8span.AddEvent("starting work")
9track.SetAttribute(ctx, "user.id", "123")
10defer 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
1track.WithConfigTag(track.PauseDefaultMetrics, true) - Pause metrics / traces / logs / profiling(You can also use the matching MW_APM_COLLECT_* env vars below.)
1track.WithConfigTag(track.PauseMetrics|PauseTraces|PauseLogs|PauseProfiling, true) - Debug to console
1track.WithConfigTag(track.Debug, true) - Debug to files
1track.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.