# Set Up Logs in Go | Sentry for Zap

With Sentry Structured Logs, you can send text-based log information from your applications to Sentry. Once in Sentry, these logs can be viewed alongside relevant errors, searched by text-string, or searched using their individual attributes.

## [Requirements](https://docs.sentry.io/platforms/go/guides/zap/logs.md#requirements)

Logs in Go are supported in Sentry Go SDK version `0.33.0` and above. To use integrations with other logging libraries, check their specific documentation pages for detailed requirements.

## [Setup](https://docs.sentry.io/platforms/go/guides/zap/logs.md#setup)

To enable logging, you need to initialize the SDK with the `EnableLogs` option set to `true`.

```go
package main

import (
	"fmt"
	"time"

	"github.com/getsentry/sentry-go"
)

func main() {
	if err := sentry.Init(sentry.ClientOptions{
		Dsn:        "___PUBLIC_DSN___",
		// Enable logs to be sent to Sentry
		EnableLogs: true,
	}); err != nil {
		fmt.Printf("Sentry initialization failed: %v\n", err)
	}
	// Flush buffered events before the program terminates.
	defer sentry.Flush(2 * time.Second)
}
```

## [Usage](https://docs.sentry.io/platforms/go/guides/zap/logs.md#usage)

Once the feature is enabled on the SDK and the SDK is initialized, you can send logs by using the `sentry.Logger` API or our different integrations.

The `sentry.Logger` API exposes methods that support six different log levels:

* `trace`
* `debug`
* `info`
* `warn`
* `error`
* `fatal`

The methods support both `fmt.Print` and `fmt.Printf` like syntax. If you pass in format specifiers like `%v`, these will be sent to Sentry, and can be searched from within the Logs UI, and even added to the Logs views as a dedicated column.

```go
func main() {
	if err := sentry.Init(sentry.ClientOptions{
		Dsn:        "___PUBLIC_DSN___",
		EnableLogs: true,
	}); err != nil {
		log.Fatalf("Sentry initialization failed: %v", err)
	}
	// Flush buffered events before the program terminates.
	// Set the timeout to the maximum duration the program can afford to wait.
	defer sentry.Flush(2 * time.Second)

    // The SentryLogger requires context, to link logs with the appropriate traces. You can either create a new logger
	// by providing the context, or use WithCtx() to pass the context inline.
	ctx := context.Background()
	logger := sentry.NewLogger(ctx)

	// Or inline using WithCtx()
    newCtx := context.Background()
    // WithCtx() does not modify the original context attached on the logger.
	logger.Info().WithCtx(newCtx).Emit("context passed")

	// You can use the logger like [fmt.Print]
	logger.Info().Emit("Hello ", "world!")
	// Or like [fmt.Printf]
	logger.Info().Emitf("Hello %v!", "world")
}
```

You can also pass additional permanent attributes to the logger via `SetAttributes`, or attach certain attributes to the `LogEntry` itself. These attributes do not persist after Emitting the `LogEntry`. All attributes will be searchable in the Logs UI.

```go
logger.SetAttributes(
    attribute.Int("key.int", 42),
    attribute.Bool("key.boolean", true),
    attribute.Float64("key.float", 42.4),
    attribute.String("key.string", "string"),
)
logger.Warn().Emitf("I have params: %v and attributes", "example param")

// This entry would contain all attributes attached to the logger.
// However, it's also possible to overwrite them.
logger.Info().String("key.string", "newstring").Emit("overwriting key.string")
```

Currently, the `attribute` API supports only these value types: `int`, `string`, `bool`, and `float`.

## [Options](https://docs.sentry.io/platforms/go/guides/zap/logs.md#options)

#### [BeforeSendLog](https://docs.sentry.io/platforms/go/guides/zap/logs.md#beforesendlog)

To filter logs, or update them before they are sent to Sentry, you can use the `BeforeSendLog` client option.

```go
sentry.Init(sentry.ClientOptions{
    Dsn:        "___PUBLIC_DSN___",
    EnableLogs: true,
    BeforeSendLog: func(log *sentry.Log) *sentry.Log {
        // filter out all trace logs
        if log.Level == sentry.LogLevelTrace {
            return nil
        }

        // filter all logs below warning
        if log.Severity <= sentry.LogSeverityInfo {
            return nil
        }
        return log
    },
})
```

### [Debug](https://docs.sentry.io/platforms/go/guides/zap/logs.md#debug)

If the `Debug` init option is set to true, calls to the `sentry.Logger` will also print to the console with the appropriate log level.

## [Correlating Logs With Traces](https://docs.sentry.io/platforms/go/guides/zap/logs.md#correlating-logs-with-traces)

In order to properly attach the correct trace with each log entry, a `context.Context` is required. If you're using logs combined with tracing, you should pass the correct context to properly attach each trace with the appropriate logs.

## [Default Attributes](https://docs.sentry.io/platforms/go/guides/zap/logs.md#default-attributes)

The Go SDK automatically sets several default attributes on all log entries to provide context and improve debugging:

### [Core Attributes](https://docs.sentry.io/platforms/go/guides/zap/logs.md#core-attributes)

* `environment`: The environment set in the SDK if defined. This is sent from the SDK as `sentry.environment`.
* `release`: The release set in the SDK if defined. This is sent from the SDK as `sentry.release`.
* `sdk.name`: The name of the SDK that sent the log. This is sent from the SDK as `sentry.sdk.name`.
* `sdk.version`: The version of the SDK that sent the log. This is sent from the SDK as `sentry.sdk.version`.

### [Message Template Attributes](https://docs.sentry.io/platforms/go/guides/zap/logs.md#message-template-attributes)

If the log was parameterized, Sentry adds the message template and parameters as log attributes.

* `message.template`: The parameterized template string. This is sent from the SDK as `sentry.message.template`.
* `message.parameter.X`: The parameters to fill the template string. X can either be the number that represent the parameter's position in the template string (`sentry.message.parameter.0`, `sentry.message.parameter.1`, etc) or the parameter's name (`sentry.message.parameter.item_id`, `sentry.message.parameter.user_id`, etc). This is sent from the SDK as `sentry.message.parameter.X`.

For example, with the following log:

```go
ctx := context.Background()
logger := sentry.NewLogger(ctx)

logger.Info().Emitf("A %s log message", "formatted")
```

Sentry will add the following attributes:

* `message.template`: "A %s log message"
* `message.parameter.0`: "formatted"

### [Server Attributes](https://docs.sentry.io/platforms/go/guides/zap/logs.md#server-attributes)

* `server.address`: The address of the server that sent the log. Equivalent to `server_name` that gets attached to Sentry errors.

### [User Attributes](https://docs.sentry.io/platforms/go/guides/zap/logs.md#user-attributes)

If user information is available in the current scope, the following attributes are added to the log:

* `user.id`: The user ID.
* `user.name`: The username.
* `user.email`: The email address.

### [Integration Attributes](https://docs.sentry.io/platforms/go/guides/zap/logs.md#integration-attributes)

If a log is generated by an SDK integration, the SDK will set additional attributes to help you identify the source of the log.

* `origin`: The origin of the log. This is sent from the SDK as `sentry.origin`.

## [Other Logging Integrations](https://docs.sentry.io/platforms/go/guides/zap/logs.md#other-logging-integrations)

Available integrations:

* [Slog](https://docs.sentry.io/platforms/go/guides/slog.md)
* [Logrus](https://docs.sentry.io/platforms/go/guides/logrus.md)

### [`io.Writer` interface](https://docs.sentry.io/platforms/go/guides/zap/logs.md#iowriter-interface)

The `sentry.Logger` implements the `io.Writer` interface, so you can easily inject the logger into your existing setup. However, to correctly link your traces you would need to create a new logger everytime you want to pass a new context. Due to this limitation we recommend using the `sentry.Logger` or any of the other supported integrations.

```go
sentryLogger := sentry.NewLogger(ctx)
logger := log.New(sentryLogger, "", log.LstdFlags)
logger.Println("Implementing log.Logger")
```
