-
-
Notifications
You must be signed in to change notification settings - Fork 233
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add bunzerolog options
- Loading branch information
Showing
5 changed files
with
420 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
# Patterns for files created by this project. | ||
# For other files, use global gitignore. | ||
*.s3db | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# bunzerolog | ||
|
||
bunzerolog is a logging package for Bun that uses zerolog. | ||
This package enables SQL queries executed by Bun to be logged and displayed using zerolog. | ||
|
||
## Installation | ||
|
||
```bash | ||
go get github.com/uptrace/bun/extra/bunzerolog | ||
``` | ||
|
||
## Features | ||
|
||
- Supports setting a `*zerolog.Logger` instance or uses the global logger if not set. | ||
- Supports setting a `*zerolog.Logger` instance using the context. | ||
- Logs general SQL queries with configurable log levels. | ||
- Logs slow SQL queries based on a configurable duration threshold. | ||
- Logs SQL queries that result in errors, for easier debugging. | ||
- Allows for custom log formatting. | ||
|
||
## Usage | ||
|
||
First, import the bunzerolog package: | ||
```go | ||
import "github.com/uptrace/bun/extra/bunzerolog" | ||
``` | ||
|
||
Then, create a new QueryHook and add the hook to `*bun.DB` instance: | ||
```go | ||
import "github.com/rs/zerolog" | ||
|
||
db := bun.NewDB(sqldb, dialect) | ||
|
||
hook := bunzerolog.NewQueryHook( | ||
bunzerolog.WithQueryLogLevel(zerolog.DebugLevel), | ||
bunzerolog.WithSlowQueryLogLevel(zerolog.WarnLevel), | ||
bunzerolog.WithErrorQueryLogLevel(zerolog.ErrorLevel), | ||
bunzerolog.WithSlowQueryThreshold(3 * time.Second), | ||
) | ||
|
||
db.AddQueryHook(hook) | ||
``` | ||
|
||
## Setting a Custom `*zerolog.Logger` Instance | ||
|
||
To set a `*zerolog.Logger` instance, you can use the WithLogger option: | ||
|
||
```go | ||
logger := zerolog.New(os.Stderr).With().Timestamp().Logger() | ||
hook := bunzerolog.NewQueryHook( | ||
bunzerolog.WithLogger(logger), | ||
// other options... | ||
) | ||
``` | ||
|
||
If a `*zerolog.Logger` instance is not set, the logger from the context will be used. | ||
|
||
## Custom Log Formatting | ||
|
||
To customize the log format, you can use the WithLogFormat option: | ||
|
||
```go | ||
customFormat := func(ctx context.Context, event *bun.QueryEvent, zerevent *zerolog.Event) *zerolog.Event { | ||
duration := h.now().Sub(event.StartTime) | ||
|
||
return zerevent. | ||
Err(event.Err). | ||
Str("request_id", requestid.FromContext(ctx)). | ||
Str("query", event.Query). | ||
Str("operation", event.Operation()). | ||
Str("duration", duration.String()) | ||
} | ||
|
||
hook := bunzerolog.NewQueryHook( | ||
bunzerolog.WithLogFormat(customFormat), | ||
// other options... | ||
) | ||
``` | ||
|
||
## Options | ||
|
||
- `WithLogger(logger *zerolog.Logger)`: Sets a `*zerolog.Logger` instance. If not set, the logger from context will be used. | ||
- `WithQueryLogLevel(level zerolog.Level)`: Sets the log level for general queries. | ||
- `WithSlowQueryLogLevel(level zerolog.Level)`: Sets the log level for slow queries. | ||
- `WithErrorQueryLogLevel(level zerolog.Level)`: Sets the log level for queries that result in errors. | ||
- `WithSlowQueryThreshold(threshold time.Duration)`: Sets the duration threshold for identifying slow queries. | ||
- `WithLogFormat(f logFormat)`: Sets the custom format for slog output. |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package bunzerolog | ||
|
||
// bunslog provides logging functionalities for Bun using slog. | ||
// This package allows SQL queries issued by Bun to be displayed using slog. | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"errors" | ||
"time" | ||
|
||
"github.com/rs/zerolog" | ||
"github.com/rs/zerolog/log" | ||
|
||
"github.com/uptrace/bun" | ||
) | ||
|
||
var _ bun.QueryHook = (*QueryHook)(nil) | ||
|
||
// Option is a function that configures a QueryHook. | ||
type Option func(*QueryHook) | ||
|
||
// WithLogger sets the *zerolog.Logger instance. | ||
func WithLogger(logger *zerolog.Logger) Option { | ||
return func(h *QueryHook) { | ||
h.logger = logger | ||
} | ||
} | ||
|
||
// WithQueryLogLevel sets the log level for general queries. | ||
func WithQueryLogLevel(level zerolog.Level) Option { | ||
return func(h *QueryHook) { | ||
h.queryLogLevel = level | ||
} | ||
} | ||
|
||
// WithSlowQueryLogLevel sets the log level for slow queries. | ||
func WithSlowQueryLogLevel(level zerolog.Level) Option { | ||
return func(h *QueryHook) { | ||
h.slowQueryLogLevel = level | ||
} | ||
} | ||
|
||
// WithErrorQueryLogLevel sets the log level for queries that result in an error. | ||
func WithErrorQueryLogLevel(level zerolog.Level) Option { | ||
return func(h *QueryHook) { | ||
h.errorLogLevel = level | ||
} | ||
} | ||
|
||
// WithSlowQueryThreshold sets the duration threshold for identifying slow queries. | ||
func WithSlowQueryThreshold(threshold time.Duration) Option { | ||
return func(h *QueryHook) { | ||
h.slowQueryThreshold = threshold | ||
} | ||
} | ||
|
||
// WithLogFormat sets the custom format for slog output. | ||
func WithLogFormat(f LogFormatFn) Option { | ||
return func(h *QueryHook) { | ||
h.logFormat = f | ||
} | ||
} | ||
|
||
type LogFormatFn func(ctx context.Context, event *bun.QueryEvent, zeroctx *zerolog.Event) *zerolog.Event | ||
|
||
// QueryHook is a hook for Bun that enables logging with slog. | ||
// It implements bun.QueryHook interface. | ||
type QueryHook struct { | ||
logger *zerolog.Logger | ||
queryLogLevel zerolog.Level | ||
slowQueryLogLevel zerolog.Level | ||
errorLogLevel zerolog.Level | ||
slowQueryThreshold time.Duration | ||
logFormat LogFormatFn | ||
now func() time.Time | ||
} | ||
|
||
// NewQueryHook initializes a new QueryHook with the given options. | ||
func NewQueryHook(opts ...Option) *QueryHook { | ||
h := &QueryHook{ | ||
queryLogLevel: zerolog.DebugLevel, | ||
slowQueryLogLevel: zerolog.WarnLevel, | ||
errorLogLevel: zerolog.ErrorLevel, | ||
now: time.Now, | ||
} | ||
|
||
for _, opt := range opts { | ||
opt(h) | ||
} | ||
|
||
// use default format | ||
if h.logFormat == nil { | ||
h.logFormat = func(ctx context.Context, event *bun.QueryEvent, zerevent *zerolog.Event) *zerolog.Event { | ||
duration := h.now().Sub(event.StartTime) | ||
|
||
return zerevent. | ||
Err(event.Err). | ||
Str("query", event.Query). | ||
Str("operation", event.Operation()). | ||
Str("duration", duration.String()) | ||
} | ||
} | ||
|
||
return h | ||
} | ||
|
||
// BeforeQuery is called before a query is executed. | ||
func (h *QueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) context.Context { | ||
return ctx | ||
} | ||
|
||
// AfterQuery is called after a query is executed. | ||
// It logs the query based on its duration and whether it resulted in an error. | ||
func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) { | ||
level := h.queryLogLevel | ||
duration := h.now().Sub(event.StartTime) | ||
if h.slowQueryThreshold > 0 && h.slowQueryThreshold <= duration { | ||
level = h.slowQueryLogLevel | ||
} | ||
|
||
if event.Err != nil && !errors.Is(event.Err, sql.ErrNoRows) { | ||
level = h.errorLogLevel | ||
} | ||
|
||
l := h.logger | ||
if l == nil { | ||
l = log.Ctx(ctx) | ||
} | ||
|
||
h.logFormat(ctx, event, l.WithLevel(level)).Send() | ||
} |
Oops, something went wrong.