Collecting Zap logs with OpenTelemetry

With OpenTelemetry Zap, you can automatically generate OpenTelemetry events from your logging statements. This can be useful for understanding how your application is behaving in production and diagnosing issues.

OpenTelemetry Zap automatically injects trace and span IDs for each logging statement, allowing you to correlate your logs with other telemetry data from your application.

What is OpenTelemetry?

OpenTelemetryopen in new window is an open source observability framework hosted by Cloud Native Computing Foundation. It is a merger of OpenCensus and OpenTracing projects.

OpenTelemetry aims to provide a single standard across all types of observability signals such as OpenTemetry logsopen in new window, distributed tracingopen in new window, and metricsopen in new window.

OpenTelemetry specifies how to collect and send telemetry data to backend platforms. With OpenTelemetry, you can instrument your application once and then add or change vendors without changing the instrumentation.

Zap instrumentation

To install otelzap instrumentation:

go get github.com/uptrace/opentelemetry-go-extra/otelzap

Usage

otelzap instrumentation records Zap log messages as events on the existing span from the passed context.Context. It does not record anything if the context does not contain a span.

You need to create an otelzap.Logger using this package and pass a contextopen in new window to propagate the active span.

import (
    "go.uber.org/zap"
    "github.com/uptrace/opentelemetry-go-extra/otelzap"
)

// Wrap zap logger to extend Zap with API that accepts a context.Context.
log := otelzap.New(zap.NewExample())

// And then pass ctx to propagate the span.
log.Ctx(ctx).Error("hello from zap",
	zap.Error(errors.New("hello world")),
	zap.String("foo", "bar"))

// Alternatively.
log.ErrorContext(ctx, "hello from zap",
	zap.Error(errors.New("hello world")),
	zap.String("foo", "bar"))

Both variants are fast and don't allocate. See exampleopen in new window for details.

Global logger

Just like Zap, otelzap provides a global logger that can be set with otelzap.ReplaceGlobals:

package main

import (
	"go.uber.org/zap"
	"github.com/uptrace/opentelemetry-go-extra/otelzap"
)

func main() {
	logger := otelzap.New(zap.NewExample())
	defer logger.Sync()

	undo := otelzap.ReplaceGlobals(logger)
	defer undo()

	otelzap.L().Info("replaced zap's global loggers")
	otelzap.Ctx(context.TODO()).Info("... and with context")
}

Sugared logger

You can also use sugared logger API in a similar way:

log := otelzap.New(zap.NewExample())
sugar := log.Sugar()

sugar.Ctx(ctx).Infow("failed to fetch URL",
	// Structured context as loosely typed key-value pairs.
	"url", url,
	"attempt", 3,
	"backoff", time.Second,
)
sugar.InfowContext(ctx, "failed to fetch URL",
	// Structured context as loosely typed key-value pairs.
	"url", url,
	"attempt", 3,
	"backoff", time.Second,
)

sugar.Ctx(ctx).Infof("Failed to fetch URL: %s", url)
sugar.InfofContext(ctx, "Failed to fetch URL: %s", url)

What is Uptrace?

Uptrace is a distributed tracing toolopen in new window that supports distributed tracing, metrics, and logs. You can use it to monitor applications and troubleshoot issues.

Uptrace Overview

Uptrace comes with an intuitive query builder, rich dashboards, alerting rules with notifications, and integrations for most languages and frameworks.

Uptrace can process billions of spans and metrics on a single server and allows you to monitor your applications at 10x lower cost.

In just a few minutes, you can try Uptrace by visiting the cloud demoopen in new window (no login required) or running it locally with Dockeropen in new window. The source code is available on GitHubopen in new window.

What's next?

Next, instrument more operations to get a more detailed picture. Try to prioritize network calls, disk operations, database queries, error and logs.

You can also create your own instrumentations using OpenTelemetry Go Tracing APIopen in new window.

Last Updated: