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.