Monitoring Go Applications: 3 Tools for Logging and Tracing

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why is your Go application silent, and how to fix it?

\n

You wrote a perfect microservice in Go. It passes all tests, starts quickly, and consumes minimal memory. You deploy it to production — and an hour later, users complain about slowdowns. You log into the server, look at the empty terminal, and realize: the application is silent. No logs, no metrics, no understanding of what went wrong.

\n

Sound familiar? Welcome to the world of observability for Go applications. Without monitoring and logging, your code is a black box. Today, we'll break down how to turn it into a transparent system where every delay and every error is visible at a glance.

\n

I'll show you three working tools, real Go code examples, and give you a checklist that will save your production environment. Let's go!

\n\n

1. Structured Logging: How to Stop Writing to the Console and Start Living

\n

The naive fmt.Println("Error:", err) is a thing of the past. Modern Go logging requires order: JSON format, levels (debug, info, warn, error), and searchable fields. Meet the king — the zap library from Uber.

\n\n

Why zap?

\n
    \n
  • Speed: it is 10-100 times faster than the standard log under high load.
  • \n
  • Structure: you add keys and values instead of concatenating strings.
  • \n
  • Flexibility: you can write to a file, stdout, Elasticsearch — anywhere.
  • \n
\n\n

Installation and Basic Example

\n
go get -u go.uber.org/zap
\n\n

Simple logger code:

\n
package main\n\nimport (\n    "go.uber.org/zap"\n)\n\nfunc main() {\n    logger, _ := zap.NewProduction()\n    defer logger.Sync() // flush buffer before exit\n\n    logger.Info("Service started",\n        zap.String("env", "production"),\n        zap.Int("port", 8080),\n    )\n\n    // Example with an error\n    err := someFunction()\n    if err != nil {\n        logger.Error("Failed to perform operation",\n            zap.Error(err),\n            zap.String("module", "database"),\n        )\n    }\n}
\n\n

The output is clean JSON:

\n
{"level":"info","ts":1700000000.123,"msg":"Service started","env":"production","port":8080}
\n\n

Now any tool like Kibana or Grafana Loki can parse these logs by fields. Want to find all errors in the "database" module from the last hour? One click.

\n\n

Tip: Don't log secrets and PII

\n

Add a check: if a field contains a password or email — mask it. Use zap.String("user", "***") for sensitive data.

\n\n

2. Metrics and Monitoring: Turning Code into a Dashboard

\n

Logs are good, but they talk about the past. Metrics are the pulse of your application in real time. For monitoring Go applications, the de facto standard is Prometheus + its client library.

\n\n

What to measure?

\n
    \n
  • RPS (requests per second) — service load.
  • \n
  • Latency (p50, p95, p99) — response time.
  • \n
  • Error rate — percentage of errors.
  • \n
  • Goroutines — goroutine leaks.
  • \n
  • Memory / CPU — resources.
  • \n
\n\n

Example: Adding HTTP Server Metrics

\n
package main\n\nimport (\n    "net/http"\n    "github.com/prometheus/client_golang/prometheus"\n    "github.com/prometheus/client_golang/prometheus/promhttp"\n)\n\nvar (\n    requestCount = prometheus.NewCounterVec(\n        prometheus.CounterOpts{\n            Name: "http_requests_total",\n            Help: "Total number of HTTP requests",\n        },\n        []string{"method", "endpoint", "status"},\n    )\n    requestDuration = prometheus.NewHistogramVec(\n        prometheus.HistogramOpts{\n            Name:    "http_request                

Blogs

Book Recommendations