Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions oncetask/context_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,26 @@ type contextKey string
const (
taskIDContextKey contextKey = "oncetask.taskID"
resourceKeyContextKey contextKey = "oncetask.resourceKey"
taskTypeContextKey contextKey = "oncetask.taskType"
)

// withTaskContext adds both task ID and resource key to the context for automatic logging
func withTaskContext(ctx context.Context, taskID, resourceKey string) context.Context {
// withTaskContext adds task ID, resource key, and task type to the context for automatic logging
func withTaskContext(ctx context.Context, taskID, resourceKey, taskType string) context.Context {
if taskID != "" {
ctx = context.WithValue(ctx, taskIDContextKey, taskID)
}
if resourceKey != "" {
ctx = context.WithValue(ctx, resourceKeyContextKey, resourceKey)
}
ctx = context.WithValue(ctx, taskTypeContextKey, taskType)
return ctx
}

func withSingleTaskContext[TaskKind ~string](ctx context.Context, tasks []OnceTask[TaskKind]) context.Context {
if len(tasks) == 0 {
return ctx
}
taskID := tasks[0].Id
resourceKey := tasks[0].ResourceKey
return withTaskContext(ctx, taskID, resourceKey)
return withTaskContext(ctx, tasks[0].Id, tasks[0].ResourceKey, string(tasks[0].Type))
}

// withResourceKeyTaskContext is used for resource key batched tasks and adds only the resource key to the context for automatic logging
Expand All @@ -43,7 +43,7 @@ func withResourceKeyTaskContext[TaskKind ~string](ctx context.Context, tasks []O
taskID = tasks[0].Id
}

return withTaskContext(ctx, taskID, tasks[0].ResourceKey)
return withTaskContext(ctx, taskID, tasks[0].ResourceKey, string(tasks[0].Type))
}

// GetCurrentTaskID returns the task ID stored in the context, or an empty string if not present.
Expand Down
8 changes: 4 additions & 4 deletions oncetask/context_keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ func TestTaskIDFromContext(t *testing.T) {
})

t.Run("returns task ID when present in context", func(t *testing.T) {
ctx := withTaskContext(context.Background(), "test-task-123", "")
ctx := withTaskContext(context.Background(), "test-task-123", "", "email")
if got := GetCurrentTaskID(ctx); got != "test-task-123" {
t.Errorf("Expected 'test-task-123', got: %q", got)
}
})

t.Run("returns task ID when both task ID and resource key are present", func(t *testing.T) {
ctx := withTaskContext(context.Background(), "task-456", "resource-789")
ctx := withTaskContext(context.Background(), "task-456", "resource-789", "email")
if got := GetCurrentTaskID(ctx); got != "task-456" {
t.Errorf("Expected 'task-456', got: %q", got)
}
Expand All @@ -37,14 +37,14 @@ func TestResourceKeyFromContext(t *testing.T) {
})

t.Run("returns resource key when present in context", func(t *testing.T) {
ctx := withTaskContext(context.Background(), "", "user-123")
ctx := withTaskContext(context.Background(), "", "user-123", "email")
if got := GetCurrentTaskResourceKey(ctx); got != "user-123" {
t.Errorf("Expected 'user-123', got: %q", got)
}
})

t.Run("returns resource key when both task ID and resource key are present", func(t *testing.T) {
ctx := withTaskContext(context.Background(), "task-456", "resource-789")
ctx := withTaskContext(context.Background(), "task-456", "resource-789", "email")
if got := GetCurrentTaskResourceKey(ctx); got != "resource-789" {
t.Errorf("Expected 'resource-789', got: %q", got)
}
Expand Down
4 changes: 4 additions & 0 deletions oncetask/context_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func (h *ContextHandler) Handle(ctx context.Context, r slog.Record) error {
if resourceKey, ok := ctx.Value(resourceKeyContextKey).(string); ok {
r.AddAttrs(slog.String("resourceKey", resourceKey))
}
// Extract task type from context and add it to the record
if taskType, ok := ctx.Value(taskTypeContextKey).(string); ok {
r.AddAttrs(slog.String("taskType", taskType))
}
return h.handler.Handle(ctx, r)
}

Expand Down
29 changes: 25 additions & 4 deletions oncetask/context_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func TestContextHandler_WithTaskIDOnly(t *testing.T) {
logger := slog.New(contextHandler)

// Create a context with only task ID
ctx := withTaskContext(context.Background(), "test-task-123", "")
ctx := withTaskContext(context.Background(), "test-task-123", "", "email")

// Log using the context
logger.InfoContext(ctx, "Processing task", "operation", "sync")
Expand Down Expand Up @@ -99,7 +99,7 @@ func TestContextHandler_WithResourceKeyOnly(t *testing.T) {
logger := slog.New(contextHandler)

// Create a context with only resource key (batch processing scenario)
ctx := withTaskContext(context.Background(), "", "user-123")
ctx := withTaskContext(context.Background(), "", "user-123", "email")

// Log using the context
logger.InfoContext(ctx, "Processing resource batch", "operation", "update")
Expand Down Expand Up @@ -144,7 +144,7 @@ func TestContextHandler_WithBothTaskIDAndResourceKey(t *testing.T) {
logger := slog.New(contextHandler)

// Create a context with both task ID and resource key
ctx := withTaskContext(context.Background(), "task-999", "user-456")
ctx := withTaskContext(context.Background(), "task-999", "user-456", "email")

// Log using the context
logger.InfoContext(ctx, "Processing task with resource key")
Expand All @@ -165,6 +165,27 @@ func TestContextHandler_WithBothTaskIDAndResourceKey(t *testing.T) {
}
}

func TestContextHandler_InjectsTaskType(t *testing.T) {
var buf bytes.Buffer
jsonHandler := slog.NewJSONHandler(&buf, &slog.HandlerOptions{
Level: slog.LevelInfo,
})
contextHandler := NewContextHandler(jsonHandler)
logger := slog.New(contextHandler)

ctx := withTaskContext(context.Background(), "task-1", "user-1", "send_email")
logger.InfoContext(ctx, "Processing")

var logEntry map[string]interface{}
if err := json.Unmarshal(buf.Bytes(), &logEntry); err != nil {
t.Fatalf("Failed to parse log output: %v", err)
}

if taskType, ok := logEntry["taskType"].(string); !ok || taskType != "send_email" {
t.Errorf("Expected taskType to be 'send_email', got: %v", logEntry["taskType"])
}
}

func TestContextHandler_WithAttrs(t *testing.T) {
var buf bytes.Buffer
jsonHandler := slog.NewJSONHandler(&buf, &slog.HandlerOptions{
Expand All @@ -175,7 +196,7 @@ func TestContextHandler_WithAttrs(t *testing.T) {
// Create a logger with additional attributes
logger := slog.New(contextHandler).With("service", "oncetask")

ctx := withTaskContext(context.Background(), "task-789", "")
ctx := withTaskContext(context.Background(), "task-789", "", "email")
logger.InfoContext(ctx, "Test message")

var logEntry map[string]interface{}
Expand Down
2 changes: 1 addition & 1 deletion oncetask/once_task_firestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func (m *firestoreOnceTaskManager[TaskKind]) runLoop(
if len(cancelledTasks) > 0 {
cancellationHandler := getCancellationHandler[TaskKind](config)
for _, task := range cancelledTasks {
ctx := withTaskContext(handlerCtx, task.Id, task.ResourceKey)
ctx := withTaskContext(handlerCtx, task.Id, task.ResourceKey, string(task.Type))
result, execErr := SafeExecute(ctx, cancellationHandler, &task)
if err := m.completeBatch(m.ctx, []OnceTask[TaskKind]{task}, execErr, result, config); err != nil {
slog.ErrorContext(m.ctx, "Failed to complete cancelled task", "error", err, "taskId", task.Id)
Expand Down
Loading