Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4262307
initial logger impl
giortzisg May 7, 2025
67128ea
add attributes
giortzisg May 8, 2025
3e25a81
remove tests
giortzisg May 8, 2025
3e17902
add BeforeSendLog option
giortzisg May 8, 2025
bd907d9
fix metadata attributes
giortzisg May 8, 2025
67975c0
improve test coverage
giortzisg May 8, 2025
2b8e407
add sentry origin
giortzisg May 8, 2025
7b181b7
change flush timeout
giortzisg May 9, 2025
74ce60d
fix trace & span propagation
giortzisg May 9, 2025
bcac19d
add ctx on log functions & handle trace propagation
giortzisg May 9, 2025
14827a0
lint issues
giortzisg May 9, 2025
6325542
Merge branch 'master' into feat/logger
giortzisg May 9, 2025
bfa595d
Merge branch 'master' into feat/logger
giortzisg May 9, 2025
95f8ed9
review changes
giortzisg May 12, 2025
d2c5b2a
change severity and level json
giortzisg May 12, 2025
98cf9fe
add Printf like methods & docs
giortzisg May 12, 2025
5b2aa71
setAttributes doc comment
giortzisg May 12, 2025
e0e251d
add examples and fix setAttributes
giortzisg May 12, 2025
bd47723
fix setAttributes
giortzisg May 12, 2025
2c8665c
fix warn level name
giortzisg May 12, 2025
710be2f
write to stdout if debug true
giortzisg May 12, 2025
b4360c9
Merge remote-tracking branch 'origin/master' into feat/logger
giortzisg May 12, 2025
10d9ae0
ignore coverage
giortzisg May 12, 2025
d40ff0a
change server addr & beforeSendLog
giortzisg May 12, 2025
120a48f
update doc comments
giortzisg May 13, 2025
9b7b141
add attributes tests
giortzisg May 13, 2025
74a43a0
add comment for check-in type
giortzisg May 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions _examples/http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ func NewImage(ctx context.Context, width, height int, seed []byte) image.Image {
// The only reason to change logger configuration in this example is aesthetics.
func configureLoggers() {
logFlags := log.Ldate | log.Ltime
sentry.Logger.SetPrefix("[sentry sdk] ")
sentry.Logger.SetFlags(logFlags)
sentry.DebugLogger.SetPrefix("[sentry sdk] ")
sentry.DebugLogger.SetFlags(logFlags)
log.SetPrefix("[http example] ")
log.SetFlags(logFlags)
}
94 changes: 94 additions & 0 deletions batch_logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package sentry

import (
"context"
"sync"
"time"
)

const (
batchSize = 100
batchTimeout = 5 * time.Second
)

type BatchLogger struct {
client *Client
logCh chan Log
cancel context.CancelFunc
wg sync.WaitGroup
startOnce sync.Once
}

func NewBatchLogger(client *Client) *BatchLogger {
return &BatchLogger{
client: client,
logCh: make(chan Log, batchSize),
}
}

func (l *BatchLogger) Start() {
l.startOnce.Do(func() {
ctx, cancel := context.WithCancel(context.Background())
l.cancel = cancel
l.wg.Add(1)
go l.run(ctx)
})
}

func (l *BatchLogger) Flush() {
if l.cancel != nil {
l.cancel()
l.wg.Wait()
}
}

func (l *BatchLogger) run(ctx context.Context) {
defer l.wg.Done()
var logs []Log
timer := time.NewTimer(batchTimeout)

for {
select {
case log := <-l.logCh:
logs = append(logs, log)
if len(logs) >= batchSize {
l.processEvent(logs)
logs = nil
if !timer.Stop() {
<-timer.C
}

Check warning on line 59 in batch_logger.go

View check run for this annotation

Codecov / codecov/patch

batch_logger.go#L58-L59

Added lines #L58 - L59 were not covered by tests
timer.Reset(batchTimeout)
}
case <-timer.C:
if len(logs) > 0 {
l.processEvent(logs)
logs = nil
}
timer.Reset(batchTimeout)

Check warning on line 67 in batch_logger.go

View check run for this annotation

Codecov / codecov/patch

batch_logger.go#L62-L67

Added lines #L62 - L67 were not covered by tests
case <-ctx.Done():
// Drain remaining logs from channel
drain:
for {
select {
case log := <-l.logCh:
logs = append(logs, log)
default:
break drain
}
}

if len(logs) > 0 {
l.processEvent(logs)
}
return
}
}
}

func (l *BatchLogger) processEvent(logs []Log) {
event := NewEvent()
event.Timestamp = time.Now()
event.Type = logType
event.Logs = logs
l.client.CaptureEvent(event, nil, nil)
}
36 changes: 24 additions & 12 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@
error
}

// Logger is an instance of log.Logger that is use to provide debug information about running Sentry Client
// can be enabled by either using Logger.SetOutput directly or with Debug client option.
var Logger = log.New(io.Discard, "[Sentry] ", log.LstdFlags)
// DebugLogger is an instance of log.Logger that is used to provide debug information about running Sentry Client
// can be enabled by either using DebugLogger.SetOutput directly or with Debug client option.
var DebugLogger = log.New(io.Discard, "[Sentry] ", log.LstdFlags)

// EventProcessor is a function that processes an event.
// Event processors are used to change an event before it is sent to Sentry.
Expand Down Expand Up @@ -223,6 +223,9 @@
MaxErrorDepth int
// Default event tags. These are overridden by tags set on a scope.
Tags map[string]string
// EnableLogs is a boolean flag to control if log envelopes will be generated and
// sent to Sentry via the logging API.
EnableLogs bool
}

// Client is the underlying processor that is used by the main API and Hub
Expand All @@ -237,7 +240,8 @@
sdkVersion string
// Transport is read-only. Replacing the transport of an existing client is
// not supported, create a new client instead.
Transport Transport
Transport Transport
batchLogger *BatchLogger
}

// NewClient creates and returns an instance of Client configured using
Expand Down Expand Up @@ -275,7 +279,7 @@
if debugWriter == nil {
debugWriter = os.Stderr
}
Logger.SetOutput(debugWriter)
DebugLogger.SetOutput(debugWriter)

Check warning on line 282 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L282

Added line #L282 was not covered by tests
}

if options.Dsn == "" {
Expand Down Expand Up @@ -337,6 +341,11 @@
sdkVersion: SDKVersion,
}

if options.EnableLogs {
client.batchLogger = NewBatchLogger(&client)
client.batchLogger.Start()
}

client.setupTransport()
client.setupIntegrations()

Expand Down Expand Up @@ -383,12 +392,12 @@

for _, integration := range integrations {
if client.integrationAlreadyInstalled(integration.Name()) {
Logger.Printf("Integration %s is already installed\n", integration.Name())
DebugLogger.Printf("Integration %s is already installed\n", integration.Name())

Check warning on line 395 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L395

Added line #L395 was not covered by tests
continue
}
client.integrations = append(client.integrations, integration)
integration.SetupOnce(client)
Logger.Printf("Integration installed: %s\n", integration.Name())
DebugLogger.Printf("Integration installed: %s\n", integration.Name())
}

sort.Slice(client.integrations, func(i, j int) bool {
Expand Down Expand Up @@ -507,6 +516,9 @@
// the network synchronously, configure it to use the HTTPSyncTransport in the
// call to Init.
func (client *Client) Flush(timeout time.Duration) bool {
if client.batchLogger != nil {
client.batchLogger.Flush()
}
return client.Transport.Flush(timeout)
}

Expand Down Expand Up @@ -605,7 +617,7 @@
// options.TracesSampler when they are started. Other events
// (errors, messages) are sampled here. Does not apply to check-ins.
if event.Type != transactionType && event.Type != checkInType && !sample(client.options.SampleRate) {
Logger.Println("Event dropped due to SampleRate hit.")
DebugLogger.Println("Event dropped due to SampleRate hit.")
return nil
}

Expand All @@ -620,13 +632,13 @@
if event.Type == transactionType && client.options.BeforeSendTransaction != nil {
// Transaction events
if event = client.options.BeforeSendTransaction(event, hint); event == nil {
Logger.Println("Transaction dropped due to BeforeSendTransaction callback.")
DebugLogger.Println("Transaction dropped due to BeforeSendTransaction callback.")
return nil
}
} else if event.Type != transactionType && event.Type != checkInType && client.options.BeforeSend != nil {
// All other events
if event = client.options.BeforeSend(event, hint); event == nil {
Logger.Println("Event dropped due to BeforeSend callback.")
DebugLogger.Println("Event dropped due to BeforeSend callback.")
return nil
}
}
Expand Down Expand Up @@ -692,7 +704,7 @@
id := event.EventID
event = processor(event, hint)
if event == nil {
Logger.Printf("Event dropped by one of the Client EventProcessors: %s\n", id)
DebugLogger.Printf("Event dropped by one of the Client EventProcessors: %s\n", id)
return nil
}
}
Expand All @@ -701,7 +713,7 @@
id := event.EventID
event = processor(event, hint)
if event == nil {
Logger.Printf("Event dropped by one of the Global EventProcessors: %s\n", id)
DebugLogger.Printf("Event dropped by one of the Global EventProcessors: %s\n", id)

Check warning on line 716 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L716

Added line #L716 was not covered by tests
return nil
}
}
Expand Down
2 changes: 1 addition & 1 deletion fasthttp/sentryfasthttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
func convert(ctx *fasthttp.RequestCtx) *http.Request {
defer func() {
if err := recover(); err != nil {
sentry.Logger.Printf("%v", err)
sentry.DebugLogger.Printf("%v", err)

Check warning on line 146 in fasthttp/sentryfasthttp.go

View check run for this annotation

Codecov / codecov/patch

fasthttp/sentryfasthttp.go#L146

Added line #L146 was not covered by tests
}
}()

Expand Down
2 changes: 1 addition & 1 deletion fiber/sentryfiber.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
func convert(ctx *fiber.Ctx) *http.Request {
defer func() {
if err := recover(); err != nil {
sentry.Logger.Printf("%v", err)
sentry.DebugLogger.Printf("%v", err)

Check warning on line 146 in fiber/sentryfiber.go

View check run for this annotation

Codecov / codecov/patch

fiber/sentryfiber.go#L146

Added line #L146 was not covered by tests
}
}()

Expand Down
2 changes: 1 addition & 1 deletion hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func (hub *Hub) AddBreadcrumb(breadcrumb *Breadcrumb, hint *BreadcrumbHint) {
hint = &BreadcrumbHint{}
}
if breadcrumb = client.options.BeforeBreadcrumb(breadcrumb, hint); breadcrumb == nil {
Logger.Println("breadcrumb dropped due to BeforeBreadcrumb callback.")
DebugLogger.Println("breadcrumb dropped due to BeforeBreadcrumb callback.")
return
}
}
Expand Down
6 changes: 3 additions & 3 deletions integrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
mi.once.Do(func() {
info, ok := debug.ReadBuildInfo()
if !ok {
Logger.Print("The Modules integration is not available in binaries built without module support.")
DebugLogger.Print("The Modules integration is not available in binaries built without module support.")

Check warning on line 35 in integrations.go

View check run for this annotation

Codecov / codecov/patch

integrations.go#L35

Added line #L35 was not covered by tests
return
}
mi.modules = extractModules(info)
Expand Down Expand Up @@ -141,7 +141,7 @@
for _, suspect := range suspects {
for _, pattern := range iei.ignoreErrors {
if pattern.Match([]byte(suspect)) || strings.Contains(suspect, pattern.String()) {
Logger.Printf("Event dropped due to being matched by `IgnoreErrors` option."+
DebugLogger.Printf("Event dropped due to being matched by `IgnoreErrors` option."+
"| Value matched: %s | Filter used: %s", suspect, pattern)
return nil
}
Expand Down Expand Up @@ -203,7 +203,7 @@

for _, pattern := range iei.ignoreTransactions {
if pattern.Match([]byte(suspect)) || strings.Contains(suspect, pattern.String()) {
Logger.Printf("Transaction dropped due to being matched by `IgnoreTransactions` option."+
DebugLogger.Printf("Transaction dropped due to being matched by `IgnoreTransactions` option."+
"| Value matched: %s | Filter used: %s", suspect, pattern)
return nil
}
Expand Down
42 changes: 38 additions & 4 deletions interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ import (
"time"
)

// eventType is the type of an error event.
// eventType is the type of error event.
const eventType = "event"

// transactionType is the type of a transaction event.
// transactionType is the type of transaction event.
const transactionType = "transaction"

// checkInType is the type of a check in event.
// logType is the type of log event.
const logType = "log"

// checkInType is the type of check in event.
const checkInType = "check_in"

// Level marks the severity of the event.
Expand All @@ -34,7 +37,7 @@ const (
LevelFatal Level = "fatal"
)

// SdkInfo contains all metadata about about the SDK being used.
// SdkInfo contains all metadata about the SDK being used.
type SdkInfo struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
Expand Down Expand Up @@ -96,6 +99,18 @@ func (b *Breadcrumb) MarshalJSON() ([]byte, error) {
return json.Marshal((*breadcrumb)(b))
}

// Logger allows sending structured logs to sentry.
type Logger interface {
Write(p []byte) (n int, err error)
Trace(ctx context.Context, v ...interface{})
Debug(ctx context.Context, v ...interface{})
Info(ctx context.Context, v ...interface{})
Warn(ctx context.Context, v ...interface{})
Error(ctx context.Context, v ...interface{})
Fatal(ctx context.Context, v ...interface{})
Panic(ctx context.Context, v ...interface{})
}

// Attachment allows associating files with your events to aid in investigation.
// An event may contain one or more attachments.
type Attachment struct {
Expand Down Expand Up @@ -325,6 +340,9 @@ type Event struct {
CheckIn *CheckIn `json:"check_in,omitempty"`
MonitorConfig *MonitorConfig `json:"monitor_config,omitempty"`

// The fields below are only relevant for logs
Logs []Log `json:"items,omitempty"`

// The fields below are not part of the final JSON payload.

sdkMetaData SDKMetaData
Expand Down Expand Up @@ -547,3 +565,19 @@ type EventHint struct {
Request *http.Request
Response *http.Response
}

// Log contains information about a logging event sent to Sentry.
type Log struct {
Timestamp time.Time `json:"timestamp,omitempty"`
TraceID TraceID `json:"trace_id,omitempty"`
Level Level `json:"level,omitempty"`
Severity int `json:"severity_number"`
Body string `json:"body,omitempty"`
Attributes map[string]Attribute `json:"attributes,omitempty"`
}

// Attribute is a log attribute.
type Attribute struct {
Value any `json:"value"`
Type string `json:"type"`
}
Loading
Loading