Skip to content

Commit 54b5393

Browse files
jsonifyclaudegemini-code-assist[bot]
authored
Add Noted output dropdown option (#128)
* feat: Add dedicated Noted output channel for debug logging - Created new LogService with output channel support - Supports multiple log levels: Debug, Info, Warning, Error - Added 'noted.debug' configuration setting to enable debug mode - Integrated logging into extension activation and key operations - Added 'Noted: Show Output Channel' command (noted.showOutput) - Logger appears in Output panel dropdown (View > Output > Noted) - Automatic disposal on extension deactivation - Example logging added to openDailyNote function This provides a centralized location for extension diagnostics and debug information. * Update src/services/logService.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update src/extension.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update src/services/logService.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: Claude <[email protected]> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 6f630ad commit 54b5393

File tree

3 files changed

+183
-4
lines changed

3 files changed

+183
-4
lines changed

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,11 @@
393393
"title": "Noted: View What's New",
394394
"icon": "$(info)"
395395
},
396+
{
397+
"command": "noted.showOutput",
398+
"title": "Noted: Show Output Channel",
399+
"icon": "$(output)"
400+
},
396401
{
397402
"command": "noted.showPreview",
398403
"title": "Noted: Open Preview (with Diagram Support)",
@@ -1156,6 +1161,11 @@
11561161
"default": "md",
11571162
"description": "File format for notes"
11581163
},
1164+
"noted.debug": {
1165+
"type": "boolean",
1166+
"default": false,
1167+
"description": "Enable debug logging to the 'Noted' output channel (View > Output > Noted)"
1168+
},
11591169
"noted.useTemplate": {
11601170
"type": "boolean",
11611171
"default": false,

src/extension.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import {
9393
handleEditBundle,
9494
handleDeleteBundle
9595
} from './commands/bundleCommands';
96+
import { logger, LogLevel } from './services/logService';
9697
import {
9798
handleCreatePromptTemplate,
9899
handleEditPromptTemplate,
@@ -119,9 +120,16 @@ import { FOLDER_PATTERNS, SPECIAL_FOLDERS, MONTH_NAMES, SUPPORTED_EXTENSIONS, ge
119120
import { showModalWarning, showModalInfo, StandardButtons, StandardDetails } from './utils/dialogHelpers';
120121

121122
export function activate(context: vscode.ExtensionContext) {
122-
console.log('Noted extension is now active');
123-
123+
// Initialize logger and add to subscriptions for proper disposal
124124
const config = vscode.workspace.getConfiguration('noted');
125+
const debugMode = config.get<boolean>('debug', false);
126+
if (debugMode) {
127+
logger.setMinLogLevel(LogLevel.Debug);
128+
}
129+
context.subscriptions.push(logger);
130+
131+
logger.info('Noted extension activated');
132+
logger.debug('Configuration loaded', { notesFolder: config.get('notesFolder') });
125133
const notesFolder = config.get<string>('notesFolder', 'Notes');
126134
const workspaceFolders = vscode.workspace.workspaceFolders;
127135

@@ -1747,6 +1755,11 @@ export function activate(context: vscode.ExtensionContext) {
17471755
await showChangelogView(context);
17481756
});
17491757

1758+
// Command to show the Noted output channel (for debugging)
1759+
let showOutput = vscode.commands.registerCommand('noted.showOutput', () => {
1760+
logger.show(false); // Show the output channel and focus it
1761+
});
1762+
17501763
// Command to show current notes folder configuration (for debugging)
17511764
let showNotesConfig = vscode.commands.registerCommand('noted.showConfig', async () => {
17521765
const config = vscode.workspace.getConfiguration('noted');
@@ -2204,7 +2217,7 @@ export function activate(context: vscode.ExtensionContext) {
22042217
createTemplateWithAI, enhanceTemplate, selectAIModel,
22052218
createBundle, createBundleFromTemplates, editBundle, deleteBundle,
22062219
migrateTemplates, createUserStoryWithAI, showTemplateBrowserCmd,
2207-
createFolder, moveNote, renameFolder, deleteFolder, showCalendar, showGraph, showActivity, showVersionChangelog,
2220+
createFolder, moveNote, renameFolder, deleteFolder, showCalendar, showGraph, showActivity, showVersionChangelog, showOutput,
22082221
togglePinNote, archiveNote, unarchiveNote, archiveOldNotes, rebuildBacklinks, clearBacklinks,
22092222
toggleSelectMode, toggleNoteSelection, selectAllNotes, clearSelection, bulkDelete, bulkMove, bulkArchive, bulkMerge,
22102223
createHierarchicalNote, openHierarchicalNote, searchHierarchicalNotes,
@@ -2632,6 +2645,7 @@ async function openDailyNote(templateType?: string) {
26322645
const notesPath = getNotesPath();
26332646
if (!notesPath) {
26342647
vscode.window.showErrorMessage('Please open a workspace folder first');
2648+
logger.warn('openDailyNote: No workspace folder');
26352649
return;
26362650
}
26372651

@@ -2651,6 +2665,8 @@ async function openDailyNote(templateType?: string) {
26512665
const noteFolder = path.join(notesPath, year, folderName);
26522666
const filePath = path.join(noteFolder, fileName);
26532667

2668+
logger.debug('openDailyNote', { fileName, templateType });
2669+
26542670
try {
26552671
await fsp.access(noteFolder);
26562672
} catch {
@@ -2660,10 +2676,12 @@ async function openDailyNote(templateType?: string) {
26602676
let isNewNote = false;
26612677
try {
26622678
await fsp.access(filePath);
2679+
logger.info('Opening existing daily note', { fileName });
26632680
} catch {
26642681
const content = await generateTemplate(templateType, now);
26652682
await fsp.writeFile(filePath, content);
26662683
isNewNote = true;
2684+
logger.info('Created new daily note', { fileName, templateType });
26672685
}
26682686

26692687
const document = await vscode.workspace.openTextDocument(filePath);
@@ -2679,6 +2697,7 @@ async function openDailyNote(templateType?: string) {
26792697
autoTagNewNote(filePath);
26802698
}
26812699
} catch (error) {
2700+
logger.error('Failed to open daily note', error instanceof Error ? error : new Error(String(error)));
26822701
vscode.window.showErrorMessage(`Failed to open daily note: ${error instanceof Error ? error.message : String(error)}`);
26832702
}
26842703
}
@@ -3289,4 +3308,6 @@ async function previewTemplate() {
32893308
`;
32903309
}
32913310

3292-
export function deactivate() {}
3311+
export function deactivate() {
3312+
logger.info('Noted extension deactivated');
3313+
}

src/services/logService.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import * as vscode from 'vscode';
2+
3+
/**
4+
* Log levels for the output channel
5+
*/
6+
export enum LogLevel {
7+
Debug = 'DEBUG',
8+
Info = 'INFO',
9+
Warning = 'WARN',
10+
Error = 'ERROR'
11+
}
12+
13+
/**
14+
* Service for logging messages to the Noted output channel
15+
* Provides a centralized logging mechanism visible in VS Code's Output panel
16+
*/
17+
export class LogService {
18+
private static instance: LogService;
19+
private outputChannel: vscode.OutputChannel;
20+
private minLogLevel: LogLevel = LogLevel.Info;
21+
22+
private constructor() {
23+
this.outputChannel = vscode.window.createOutputChannel('Noted');
24+
}
25+
26+
/**
27+
* Get the singleton instance of LogService
28+
*/
29+
public static getInstance(): LogService {
30+
if (!LogService.instance) {
31+
LogService.instance = new LogService();
32+
}
33+
return LogService.instance;
34+
}
35+
36+
/**
37+
* Set the minimum log level to display
38+
*/
39+
public setMinLogLevel(level: LogLevel): void {
40+
this.minLogLevel = level;
41+
}
42+
43+
/**
44+
* Get the current minimum log level
45+
*/
46+
public getMinLogLevel(): LogLevel {
47+
return this.minLogLevel;
48+
}
49+
50+
/**
51+
* Check if a log level should be logged based on current min level
52+
*/
53+
private shouldLog(level: LogLevel): boolean {
54+
const levelOrder: Record<LogLevel, number> = {
55+
[LogLevel.Debug]: 0, [LogLevel.Info]: 1,
56+
[LogLevel.Warning]: 2, [LogLevel.Error]: 3
57+
};
58+
return levelOrder[level] >= levelOrder[this.minLogLevel];
59+
}
60+
61+
/**
62+
* Format a log message with timestamp and level
63+
*/
64+
private formatMessage(level: LogLevel, message: string, data?: any): string {
65+
const timestamp = new Date().toISOString().slice(11, 23); // Extracts HH:mm:ss.sss from YYYY-MM-DDTHH:mm:ss.sssZ
66+
let formatted = `[${timestamp}] [${level}] ${message}`;
67+
68+
if (data !== undefined) {
69+
if (data instanceof Error) {
70+
formatted += `\n Error: ${data.message}\n Stack: ${data.stack}`;
71+
} else if (typeof data === 'object') {
72+
try {
73+
formatted += `\n ${JSON.stringify(data, null, 2)}`;
74+
} catch (e) {
75+
formatted += `\n [Unable to stringify object]`;
76+
}
77+
} else {
78+
formatted += `\n ${data}`;
79+
}
80+
}
81+
82+
return formatted;
83+
}
84+
85+
/**
86+
* Log a message to the output channel
87+
*/
88+
private log(level: LogLevel, message: string, data?: any): void {
89+
if (!this.shouldLog(level)) {
90+
return;
91+
}
92+
93+
const formatted = this.formatMessage(level, message, data);
94+
this.outputChannel.appendLine(formatted);
95+
}
96+
97+
/**
98+
* Log a debug message (lowest priority)
99+
*/
100+
public debug(message: string, data?: any): void {
101+
this.log(LogLevel.Debug, message, data);
102+
}
103+
104+
/**
105+
* Log an info message
106+
*/
107+
public info(message: string, data?: any): void {
108+
this.log(LogLevel.Info, message, data);
109+
}
110+
111+
/**
112+
* Log a warning message
113+
*/
114+
public warn(message: string, data?: any): void {
115+
this.log(LogLevel.Warning, message, data);
116+
}
117+
118+
/**
119+
* Log an error message (highest priority)
120+
*/
121+
public error(message: string, error?: Error | any): void {
122+
this.log(LogLevel.Error, message, error);
123+
}
124+
125+
/**
126+
* Show the output channel in the UI
127+
*/
128+
public show(preserveFocus: boolean = true): void {
129+
this.outputChannel.show(preserveFocus);
130+
}
131+
132+
/**
133+
* Clear all messages from the output channel
134+
*/
135+
public clear(): void {
136+
this.outputChannel.clear();
137+
}
138+
139+
/**
140+
* Dispose of the output channel
141+
*/
142+
public dispose(): void {
143+
this.outputChannel.dispose();
144+
}
145+
}
146+
147+
// Export a singleton instance for easy access
148+
export const logger = LogService.getInstance();

0 commit comments

Comments
 (0)