---
title: "Go"
description: "Learn how to configure Middleware APM for Go applications to track performance, monitor requests, and debug errors effectively."
section: "APM"
breadcrumb: "APM > Go"
canonical: "https://docs.middleware.io/get-started/apm/go"
---
# Go

Use the Middleware Go APM SDK to collect traces, metrics and application logs from Go services, including popular frameworks and database clients. The table shows which data types it collects.

| Traces | Metrics | App Logs | Custom Logs | Profiling |
| --- | --- | --- | --- | --- |
| ✅ | ✅ | ✖ | ✅ | ✅ |

## 1. Prerequisites

Go 1.17+
Check with:

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

#### Host (with Middleware Host Agent)

```go
    // main.go (in func main)
config, err := track.Track(
  track.WithConfigTag("service", "YOUR_SERVICE_NAME"),
  track.WithConfigTag("accessToken", "your-initial-token"), // 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).

#### Serverless (no Host Agent)

Send directly to your tenant by adding a `target` and your API key:

```go
    config, err := track.Track(
  track.WithConfigTag("service", "YOUR_SERVICE_NAME"),
  track.WithConfigTag("accessToken", "your-initial-token"),
  track.WithConfigTag("target", "your-initial-uid"),
)
if err != nil { panic(err) }
defer func() { _ = config.Tp.Shutdown(context.Background()) }()
```

## 4. Framework quick-starts

> **Note:** Use these only if you use the corresponding library.

#### net/http

```go
    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,   "your-initial-token"),
  )
  http.Handle("/hello", mwhttp.MiddlewareHandler(http.HandlerFunc(hello), "hello"))
}
```

#### Gin (v1.1.18+)

```go
    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","your-initial-token"),
  )
  r.Use(mwgin.Middleware(config))
}
```

#### Gorilla Mux (v1.8.0)

```go
    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","your-initial-token"),
  )
  r.Use(mwmux.Middleware(config))
}
```

#### Chi

**Legacy (chi v1.5.4+)**

```go
    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","your-initial-token"),
  )
  r.Use(mwchi.Middleware("<my-server>", mwchi.WithChiRoutes(r)))
}
```

**Chi v5 (v5.0.8+)**

```go
    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
    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
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
      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
    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
    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:**

  ```go
      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,   "your-initial-token"),
      )

      logger := mwotelslog.NewMWOTelLogger(config,
      mwotelslog.WithDefaultConsoleLog(),
      mwotelslog.WithName("otelslog"),
      )
      slog.SetDefault(logger)
  ```
- **Zap:**

  ```go
      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,   "your-initial-token"),
      )

      logger := zap.New(zapcore.NewTee(/* consoleCore, fileCore, */ mwotelzap.NewMWOTelCore(config, mwotelzap.WithName("otelzaplog"))))
      zap.ReplaceGlobals(logger)
  ```
- **zerolog**

```go
    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,   "your-initial-token"),
)
hook := mwotelzerolog.NewMWOTelHook(config)
logger := log.Hook(hook)
```

- **logrus**

```go
    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,   "your-initial-token"),
)
logHook := otellog.NewMWOTelHook(config, otellog.WithLevels(log.AllLevels), otellog.WithName("otellogrus"))
log.AddHook(logHook)
log.SetFormatter(&log.JSONFormatter{})
```

### B. Record errors (stack traces)

```go
    err := errors.New("something went wrong")
track.ErrorRecording(ctx, err)
```

### C. Get current span

```go
    span := track.SpanFromContext(ctx)
```

### D. Custom spans, events & attributes (quick taste)

```go
    config, _ := track.Track(
  track.WithConfigTag("service", "<APM-SERVICE-NAME>"),
  track.WithConfigTag("accessToken", "your-initial-token"),
)
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

  ```go
      track.WithConfigTag(track.PauseDefaultMetrics, true)
  ```
- Pause metrics / traces / logs / profiling

  ```go
      track.WithConfigTag(track.PauseMetrics|PauseTraces|PauseLogs|PauseProfiling, true)
  ```

  (You can also use the matching MW_APM_COLLECT_* env vars below.)
- Debug to console

  ```go
      track.WithConfigTag(track.Debug, true)
  ```
- Debug to files

  ```go
      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 |

> **Note:** - 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? Get in touch with us via our Contact Us page or join our Slack channel.
