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.