Sfoglia il codice sorgente

chore: refactoring

adamdottv 9 mesi fa
parent
commit
ae86ef519c

+ 15 - 5
internal/db/logs.sql.go

@@ -10,7 +10,7 @@ import (
 	"database/sql"
 	"database/sql"
 )
 )
 
 
-const createLog = `-- name: CreateLog :exec
+const createLog = `-- name: CreateLog :one
 INSERT INTO logs (
 INSERT INTO logs (
     id,
     id,
     session_id,
     session_id,
@@ -27,7 +27,7 @@ INSERT INTO logs (
     ?,
     ?,
     ?,
     ?,
     ?
     ?
-)
+) RETURNING id, session_id, timestamp, level, message, attributes, created_at
 `
 `
 
 
 type CreateLogParams struct {
 type CreateLogParams struct {
@@ -40,8 +40,8 @@ type CreateLogParams struct {
 	CreatedAt  int64          `json:"created_at"`
 	CreatedAt  int64          `json:"created_at"`
 }
 }
 
 
-func (q *Queries) CreateLog(ctx context.Context, arg CreateLogParams) error {
-	_, err := q.exec(ctx, q.createLogStmt, createLog,
+func (q *Queries) CreateLog(ctx context.Context, arg CreateLogParams) (Log, error) {
+	row := q.queryRow(ctx, q.createLogStmt, createLog,
 		arg.ID,
 		arg.ID,
 		arg.SessionID,
 		arg.SessionID,
 		arg.Timestamp,
 		arg.Timestamp,
@@ -50,7 +50,17 @@ func (q *Queries) CreateLog(ctx context.Context, arg CreateLogParams) error {
 		arg.Attributes,
 		arg.Attributes,
 		arg.CreatedAt,
 		arg.CreatedAt,
 	)
 	)
-	return err
+	var i Log
+	err := row.Scan(
+		&i.ID,
+		&i.SessionID,
+		&i.Timestamp,
+		&i.Level,
+		&i.Message,
+		&i.Attributes,
+		&i.CreatedAt,
+	)
+	return i, err
 }
 }
 
 
 const listAllLogs = `-- name: ListAllLogs :many
 const listAllLogs = `-- name: ListAllLogs :many

+ 1 - 1
internal/db/querier.go

@@ -11,7 +11,7 @@ import (
 
 
 type Querier interface {
 type Querier interface {
 	CreateFile(ctx context.Context, arg CreateFileParams) (File, error)
 	CreateFile(ctx context.Context, arg CreateFileParams) (File, error)
-	CreateLog(ctx context.Context, arg CreateLogParams) error
+	CreateLog(ctx context.Context, arg CreateLogParams) (Log, error)
 	CreateMessage(ctx context.Context, arg CreateMessageParams) (Message, error)
 	CreateMessage(ctx context.Context, arg CreateMessageParams) (Message, error)
 	CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error)
 	CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error)
 	DeleteFile(ctx context.Context, id string) error
 	DeleteFile(ctx context.Context, id string) error

+ 3 - 3
internal/db/sql/logs.sql

@@ -1,4 +1,4 @@
--- name: CreateLog :exec
+-- name: CreateLog :one
 INSERT INTO logs (
 INSERT INTO logs (
     id,
     id,
     session_id,
     session_id,
@@ -15,7 +15,7 @@ INSERT INTO logs (
     ?,
     ?,
     ?,
     ?,
     ?
     ?
-);
+) RETURNING *;
 
 
 -- name: ListLogsBySession :many
 -- name: ListLogsBySession :many
 SELECT * FROM logs
 SELECT * FROM logs
@@ -25,4 +25,4 @@ ORDER BY timestamp ASC;
 -- name: ListAllLogs :many
 -- name: ListAllLogs :many
 SELECT * FROM logs
 SELECT * FROM logs
 ORDER BY timestamp DESC
 ORDER BY timestamp DESC
-LIMIT ?;
+LIMIT ?;

+ 65 - 62
internal/logging/logging.go

@@ -22,11 +22,11 @@ import (
 type Log struct {
 type Log struct {
 	ID         string
 	ID         string
 	SessionID  string
 	SessionID  string
-	Timestamp  int64
+	Timestamp  time.Time
 	Level      string
 	Level      string
 	Message    string
 	Message    string
 	Attributes map[string]string
 	Attributes map[string]string
-	CreatedAt  int64
+	CreatedAt  time.Time
 }
 }
 
 
 const (
 const (
@@ -36,7 +36,7 @@ const (
 type Service interface {
 type Service interface {
 	pubsub.Subscriber[Log]
 	pubsub.Subscriber[Log]
 
 
-	Create(ctx context.Context, log Log) error
+	Create(ctx context.Context, timestamp time.Time, level, message string, attributes map[string]string, sessionID string) error
 	ListBySession(ctx context.Context, sessionID string) ([]Log, error)
 	ListBySession(ctx context.Context, sessionID string) ([]Log, error)
 	ListAll(ctx context.Context, limit int) ([]Log, error)
 	ListAll(ctx context.Context, limit int) ([]Log, error)
 }
 }
@@ -69,42 +69,35 @@ func GetService() Service {
 	return globalLoggingService
 	return globalLoggingService
 }
 }
 
 
-func (s *service) Create(ctx context.Context, log Log) error {
-	if log.ID == "" {
-		log.ID = uuid.New().String()
-	}
-	if log.Timestamp == 0 {
-		log.Timestamp = time.Now().UnixMilli()
-	}
-	if log.CreatedAt == 0 {
-		log.CreatedAt = time.Now().UnixMilli()
-	}
-	if log.Level == "" {
-		log.Level = "info"
+func (s *service) Create(ctx context.Context, timestamp time.Time, level, message string, attributes map[string]string, sessionID string) error {
+	if level == "" {
+		level = "info"
 	}
 	}
 
 
 	var attributesJSON sql.NullString
 	var attributesJSON sql.NullString
-	if len(log.Attributes) > 0 {
-		attributesBytes, err := json.Marshal(log.Attributes)
+	if len(attributes) > 0 {
+		attributesBytes, err := json.Marshal(attributes)
 		if err != nil {
 		if err != nil {
 			return fmt.Errorf("failed to marshal log attributes: %w", err)
 			return fmt.Errorf("failed to marshal log attributes: %w", err)
 		}
 		}
 		attributesJSON = sql.NullString{String: string(attributesBytes), Valid: true}
 		attributesJSON = sql.NullString{String: string(attributesBytes), Valid: true}
 	}
 	}
 
 
-	err := s.db.CreateLog(ctx, db.CreateLogParams{
-		ID:         log.ID,
-		SessionID:  sql.NullString{String: log.SessionID, Valid: log.SessionID != ""},
-		Timestamp:  log.Timestamp,
-		Level:      log.Level,
-		Message:    log.Message,
+	dbLog, err := s.db.CreateLog(ctx, db.CreateLogParams{
+		ID:         uuid.New().String(),
+		SessionID:  sql.NullString{String: sessionID, Valid: sessionID != ""},
+		Timestamp:  timestamp.UnixMilli(),
+		Level:      level,
+		Message:    message,
 		Attributes: attributesJSON,
 		Attributes: attributesJSON,
-		CreatedAt:  log.CreatedAt,
+		CreatedAt:  time.Now().UnixMilli(),
 	})
 	})
+
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("db.CreateLog: %w", err)
 		return fmt.Errorf("db.CreateLog: %w", err)
 	}
 	}
 
 
+	log := s.fromDBItem(dbLog)
 	s.broker.Publish(EventLogCreated, log)
 	s.broker.Publish(EventLogCreated, log)
 	return nil
 	return nil
 }
 }
@@ -114,7 +107,12 @@ func (s *service) ListBySession(ctx context.Context, sessionID string) ([]Log, e
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("db.ListLogsBySession: %w", err)
 		return nil, fmt.Errorf("db.ListLogsBySession: %w", err)
 	}
 	}
-	return s.fromDBItems(dbLogs)
+
+	logs := make([]Log, len(dbLogs))
+	for i, dbSess := range dbLogs {
+		logs[i] = s.fromDBItem(dbSess)
+	}
+	return logs, nil
 }
 }
 
 
 func (s *service) ListAll(ctx context.Context, limit int) ([]Log, error) {
 func (s *service) ListAll(ctx context.Context, limit int) ([]Log, error) {
@@ -122,39 +120,41 @@ func (s *service) ListAll(ctx context.Context, limit int) ([]Log, error) {
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("db.ListAllLogs: %w", err)
 		return nil, fmt.Errorf("db.ListAllLogs: %w", err)
 	}
 	}
-	return s.fromDBItems(dbLogs)
+	logs := make([]Log, len(dbLogs))
+	for i, dbSess := range dbLogs {
+		logs[i] = s.fromDBItem(dbSess)
+	}
+	return logs, nil
 }
 }
 
 
 func (s *service) Subscribe(ctx context.Context) <-chan pubsub.Event[Log] {
 func (s *service) Subscribe(ctx context.Context) <-chan pubsub.Event[Log] {
 	return s.broker.Subscribe(ctx)
 	return s.broker.Subscribe(ctx)
 }
 }
 
 
-func (s *service) fromDBItems(items []db.Log) ([]Log, error) {
-	logs := make([]Log, len(items))
-	for i, item := range items {
-		log := Log{
-			ID:        item.ID,
-			SessionID: item.SessionID.String,
-			Timestamp: item.Timestamp * 1000,
-			Level:     item.Level,
-			Message:   item.Message,
-			CreatedAt: item.CreatedAt * 1000,
-		}
-		if item.Attributes.Valid && item.Attributes.String != "" {
-			if err := json.Unmarshal([]byte(item.Attributes.String), &log.Attributes); err != nil {
-				slog.Error("Failed to unmarshal log attributes", "log_id", item.ID, "error", err)
-				log.Attributes = make(map[string]string)
-			}
-		} else {
+func (s *service) fromDBItem(item db.Log) Log {
+	log := Log{
+		ID:        item.ID,
+		SessionID: item.SessionID.String,
+		Timestamp: time.UnixMilli(item.Timestamp),
+		Level:     item.Level,
+		Message:   item.Message,
+		CreatedAt: time.UnixMilli(item.CreatedAt),
+	}
+
+	if item.Attributes.Valid && item.Attributes.String != "" {
+		if err := json.Unmarshal([]byte(item.Attributes.String), &log.Attributes); err != nil {
+			slog.Error("Failed to unmarshal log attributes", "log_id", item.ID, "error", err)
 			log.Attributes = make(map[string]string)
 			log.Attributes = make(map[string]string)
 		}
 		}
-		logs[i] = log
+	} else {
+		log.Attributes = make(map[string]string)
 	}
 	}
-	return logs, nil
+
+	return log
 }
 }
 
 
-func Create(ctx context.Context, log Log) error {
-	return GetService().Create(ctx, log)
+func Create(ctx context.Context, timestamp time.Time, level, message string, attributes map[string]string, sessionID string) error {
+	return GetService().Create(ctx, timestamp, level, message, attributes, sessionID)
 }
 }
 
 
 func ListBySession(ctx context.Context, sessionID string) ([]Log, error) {
 func ListBySession(ctx context.Context, sessionID string) ([]Log, error) {
@@ -175,9 +175,13 @@ func (sw *slogWriter) Write(p []byte) (n int, err error) {
 	// Example: time=2024-05-09T12:34:56.789-05:00 level=INFO msg="User request" session=xyz foo=bar
 	// Example: time=2024-05-09T12:34:56.789-05:00 level=INFO msg="User request" session=xyz foo=bar
 	d := logfmt.NewDecoder(bytes.NewReader(p))
 	d := logfmt.NewDecoder(bytes.NewReader(p))
 	for d.ScanRecord() {
 	for d.ScanRecord() {
-		logEntry := Log{
-			Attributes: make(map[string]string),
-		}
+		var timestamp time.Time
+		var level string
+		var message string
+		var sessionID string
+		var attributes map[string]string
+
+		attributes = make(map[string]string)
 		hasTimestamp := false
 		hasTimestamp := false
 
 
 		for d.ScanKeyval() {
 		for d.ScanKeyval() {
@@ -191,45 +195,44 @@ func (sw *slogWriter) Write(p []byte) (n int, err error) {
 					parsedTime, timeErr = time.Parse(time.RFC3339, value)
 					parsedTime, timeErr = time.Parse(time.RFC3339, value)
 					if timeErr != nil {
 					if timeErr != nil {
 						slog.Error("Failed to parse time in slog writer", "value", value, "error", timeErr)
 						slog.Error("Failed to parse time in slog writer", "value", value, "error", timeErr)
-						logEntry.Timestamp = time.Now().UnixMilli()
+						timestamp = time.Now()
 						hasTimestamp = true
 						hasTimestamp = true
 						continue
 						continue
 					}
 					}
 				}
 				}
-				logEntry.Timestamp = parsedTime.UnixMilli()
+				timestamp = parsedTime
 				hasTimestamp = true
 				hasTimestamp = true
 			case "level":
 			case "level":
-				logEntry.Level = strings.ToLower(value)
+				level = strings.ToLower(value)
 			case "msg", "message":
 			case "msg", "message":
-				logEntry.Message = value
-			case "session_id", "session", "sid":
-				logEntry.SessionID = value
+				message = value
+			case "session_id":
+				sessionID = value
 			default:
 			default:
-				logEntry.Attributes[key] = value
+				attributes[key] = value
 			}
 			}
 		}
 		}
-
 		if d.Err() != nil {
 		if d.Err() != nil {
 			return len(p), fmt.Errorf("logfmt.ScanRecord: %w", d.Err())
 			return len(p), fmt.Errorf("logfmt.ScanRecord: %w", d.Err())
 		}
 		}
 
 
 		if !hasTimestamp {
 		if !hasTimestamp {
-			logEntry.Timestamp = time.Now().UnixMilli()
+			timestamp = time.Now()
 		}
 		}
 
 
 		// Create log entry via the service (non-blocking or handle error appropriately)
 		// Create log entry via the service (non-blocking or handle error appropriately)
 		// Using context.Background() as this is a low-level logging write.
 		// Using context.Background() as this is a low-level logging write.
-		go func(le Log) { // Run in a goroutine to avoid blocking slog
+		go func(timestamp time.Time, level, message string, attributes map[string]string, sessionID string) { // Run in a goroutine to avoid blocking slog
 			if globalLoggingService == nil {
 			if globalLoggingService == nil {
 				// If the logging service is not initialized, log the message to stderr
 				// If the logging service is not initialized, log the message to stderr
 				// fmt.Fprintf(os.Stderr, "ERROR [logging.slogWriter]: logging service not initialized\n")
 				// fmt.Fprintf(os.Stderr, "ERROR [logging.slogWriter]: logging service not initialized\n")
 				return
 				return
 			}
 			}
-			if err := Create(context.Background(), le); err != nil {
+			if err := Create(context.Background(), timestamp, level, message, attributes, sessionID); err != nil {
 				// Log internal error using a more primitive logger to avoid loops
 				// Log internal error using a more primitive logger to avoid loops
 				fmt.Fprintf(os.Stderr, "ERROR [logging.slogWriter]: failed to persist log: %v\n", err)
 				fmt.Fprintf(os.Stderr, "ERROR [logging.slogWriter]: failed to persist log: %v\n", err)
 			}
 			}
-		}(logEntry)
+		}(timestamp, level, message, attributes, sessionID)
 	}
 	}
 	if d.Err() != nil {
 	if d.Err() != nil {
 		return len(p), fmt.Errorf("logfmt.ScanRecord final: %w", d.Err())
 		return len(p), fmt.Errorf("logfmt.ScanRecord final: %w", d.Err())

+ 1 - 1
internal/message/content.go

@@ -318,7 +318,7 @@ func (m *Message) AddFinish(reason FinishReason) {
 			break
 			break
 		}
 		}
 	}
 	}
-	m.Parts = append(m.Parts, Finish{Reason: reason, Time: time.Now().Unix()})
+	m.Parts = append(m.Parts, Finish{Reason: reason, Time: time.Now().UnixMilli()})
 }
 }
 
 
 func (m *Message) AddImageURL(url, detail string) {
 func (m *Message) AddImageURL(url, detail string) {

+ 1 - 1
internal/tui/components/logs/details.go

@@ -66,7 +66,7 @@ func (i *detailCmp) updateContent() {
 	levelStyle := getLevelStyle(i.currentLog.Level)
 	levelStyle := getLevelStyle(i.currentLog.Level)
 
 
 	// Format timestamp
 	// Format timestamp
-	timeStr := time.UnixMilli(i.currentLog.Timestamp).Format(time.RFC3339)
+	timeStr := i.currentLog.Timestamp.Format(time.RFC3339)
 
 
 	header := lipgloss.JoinHorizontal(
 	header := lipgloss.JoinHorizontal(
 		lipgloss.Center,
 		lipgloss.Center,

+ 1 - 2
internal/tui/components/logs/table.go

@@ -2,7 +2,6 @@ package logs
 
 
 import (
 import (
 	"context"
 	"context"
-	"time"
 
 
 	"github.com/charmbracelet/bubbles/key"
 	"github.com/charmbracelet/bubbles/key"
 	"github.com/charmbracelet/bubbles/table"
 	"github.com/charmbracelet/bubbles/table"
@@ -161,7 +160,7 @@ func (i *tableCmp) updateRows() {
 
 
 	for _, log := range i.logs {
 	for _, log := range i.logs {
 		// Format timestamp as time
 		// Format timestamp as time
-		timeStr := time.UnixMilli(log.Timestamp).Format("15:04:05")
+		timeStr := log.Timestamp.Format("15:04:05")
 
 
 		// Include ID as hidden first column for selection
 		// Include ID as hidden first column for selection
 		row := table.Row{
 		row := table.Row{