feat: standardize logging across UI components

- Replaced console.log and console.error statements with logger methods from @automaker/utils in various UI components, ensuring consistent log formatting and improved readability.
- Enhanced error handling by utilizing logger methods to provide clearer context for issues encountered during operations.
- Updated multiple views and hooks to integrate the new logging system, improving maintainability and debugging capabilities.

This update significantly enhances the observability of UI components, facilitating easier troubleshooting and monitoring.
This commit is contained in:
Shirone
2026-01-02 17:25:13 +01:00
parent 96a999817f
commit 69f3ba9724
86 changed files with 1079 additions and 677 deletions

View File

@@ -13,6 +13,9 @@ import { toast } from 'sonner';
import { useAutoMode } from '@/hooks/use-auto-mode';
import { truncateDescription } from '@/lib/utils';
import { getBlockingDependencies } from '@automaker/dependency-resolver';
import { createLogger } from '@automaker/utils/logger';
const logger = createLogger('BoardActions');
interface UseBoardActionsProps {
currentProject: { path: string; id: string } | null;
@@ -112,8 +115,8 @@ export function useBoardActions({
if (api?.worktree?.create) {
const result = await api.worktree.create(currentProject.path, finalBranchName);
if (result.success && result.worktree) {
console.log(
`[Board] Worktree for branch "${finalBranchName}" ${
logger.info(
`Worktree for branch "${finalBranchName}" ${
result.worktree?.isNew ? 'created' : 'already exists'
}`
);
@@ -125,8 +128,8 @@ export function useBoardActions({
// Refresh worktree list in UI
onWorktreeCreated?.();
} else if (!result.success) {
console.error(
`[Board] Failed to create worktree for branch "${finalBranchName}":`,
logger.error(
`Failed to create worktree for branch "${finalBranchName}":`,
result.error
);
toast.error('Failed to create worktree', {
@@ -135,7 +138,7 @@ export function useBoardActions({
}
}
} catch (error) {
console.error('[Board] Error creating worktree:', error);
logger.error('Error creating worktree:', error);
toast.error('Failed to create worktree', {
description: error instanceof Error ? error.message : 'An error occurred',
});
@@ -180,7 +183,7 @@ export function useBoardActions({
}
})
.catch((error) => {
console.error('[Board] Error generating title:', error);
logger.error('Error generating title:', error);
// Clear generating flag on error
const titleUpdates = { titleGenerating: false };
updateFeature(createdFeature.id, titleUpdates);
@@ -229,16 +232,16 @@ export function useBoardActions({
if (api?.worktree?.create) {
const result = await api.worktree.create(currentProject.path, finalBranchName);
if (result.success) {
console.log(
`[Board] Worktree for branch "${finalBranchName}" ${
logger.info(
`Worktree for branch "${finalBranchName}" ${
result.worktree?.isNew ? 'created' : 'already exists'
}`
);
// Refresh worktree list in UI
onWorktreeCreated?.();
} else {
console.error(
`[Board] Failed to create worktree for branch "${finalBranchName}":`,
logger.error(
`Failed to create worktree for branch "${finalBranchName}":`,
result.error
);
toast.error('Failed to create worktree', {
@@ -247,7 +250,7 @@ export function useBoardActions({
}
}
} catch (error) {
console.error('[Board] Error creating worktree:', error);
logger.error('Error creating worktree:', error);
toast.error('Failed to create worktree', {
description: error instanceof Error ? error.message : 'An error occurred',
});
@@ -292,7 +295,7 @@ export function useBoardActions({
description: `Stopped and deleted: ${truncateDescription(feature.description)}`,
});
} catch (error) {
console.error('[Board] Error stopping feature before delete:', error);
logger.error('Error stopping feature before delete:', error);
toast.error('Failed to stop agent', {
description: 'The feature will still be deleted.',
});
@@ -305,13 +308,13 @@ export function useBoardActions({
for (const imagePathObj of feature.imagePaths) {
try {
await api.deleteFile(imagePathObj.path);
console.log(`[Board] Deleted image: ${imagePathObj.path}`);
logger.info(`Deleted image: ${imagePathObj.path}`);
} catch (error) {
console.error(`[Board] Failed to delete image ${imagePathObj.path}:`, error);
logger.error(`Failed to delete image ${imagePathObj.path}:`, error);
}
}
} catch (error) {
console.error(`[Board] Error deleting images for feature ${featureId}:`, error);
logger.error(`Error deleting images for feature ${featureId}:`, error);
}
}
@@ -328,7 +331,7 @@ export function useBoardActions({
try {
const api = getElectronAPI();
if (!api?.autoMode) {
console.error('Auto mode API not available');
logger.error('Auto mode API not available');
return;
}
@@ -341,16 +344,13 @@ export function useBoardActions({
);
if (result.success) {
console.log(
'[Board] Feature run started successfully, branch:',
feature.branchName || 'default'
);
logger.info('Feature run started successfully, branch:', feature.branchName || 'default');
} else {
console.error('[Board] Failed to run feature:', result.error);
logger.error('Failed to run feature:', result.error);
await loadFeatures();
}
} catch (error) {
console.error('[Board] Error running feature:', error);
logger.error('Error running feature:', error);
await loadFeatures();
}
},
@@ -392,7 +392,7 @@ export function useBoardActions({
updateFeature(feature.id, updates);
// Must await to ensure feature status is persisted before starting agent
await persistFeatureUpdate(feature.id, updates);
console.log('[Board] Feature moved to in_progress, starting agent...');
logger.info('Feature moved to in_progress, starting agent...');
await handleRunFeature(feature);
return true;
},
@@ -413,20 +413,20 @@ export function useBoardActions({
try {
const api = getElectronAPI();
if (!api?.autoMode) {
console.error('Auto mode API not available');
logger.error('Auto mode API not available');
return;
}
const result = await api.autoMode.verifyFeature(currentProject.path, feature.id);
if (result.success) {
console.log('[Board] Feature verification started successfully');
logger.info('Feature verification started successfully');
} else {
console.error('[Board] Failed to verify feature:', result.error);
logger.error('Failed to verify feature:', result.error);
await loadFeatures();
}
} catch (error) {
console.error('[Board] Error verifying feature:', error);
logger.error('Error verifying feature:', error);
await loadFeatures();
}
},
@@ -435,20 +435,20 @@ export function useBoardActions({
const handleResumeFeature = useCallback(
async (feature: Feature) => {
console.log('[Board] handleResumeFeature called for feature:', feature.id);
logger.info('handleResumeFeature called for feature:', feature.id);
if (!currentProject) {
console.error('[Board] No current project');
logger.error('No current project');
return;
}
try {
const api = getElectronAPI();
if (!api?.autoMode) {
console.error('[Board] Auto mode API not available');
logger.error('Auto mode API not available');
return;
}
console.log('[Board] Calling resumeFeature API...', {
logger.info('Calling resumeFeature API...', {
projectPath: currentProject.path,
featureId: feature.id,
useWorktrees,
@@ -460,16 +460,16 @@ export function useBoardActions({
useWorktrees
);
console.log('[Board] resumeFeature result:', result);
logger.info('resumeFeature result:', result);
if (result.success) {
console.log('[Board] Feature resume started successfully');
logger.info('Feature resume started successfully');
} else {
console.error('[Board] Failed to resume feature:', result.error);
logger.error('Failed to resume feature:', result.error);
await loadFeatures();
}
} catch (error) {
console.error('[Board] Error resuming feature:', error);
logger.error('Error resuming feature:', error);
await loadFeatures();
}
},
@@ -523,7 +523,7 @@ export function useBoardActions({
const api = getElectronAPI();
if (!api?.autoMode?.followUpFeature) {
console.error('Follow-up feature API not available');
logger.error('Follow-up feature API not available');
toast.error('Follow-up not available', {
description: 'This feature is not available in the current version.',
});
@@ -559,7 +559,7 @@ export function useBoardActions({
// No worktreePath - server derives from feature.branchName
)
.catch((error) => {
console.error('[Board] Error sending follow-up:', error);
logger.error('Error sending follow-up:', error);
toast.error('Failed to send follow-up', {
description: error instanceof Error ? error.message : 'An error occurred',
});
@@ -587,7 +587,7 @@ export function useBoardActions({
try {
const api = getElectronAPI();
if (!api?.autoMode?.commitFeature) {
console.error('Commit feature API not available');
logger.error('Commit feature API not available');
toast.error('Commit not available', {
description: 'This feature is not available in the current version.',
});
@@ -610,14 +610,14 @@ export function useBoardActions({
// Refresh worktree selector to update commit counts
onWorktreeCreated?.();
} else {
console.error('[Board] Failed to commit feature:', result.error);
logger.error('Failed to commit feature:', result.error);
toast.error('Failed to commit feature', {
description: result.error || 'An error occurred',
});
await loadFeatures();
}
} catch (error) {
console.error('[Board] Error committing feature:', error);
logger.error('Error committing feature:', error);
toast.error('Failed to commit feature', {
description: error instanceof Error ? error.message : 'An error occurred',
});
@@ -634,7 +634,7 @@ export function useBoardActions({
try {
const api = getElectronAPI();
if (!api?.worktree?.mergeFeature) {
console.error('Worktree API not available');
logger.error('Worktree API not available');
toast.error('Merge not available', {
description: 'This feature is not available in the current version.',
});
@@ -651,13 +651,13 @@ export function useBoardActions({
)}`,
});
} else {
console.error('[Board] Failed to merge feature:', result.error);
logger.error('Failed to merge feature:', result.error);
toast.error('Failed to merge feature', {
description: result.error || 'An error occurred',
});
}
} catch (error) {
console.error('[Board] Error merging feature:', error);
logger.error('Error merging feature:', error);
toast.error('Failed to merge feature', {
description: error instanceof Error ? error.message : 'An error occurred',
});
@@ -747,7 +747,7 @@ export function useBoardActions({
: `Stopped working on: ${truncateDescription(feature.description)}`,
});
} catch (error) {
console.error('[Board] Error stopping feature:', error);
logger.error('Error stopping feature:', error);
toast.error('Failed to stop agent', {
description: error instanceof Error ? error.message : 'An error occurred',
});
@@ -857,7 +857,7 @@ export function useBoardActions({
try {
await autoMode.stopFeature(feature.id);
} catch (error) {
console.error('[Board] Error stopping feature before archive:', error);
logger.error('Error stopping feature before archive:', error);
}
}
// Archive the feature by setting status to completed

View File

@@ -1,10 +1,13 @@
import { useState, useCallback } from 'react';
import { createLogger } from '@automaker/utils/logger';
import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
import { Feature } from '@/store/app-store';
import { useAppStore } from '@/store/app-store';
import { toast } from 'sonner';
import { COLUMNS, ColumnId } from '../constants';
const logger = createLogger('BoardDragDrop');
interface UseBoardDragDropProps {
features: Feature[];
currentProject: { path: string; id: string } | null;
@@ -63,7 +66,7 @@ export function useBoardDragDrop({
if (draggedFeature.status === 'in_progress') {
// Only allow dragging in_progress if it's not currently running
if (isRunningTask) {
console.log('[Board] Cannot drag feature - currently running');
logger.debug('Cannot drag feature - currently running');
return;
}
}

View File

@@ -1,6 +1,9 @@
import { useEffect, useRef } from 'react';
import { getElectronAPI } from '@/lib/electron';
import { useAppStore } from '@/store/app-store';
import { createLogger } from '@automaker/utils/logger';
const logger = createLogger('BoardEffects');
interface UseBoardEffectsProps {
currentProject: { path: string; id: string } | null;
@@ -70,12 +73,7 @@ export function useBoardEffects({
if (!api.specRegeneration) return;
const unsubscribe = api.specRegeneration.onEvent((event) => {
console.log(
'[BoardView] Spec regeneration event:',
event.type,
'for project:',
event.projectPath
);
logger.info('Spec regeneration event:', event.type, 'for project:', event.projectPath);
if (event.projectPath !== specCreatingForProject) {
return;
@@ -108,7 +106,7 @@ export function useBoardEffects({
const { clearRunningTasks, addRunningTask } = useAppStore.getState();
if (status.runningFeatures) {
console.log('[Board] Syncing running tasks from backend:', status.runningFeatures);
logger.info('Syncing running tasks from backend:', status.runningFeatures);
clearRunningTasks(projectId);
@@ -118,7 +116,7 @@ export function useBoardEffects({
}
}
} catch (error) {
console.error('[Board] Failed to sync running tasks:', error);
logger.error('Failed to sync running tasks:', error);
}
};

View File

@@ -2,6 +2,9 @@ import { useState, useCallback, useEffect, useRef } from 'react';
import { useAppStore, Feature } from '@/store/app-store';
import { getElectronAPI } from '@/lib/electron';
import { toast } from 'sonner';
import { createLogger } from '@automaker/utils/logger';
const logger = createLogger('BoardFeatures');
interface UseBoardFeaturesProps {
currentProject: { path: string; id: string } | null;
@@ -32,7 +35,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
// If project switched, mark it but don't clear features yet
// We'll clear after successful API load to prevent data loss
if (isProjectSwitch) {
console.log(`[BoardView] Project switch detected: ${previousPath} -> ${currentPath}`);
logger.info(`Project switch detected: ${previousPath} -> ${currentPath}`);
isSwitchingProjectRef.current = true;
isInitialLoadRef.current = true;
}
@@ -48,7 +51,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
try {
const api = getElectronAPI();
if (!api.features) {
console.error('[BoardView] Features API not available');
logger.error('Features API not available');
// Keep cached features if API is unavailable
return;
}
@@ -73,7 +76,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
setPersistedCategories([]);
}
} else if (!result.success && result.error) {
console.error('[BoardView] API returned error:', result.error);
logger.error('API returned error:', result.error);
// If it's a new project or the error indicates no features found,
// that's expected - start with empty array
if (isProjectSwitch) {
@@ -83,7 +86,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
// Otherwise keep cached features
}
} catch (error) {
console.error('Failed to load features:', error);
logger.error('Failed to load features:', error);
// On error, keep existing cached features for the current project
// Only clear on project switch if we have no features from server
if (isProjectSwitch && cachedFeatures.length === 0) {
@@ -115,7 +118,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
setPersistedCategories([]);
}
} catch (error) {
console.error('Failed to load categories:', error);
logger.error('Failed to load categories:', error);
// If file doesn't exist, ensure categories are cleared
setPersistedCategories([]);
}
@@ -147,7 +150,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
setPersistedCategories(categories);
}
} catch (error) {
console.error('Failed to save category:', error);
logger.error('Failed to save category:', error);
}
},
[currentProject, persistedCategories]
@@ -165,7 +168,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
currentProject &&
event.projectPath === currentProject.path
) {
console.log('[BoardView] Spec regeneration complete, refreshing features');
logger.info('Spec regeneration complete, refreshing features');
loadFeatures();
}
});
@@ -190,27 +193,27 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
if (event.type === 'auto_mode_feature_complete') {
// Reload features when a feature is completed
console.log('[Board] Feature completed, reloading features...');
logger.info('Feature completed, reloading features...');
loadFeatures();
// Play ding sound when feature is done (unless muted)
const { muteDoneSound } = useAppStore.getState();
if (!muteDoneSound) {
const audio = new Audio('/sounds/ding.mp3');
audio.play().catch((err) => console.warn('Could not play ding sound:', err));
audio.play().catch((err) => logger.warn('Could not play ding sound:', err));
}
} else if (event.type === 'plan_approval_required') {
// Reload features when plan is generated and requires approval
// This ensures the feature card shows the "Approve Plan" button
console.log('[Board] Plan approval required, reloading features...');
logger.info('Plan approval required, reloading features...');
loadFeatures();
} else if (event.type === 'pipeline_step_started') {
// Pipeline steps update the feature status to `pipeline_*` before the step runs.
// Reload so the card moves into the correct pipeline column immediately.
console.log('[Board] Pipeline step started, reloading features...');
logger.info('Pipeline step started, reloading features...');
loadFeatures();
} else if (event.type === 'auto_mode_error') {
// Reload features when an error occurs (feature moved to waiting_approval)
console.log('[Board] Feature error, reloading features...', event.error);
logger.info('Feature error, reloading features...', event.error);
// Remove from running tasks so it moves to the correct column
if (event.featureId) {

View File

@@ -2,6 +2,9 @@ import { useCallback } from 'react';
import { Feature } from '@/store/app-store';
import { getElectronAPI } from '@/lib/electron';
import { useAppStore } from '@/store/app-store';
import { createLogger } from '@automaker/utils/logger';
const logger = createLogger('BoardPersistence');
interface UseBoardPersistenceProps {
currentProject: { path: string; id: string } | null;
@@ -18,7 +21,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
try {
const api = getElectronAPI();
if (!api.features) {
console.error('[BoardView] Features API not available');
logger.error('Features API not available');
return;
}
@@ -27,7 +30,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
updateFeature(result.feature.id, result.feature);
}
} catch (error) {
console.error('Failed to persist feature update:', error);
logger.error('Failed to persist feature update:', error);
}
},
[currentProject, updateFeature]
@@ -41,7 +44,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
try {
const api = getElectronAPI();
if (!api.features) {
console.error('[BoardView] Features API not available');
logger.error('Features API not available');
return;
}
@@ -50,7 +53,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
updateFeature(result.feature.id, result.feature);
}
} catch (error) {
console.error('Failed to persist feature creation:', error);
logger.error('Failed to persist feature creation:', error);
}
},
[currentProject, updateFeature]
@@ -64,13 +67,13 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
try {
const api = getElectronAPI();
if (!api.features) {
console.error('[BoardView] Features API not available');
logger.error('Features API not available');
return;
}
await api.features.delete(currentProject.path, featureId);
} catch (error) {
console.error('Failed to persist feature deletion:', error);
logger.error('Failed to persist feature deletion:', error);
}
},
[currentProject]