Compare commits

..

No commits in common. "29c31d44f8d4f7515a4f5a21d433a41fc671b02a" and "47416f00ed9f82d512e3687a2a0c52c56b3f2b38" have entirely different histories.

8 changed files with 80 additions and 143 deletions

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"log/slog" "log/slog"
"runtime"
"time" "time"
"github.com/fatih/color" "github.com/fatih/color"
@ -31,13 +30,7 @@ func (c *ConsoleHandler) Handle(ctx context.Context, record slog.Record) error {
colorFunc = color.New(color.FgWhite).SprintfFunc() colorFunc = color.New(color.FgWhite).SprintfFunc()
} }
// Get the caller information fmt.Println(colorFunc("%s [%s]: %s", timestamp, record.Level, record.Message))
_, file, line, ok := runtime.Caller(5)
if !ok {
file = "???"
line = 0
}
fmt.Println(colorFunc("%s [%s] %s:%d: %s", timestamp, record.Level, file, line, record.Message))
return nil return nil
} }

View File

@ -15,7 +15,6 @@ type Event struct {
Data json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
func NewEvent(level, message string, data json.RawMessage) Event { func NewEvent(level, message string, data json.RawMessage) Event {
return Event{ return Event{
ID: uuid.New(), ID: uuid.New(),

2
go.mod
View File

@ -1,4 +1,4 @@
module sneak.berlin/go/simplelog module git.eeqj.de/sneak/go-simplelog
go 1.22.1 go 1.22.1

View File

@ -3,6 +3,6 @@ package simplelog
import "log/slog" import "log/slog"
// Handler defines the interface for different log outputs. // Handler defines the interface for different log outputs.
type ExtendedHandler interface { type Handler interface {
slog.Handler slog.Handler
} }

View File

@ -1,10 +1,13 @@
package simplelog package simplelog
import ( import (
"context"
"encoding/json"
"context" "context"
"encoding/json" "encoding/json"
"log" "log"
"log/slog" "log/slog"
"log"
) )
type JSONHandler struct{} type JSONHandler struct{}

View File

@ -10,8 +10,8 @@ import (
"net" "net"
"net/url" "net/url"
"os" "os"
"os"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"time" "time"
@ -43,8 +43,6 @@ var (
type RELPHandler struct { type RELPHandler struct {
relpServerURL string relpServerURL string
relpHost string
relpPort string
conn net.Conn conn net.Conn
ch chan Event ch chan Event
done chan struct{} done chan struct{}
@ -52,52 +50,51 @@ type RELPHandler struct {
timer *time.Timer timer *time.Timer
} }
func NewRELPHandler(relpURL string) (ExtendedHandler, error) { func NewRELPHandler(relpURL string) (*RELPHandler, error) {
parsedURL, err := url.Parse(relpURL) parsedURL, err := url.Parse(relpURL)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing RELP URL: %v", err) return nil, fmt.Errorf("Error parsing RELP URL: %v", err)
} }
if parsedURL.Scheme != "tcp" { if parsedURL.Scheme != "tcp" {
return nil, fmt.Errorf("the RELP URL must have the tcp scheme, got %s", parsedURL.Scheme) return nil, fmt.Errorf("RELP URL must have the tcp scheme, got %s", parsedURL.Scheme)
} }
host, port, err := net.SplitHostPort(parsedURL.Host)
if err != nil {
return nil, fmt.Errorf("Error splitting host and port: %v", err)
}
if err := os.MkdirAll(filepath.Join(cacheDir, "simplelog"), 0755); err != nil { if err := os.MkdirAll(filepath.Join(cacheDir, "simplelog"), 0755); err != nil {
return nil, fmt.Errorf("failed to create cache directory: %v", err) return nil, fmt.Errorf("Failed to create cache directory: %v", err)
} }
r := &RELPHandler{ r := &RELPHandler{
ch: make(chan Event, diskBufferLimit), relpServerURL: parsedURL.Host,
done: make(chan struct{}), ch: make(chan Event, diskBufferLimit),
failedCh: make(chan Event, diskBufferLimit), done: make(chan struct{}),
timer: time.NewTimer(diskWriteInterval), failedCh: make(chan Event, diskBufferLimit),
relpHost: host, timer: time.NewTimer(diskWriteInterval),
relpPort: port,
} }
if relpDebug { if relpDebug {
log.Printf("Created new RELP handler for server at %s", r.relpServerURL) log.Printf("Created new RELP handler for server at %s", r.relpServerURL)
} }
err = r.Startup()
if err != nil {
return nil, err
}
return r, nil return r, nil
} }
func (r *RELPHandler) connectToRELPServer() (net.Conn, error) { func (r *RELPHandler) connectToRELPServer() (net.Conn, error) {
conn, err := net.Dial("tcp", net.JoinHostPort(r.relpHost, r.relpPort)) parsedURL, err := url.Parse(r.relpServerURL)
if err != nil {
return nil, fmt.Errorf("Error parsing RELP URL: %v", err)
}
if parsedURL.Scheme != "tcp" {
return nil, fmt.Errorf("RELP URL must have the tcp scheme, got %s", parsedURL.Scheme)
}
host, port, err := net.SplitHostPort(parsedURL.Host)
if err != nil {
return nil, fmt.Errorf("Error splitting host and port: %v", err)
}
conn, err := net.Dial("tcp", net.JoinHostPort(host, port))
if err != nil { if err != nil {
if relpDebug { if relpDebug {
log.Printf("Failed to connect to RELP server at %s: %v", net.JoinHostPort(r.relpHost, r.relpPort), err) log.Printf("Failed to connect to RELP server at %s: %v", net.JoinHostPort(host, port), err)
} }
return nil, err return nil, err
} }
if relpDebug { if relpDebug {
log.Printf("Successfully connected to RELP server at %s", net.JoinHostPort(r.relpHost, r.relpPort)) log.Printf("Successfully connected to RELP server at %s", net.JoinHostPort(host, port))
} }
return conn, nil return conn, nil
} }
@ -142,20 +139,13 @@ func (r *RELPHandler) Handle(ctx context.Context, record slog.Record) error {
if err != nil { if err != nil {
return fmt.Errorf("error marshaling attributes: %v", err) return fmt.Errorf("error marshaling attributes: %v", err)
} }
// Get the caller information event := NewEvent(record.Level.String(), record.Message, jsonData)
_, file, line, ok := runtime.Caller(5) select {
if !ok { case r.ch <- event:
file = "???" return nil // Successfully sent event to channel
line = 0 default:
return fmt.Errorf("failed to log event: channel is full")
} }
event := NewExtendedEvent(record.Level.String(), record.Message, jsonData, file, line)
for _, handler := range cl.handlers {
if err := handler.Handle(ctx, event); err != nil {
return err
}
}
return nil
} }
func (r *RELPHandler) receiveEventsFromChannel() { func (r *RELPHandler) receiveEventsFromChannel() {

View File

@ -3,11 +3,12 @@ package simplelog
import ( import (
"context" "context"
"log" "log"
"runtime"
"log/slog" "log/slog"
"os" "os"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/mattn/go-isatty"
) )
var ( var (
@ -15,21 +16,53 @@ var (
webhookURL = os.Getenv("LOGGER_WEBHOOK_URL") webhookURL = os.Getenv("LOGGER_WEBHOOK_URL")
) )
type CustomHandler struct {
handlers []slog.Handler
}
func (cl *CustomHandler) Handle(ctx context.Context, record slog.Record) error {
for _, handler := range cl.handlers {
if err := handler.Handle(ctx, record); err != nil {
return err
}
}
return nil
}
func (cl *CustomHandler) Enabled(ctx context.Context, level slog.Level) bool {
for _, handler := range cl.handlers {
if handler.Enabled(ctx, level) {
return true
}
}
return false
}
func (cl *CustomHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newHandlers := make([]slog.Handler, len(cl.handlers))
for i, handler := range cl.handlers {
newHandlers[i] = handler.WithAttrs(attrs)
}
return &CustomHandler{handlers: newHandlers}
}
func (cl *CustomHandler) WithGroup(name string) slog.Handler {
newHandlers := make([]slog.Handler, len(cl.handlers))
for i, handler := range cl.handlers {
newHandlers[i] = handler.WithGroup(name)
}
return &CustomHandler{handlers: newHandlers}
}
var ourCustomLogger *slog.Logger var ourCustomLogger *slog.Logger
var ourCustomHandler slog.Handler
func init() { func init() {
ourCustomHandler = NewMultiplexHandler() ourCustomLogger = NewCustomHandler()
ourCustomLogger = slog.New(ourCustomHandler)
slog.SetDefault(ourCustomLogger) slog.SetDefault(ourCustomLogger)
} }
type MultiplexHandler struct { func NewCustomHandler() *CustomHandler {
handlers []ExtendedHandler cl := &CustomHandler{}
}
func NewMultiplexHandler() slog.Handler {
cl := &MultiplexHandler{}
if isatty.IsTerminal(os.Stdout.Fd()) { if isatty.IsTerminal(os.Stdout.Fd()) {
cl.handlers = append(cl.handlers, NewConsoleHandler()) cl.handlers = append(cl.handlers, NewConsoleHandler())
} else { } else {
@ -51,84 +84,3 @@ func NewMultiplexHandler() slog.Handler {
} }
return cl return cl
} }
func (cl *MultiplexHandler) Handle(ctx context.Context, record slog.Record) error {
for _, handler := range cl.handlers {
if err := handler.Handle(ctx, record); err != nil {
return err
}
}
return nil
}
func (cl *MultiplexHandler) Enabled(ctx context.Context, level slog.Level) bool {
// send us all events
return true
}
func (cl *MultiplexHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newHandlers := make([]ExtendedHandler, len(cl.handlers))
for i, handler := range cl.handlers {
newHandlers[i] = handler.WithAttrs(attrs)
}
return &MultiplexHandler{handlers: newHandlers}
}
func (cl *MultiplexHandler) WithGroup(name string) slog.Handler {
newHandlers := make([]ExtendedHandler, len(cl.handlers))
for i, handler := range cl.handlers {
newHandlers[i] = handler.WithGroup(name)
}
return &MultiplexHandler{handlers: newHandlers}
}
type ExtendedEvent interface {
GetID() uuid.UUID
GetTimestamp() time.Time
GetLevel() string
GetMessage() string
GetData() json.RawMessage
GetFile() string
GetLine() int
}
type extendedEvent struct {
Event
File string `json:"file"`
Line int `json:"line"`
}
func (e extendedEvent) GetID() uuid.UUID {
return e.ID
}
func (e extendedEvent) GetTimestamp() time.Time {
return e.Timestamp
}
func (e extendedEvent) GetLevel() string {
return e.Level
}
func (e extendedEvent) GetMessage() string {
return e.Message
}
func (e extendedEvent) GetData() json.RawMessage {
return e.Data
}
func (e extendedEvent) GetFile() string {
return e.File
}
func (e extendedEvent) GetLine() int {
return e.Line
}
func NewExtendedEvent(baseEvent Event, file string, line int) ExtendedEvent {
return extendedEvent{
Event: baseEvent,
File: file,
Line: line,
}
}

View File

@ -2,7 +2,7 @@
# Set the environment variables for the RELP server URL and optionally for the webhook URL # Set the environment variables for the RELP server URL and optionally for the webhook URL
export LOGGER_RELP_URL="tcp://10.201.1.18:20514" export LOGGER_RELP_URL="tcp://10.201.1.18:20514"
#export LOGGER_WEBHOOK_URL="https://example.com/webhook" export LOGGER_WEBHOOK_URL="https://example.com/webhook"
export RELP_DEBUG=1 export RELP_DEBUG=1
# Run the Go program # Run the Go program