Go

TracesMetricsApp LogsCustom LogsProfiling

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_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

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(...).

Host (with Middleware Host Agent)
Serverless (no Host Agent)
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_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:

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
    1track.WithConfigTag(track.PauseMetrics|PauseTraces|PauseLogs|PauseProfiling, true)
    (You can also use the matching MW_APM_COLLECT_* env vars below.)
  • 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)

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.