refactor: replace happy_path_error__with_fallback with logger.happyPathError (#313)
- Removed all instances of happy_path_error__with_fallback from various hooks, services, and utilities. - Introduced logger.happyPathError for consistent logging of unexpected nulls and fallback values. - Updated the logger utility to include a new happyPathError method with enhanced context and stack trace. - Deprecated silent-debug utility as all logging functionality has been migrated to the logger.
This commit is contained in:
@@ -251,6 +251,58 @@ class Logger {
|
||||
timing(component: Component, message: string, durationMs: number, context?: LogContext): void {
|
||||
this.info(component, `⏱ ${message}`, context, { duration: `${durationMs}ms` });
|
||||
}
|
||||
|
||||
/**
|
||||
* Happy Path Error - logs when the expected "happy path" fails but we have a fallback
|
||||
*
|
||||
* Semantic meaning: "When the happy path fails, this is an error, but we have a fallback."
|
||||
*
|
||||
* Use for:
|
||||
* ✅ Unexpected null/undefined values that should theoretically never happen
|
||||
* ✅ Defensive coding where silent fallback is acceptable
|
||||
* ✅ Situations where you want to track unexpected nulls without breaking execution
|
||||
*
|
||||
* DO NOT use for:
|
||||
* ❌ Nullable fields with valid default behavior (use direct || defaults)
|
||||
* ❌ Critical validation failures (use logger.warn or throw Error)
|
||||
* ❌ Try-catch blocks where error is already logged (redundant)
|
||||
*
|
||||
* @param component - Component where error occurred
|
||||
* @param message - Error message describing what went wrong
|
||||
* @param context - Optional context (sessionId, correlationId, etc)
|
||||
* @param data - Optional data to include
|
||||
* @param fallback - Value to return (defaults to empty string)
|
||||
* @returns The fallback value
|
||||
*/
|
||||
happyPathError<T = string>(
|
||||
component: Component,
|
||||
message: string,
|
||||
context?: LogContext,
|
||||
data?: any,
|
||||
fallback: T = '' as T
|
||||
): T {
|
||||
// Capture stack trace to get caller location
|
||||
const stack = new Error().stack || '';
|
||||
const stackLines = stack.split('\n');
|
||||
// Line 0: "Error"
|
||||
// Line 1: "at happyPathError ..."
|
||||
// Line 2: "at <CALLER> ..." <- We want this one
|
||||
const callerLine = stackLines[2] || '';
|
||||
const callerMatch = callerLine.match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/);
|
||||
const location = callerMatch
|
||||
? `${callerMatch[1].split('/').pop()}:${callerMatch[2]}`
|
||||
: 'unknown';
|
||||
|
||||
// Log as a warning with location info
|
||||
const enhancedContext = {
|
||||
...context,
|
||||
location
|
||||
};
|
||||
|
||||
this.warn(component, `[HAPPY-PATH] ${message}`, enhancedContext, data);
|
||||
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
|
||||
@@ -1,33 +1,16 @@
|
||||
/**
|
||||
* Happy Path Error With Fallback
|
||||
*
|
||||
* Semantic meaning: "When the happy path fails, this is an error, but we have a fallback."
|
||||
* @deprecated This function is deprecated. Use logger.happyPathError() instead.
|
||||
* All usages have been migrated to the new logger system which consolidates logs
|
||||
* into the regular worker logs instead of separate silent.log files.
|
||||
*
|
||||
* Logs to ~/.claude-mem/silent.log and returns a fallback value.
|
||||
* Check logs with `npm run logs:silent`
|
||||
* Migration example:
|
||||
* OLD: happy_path_error__with_fallback('Missing value', { data }, 'default')
|
||||
* NEW: logger.happyPathError('COMPONENT', 'Missing value', undefined, { data }, 'default')
|
||||
*
|
||||
* Use happy_path_error__with_fallback for:
|
||||
* ✅ Unexpected null/undefined values that should theoretically never happen
|
||||
* ✅ Defensive coding where silent fallback is acceptable
|
||||
* ✅ Situations where you want to track unexpected nulls without breaking execution
|
||||
*
|
||||
* DO NOT use for:
|
||||
* ❌ Nullable fields with valid default behavior (use direct || defaults)
|
||||
* ❌ Critical validation failures (use logger.warn or throw Error)
|
||||
* ❌ Try-catch blocks where error is already logged (redundant)
|
||||
*
|
||||
* Good examples:
|
||||
* // Truly unexpected null (should never happen in theory)
|
||||
* const id = session.id || happy_path_error__with_fallback('session.id missing', { session });
|
||||
*
|
||||
* Bad examples (use direct defaults instead):
|
||||
* // Nullable field with valid empty default
|
||||
* const title = obs.title || happy_path_error__with_fallback('obs.title missing', { obs }, '(untitled)');
|
||||
* // BETTER: const title = obs.title || '(untitled)';
|
||||
*
|
||||
* // Array that can validly be undefined/null
|
||||
* const count = obs.files?.length ?? (happy_path_error__with_fallback('obs.files missing', { obs }), 0);
|
||||
* // BETTER: const count = obs.files?.length ?? 0;
|
||||
* See: src/utils/logger.ts for the new happyPathError method
|
||||
* Issue: #312 - Consolidate silent logs into regular worker logs
|
||||
*/
|
||||
|
||||
import { appendFileSync } from 'fs';
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* This keeps the worker service simple and follows one-way data stream.
|
||||
*/
|
||||
|
||||
import { happy_path_error__with_fallback } from './silent-debug.js';
|
||||
import { logger } from './logger.js';
|
||||
|
||||
/**
|
||||
* Maximum number of tags allowed in a single content block
|
||||
@@ -41,14 +41,14 @@ function countTags(content: string): number {
|
||||
*/
|
||||
export function stripMemoryTagsFromJson(content: string): string {
|
||||
if (typeof content !== 'string') {
|
||||
happy_path_error__with_fallback('[tag-stripping] received non-string for JSON context:', { type: typeof content });
|
||||
logger.happyPathError('SYSTEM', 'received non-string for JSON context', undefined, { type: typeof content }, '{}');
|
||||
return '{}'; // Safe default for JSON context
|
||||
}
|
||||
|
||||
// ReDoS protection: limit tag count before regex processing
|
||||
const tagCount = countTags(content);
|
||||
if (tagCount > MAX_TAG_COUNT) {
|
||||
happy_path_error__with_fallback('[tag-stripping] tag count exceeds limit, truncating:', {
|
||||
logger.warn('SYSTEM', 'tag count exceeds limit', undefined, {
|
||||
tagCount,
|
||||
maxAllowed: MAX_TAG_COUNT,
|
||||
contentLength: content.length
|
||||
@@ -73,14 +73,14 @@ export function stripMemoryTagsFromJson(content: string): string {
|
||||
*/
|
||||
export function stripMemoryTagsFromPrompt(content: string): string {
|
||||
if (typeof content !== 'string') {
|
||||
happy_path_error__with_fallback('[tag-stripping] received non-string for prompt context:', { type: typeof content });
|
||||
logger.happyPathError('SYSTEM', 'received non-string for prompt context', undefined, { type: typeof content }, '');
|
||||
return ''; // Safe default for prompt content
|
||||
}
|
||||
|
||||
// ReDoS protection: limit tag count before regex processing
|
||||
const tagCount = countTags(content);
|
||||
if (tagCount > MAX_TAG_COUNT) {
|
||||
happy_path_error__with_fallback('[tag-stripping] tag count exceeds limit, truncating:', {
|
||||
logger.warn('SYSTEM', 'tag count exceeds limit', undefined, {
|
||||
tagCount,
|
||||
maxAllowed: MAX_TAG_COUNT,
|
||||
contentLength: content.length
|
||||
|
||||
Reference in New Issue
Block a user