Files
aleesa-webapp-go/vendor/github.com/getsentry/sentry-go/log.go
2026-02-10 20:54:39 +03:00

317 lines
7.1 KiB
Go

package sentry
import (
"context"
"fmt"
"maps"
"os"
"strings"
"sync"
"time"
"github.com/getsentry/sentry-go/attribute"
"github.com/getsentry/sentry-go/internal/debuglog"
)
type LogLevel string
const (
LogLevelTrace LogLevel = "trace"
LogLevelDebug LogLevel = "debug"
LogLevelInfo LogLevel = "info"
LogLevelWarn LogLevel = "warn"
LogLevelError LogLevel = "error"
LogLevelFatal LogLevel = "fatal"
)
const (
LogSeverityTrace int = 1
LogSeverityDebug int = 5
LogSeverityInfo int = 9
LogSeverityWarning int = 13
LogSeverityError int = 17
LogSeverityFatal int = 21
)
var mapTypesToStr = map[attribute.Type]AttrType{
attribute.INVALID: AttributeInvalid,
attribute.BOOL: AttributeBool,
attribute.INT64: AttributeInt,
attribute.FLOAT64: AttributeFloat,
attribute.STRING: AttributeString,
}
type sentryLogger struct {
ctx context.Context
client *Client
attributes map[string]Attribute
defaultAttributes map[string]Attribute
mu sync.RWMutex
}
type logEntry struct {
logger *sentryLogger
ctx context.Context
level LogLevel
severity int
attributes map[string]Attribute
shouldPanic bool
}
// NewLogger returns a Logger that emits logs to Sentry. If logging is turned off, all logs get discarded.
func NewLogger(ctx context.Context) Logger { // nolint: dupl
var hub *Hub
hub = GetHubFromContext(ctx)
if hub == nil {
hub = CurrentHub()
}
client := hub.Client()
if client != nil && client.options.EnableLogs {
// Build default attrs
serverAddr := client.options.ServerName
if serverAddr == "" {
serverAddr, _ = os.Hostname()
}
defaults := map[string]string{
"sentry.release": client.options.Release,
"sentry.environment": client.options.Environment,
"sentry.server.address": serverAddr,
"sentry.sdk.name": client.sdkIdentifier,
"sentry.sdk.version": client.sdkVersion,
}
defaultAttrs := make(map[string]Attribute)
for k, v := range defaults {
if v != "" {
defaultAttrs[k] = Attribute{Value: v, Type: AttributeString}
}
}
return &sentryLogger{
ctx: ctx,
client: client,
attributes: make(map[string]Attribute),
defaultAttributes: defaultAttrs,
mu: sync.RWMutex{},
}
}
debuglog.Println("fallback to noopLogger: enableLogs disabled")
return &noopLogger{}
}
func (l *sentryLogger) Write(p []byte) (int, error) {
msg := strings.TrimRight(string(p), "\n")
l.Info().Emit(msg)
return len(p), nil
}
func (l *sentryLogger) log(ctx context.Context, level LogLevel, severity int, message string, entryAttrs map[string]Attribute, args ...interface{}) {
if message == "" {
return
}
scope, traceID, spanID := resolveScopeAndTrace(ctx, l.ctx)
// Pre-allocate with capacity hint to avoid map growth reallocations
estimatedCap := len(l.defaultAttributes) + len(entryAttrs) + len(args) + 8 // scope ~3 + instance ~5
attrs := make(map[string]Attribute, estimatedCap)
// attribute precedence: default -> scope -> instance (from SetAttrs) -> entry-specific
for k, v := range l.defaultAttributes {
attrs[k] = v
}
scope.populateAttrs(attrs)
l.mu.RLock()
for k, v := range l.attributes {
attrs[k] = v
}
l.mu.RUnlock()
for k, v := range entryAttrs {
attrs[k] = v
}
if len(args) > 0 {
attrs["sentry.message.template"] = Attribute{
Value: message, Type: AttributeString,
}
for i, p := range args {
attrs[fmt.Sprintf("sentry.message.parameters.%d", i)] = Attribute{
Value: fmt.Sprintf("%+v", p), Type: AttributeString,
}
}
}
log := &Log{
Timestamp: time.Now(),
TraceID: traceID,
SpanID: spanID,
Level: level,
Severity: severity,
Body: fmt.Sprintf(message, args...),
Attributes: attrs,
}
l.client.captureLog(log, scope)
if l.client.options.Debug {
debuglog.Printf(message, args...)
}
}
func (l *sentryLogger) SetAttributes(attrs ...attribute.Builder) {
l.mu.Lock()
defer l.mu.Unlock()
for _, v := range attrs {
t, ok := mapTypesToStr[v.Value.Type()]
if !ok || t == "" {
debuglog.Printf("invalid attribute type set: %v", t)
continue
}
l.attributes[v.Key] = Attribute{
Value: v.Value.AsInterface(),
Type: t,
}
}
}
func (l *sentryLogger) Trace() LogEntry {
return &logEntry{
logger: l,
ctx: l.ctx,
level: LogLevelTrace,
severity: LogSeverityTrace,
attributes: make(map[string]Attribute),
}
}
func (l *sentryLogger) Debug() LogEntry {
return &logEntry{
logger: l,
ctx: l.ctx,
level: LogLevelDebug,
severity: LogSeverityDebug,
attributes: make(map[string]Attribute),
}
}
func (l *sentryLogger) Info() LogEntry {
return &logEntry{
logger: l,
ctx: l.ctx,
level: LogLevelInfo,
severity: LogSeverityInfo,
attributes: make(map[string]Attribute),
}
}
func (l *sentryLogger) Warn() LogEntry {
return &logEntry{
logger: l,
ctx: l.ctx,
level: LogLevelWarn,
severity: LogSeverityWarning,
attributes: make(map[string]Attribute),
}
}
func (l *sentryLogger) Error() LogEntry {
return &logEntry{
logger: l,
ctx: l.ctx,
level: LogLevelError,
severity: LogSeverityError,
attributes: make(map[string]Attribute),
}
}
func (l *sentryLogger) Fatal() LogEntry {
return &logEntry{
logger: l,
ctx: l.ctx,
level: LogLevelFatal,
severity: LogSeverityFatal,
attributes: make(map[string]Attribute),
}
}
func (l *sentryLogger) Panic() LogEntry {
return &logEntry{
logger: l,
ctx: l.ctx,
level: LogLevelFatal,
severity: LogSeverityFatal,
attributes: make(map[string]Attribute),
shouldPanic: true,
}
}
func (l *sentryLogger) GetCtx() context.Context {
return l.ctx
}
func (e *logEntry) WithCtx(ctx context.Context) LogEntry {
return &logEntry{
logger: e.logger,
ctx: ctx,
level: e.level,
severity: e.severity,
attributes: maps.Clone(e.attributes),
shouldPanic: e.shouldPanic,
}
}
func (e *logEntry) String(key, value string) LogEntry {
e.attributes[key] = Attribute{Value: value, Type: AttributeString}
return e
}
func (e *logEntry) Int(key string, value int) LogEntry {
e.attributes[key] = Attribute{Value: int64(value), Type: AttributeInt}
return e
}
func (e *logEntry) Int64(key string, value int64) LogEntry {
e.attributes[key] = Attribute{Value: value, Type: AttributeInt}
return e
}
func (e *logEntry) Float64(key string, value float64) LogEntry {
e.attributes[key] = Attribute{Value: value, Type: AttributeFloat}
return e
}
func (e *logEntry) Bool(key string, value bool) LogEntry {
e.attributes[key] = Attribute{Value: value, Type: AttributeBool}
return e
}
func (e *logEntry) Emit(args ...interface{}) {
e.logger.log(e.ctx, e.level, e.severity, fmt.Sprint(args...), e.attributes)
if e.level == LogLevelFatal {
if e.shouldPanic {
panic(fmt.Sprint(args...))
}
os.Exit(1)
}
}
func (e *logEntry) Emitf(format string, args ...interface{}) {
e.logger.log(e.ctx, e.level, e.severity, format, e.attributes, args...)
if e.level == LogLevelFatal {
if e.shouldPanic {
formattedMessage := fmt.Sprintf(format, args...)
panic(formattedMessage)
}
os.Exit(1)
}
}