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

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.