Go

TracesMetricsApp LogsCustom LogsProfiling

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_SERVICE so your app can reach the Host Agent.
    • Docker: MW_AGENT_SERVICE should point to the Docker bridge gateway (default 172.17.0.1).
    • Kubernetes: MW_AGENT_SERVICE=mw-service.mw-agent-ns.svc.cluster.local.

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_SERVICE so the SDK reaches it (e.g., 172.17.0.1 on 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
    track.WithConfigTag(track.PauseMetrics|PauseTraces|PauseLogs|PauseProfiling, true)
    (You can also use the matching MW_APM_COLLECT_* env vars below.)
  • 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)#

VariableWhere it's usedWhat it doesExample
MW_AGENT_SERVICEHost-basedAddress/DNS of Middleware Host Agent for SDK to reach the agent172.17.0.1 (Docker), mw-service.mw-agent-ns.svc.cluster.local (Kubernetes)
MW_APM_COLLECT_METRICSBothEnable/disable metrics (overrides default)false to pause metrics
MW_APM_COLLECT_TRACESBothEnable/disable tracesfalse to pause traces
MW_APM_COLLECT_LOGSBothEnable/disable logsfalse 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 often 172.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/Profiling or the env vars).

Need assistance or want to learn more about Middleware? Contact our support team at [email protected] or join our Slack channel.