Compare commits

...

1 Commits

Author SHA1 Message Date
Matthew Raymer 6a622d20b8 feat: centralize debug logging with environment control 3 weeks ago
  1. 178
      docs/development/debug-logging.md
  2. 9
      electron/src/index.ts
  3. 3
      electron/src/preload.ts
  4. 2
      src/components/ContactInputForm.vue
  5. 3
      src/components/MembersList.vue
  6. 4
      src/db/databaseUtil.ts
  7. 10
      src/libs/util.ts
  8. 12
      src/registerSQLWorker.js
  9. 5
      src/services/PlatformServiceFactory.ts
  10. 8
      src/services/deepLinks.ts
  11. 11
      src/services/platforms/WebPlatformService.ts
  12. 3
      src/test/PlatformServiceMixinTest.vue
  13. 5
      src/utils/PlatformServiceMixin.ts
  14. 5
      src/utils/logger.ts
  15. 4
      src/views/DeepLinkErrorView.vue

178
docs/development/debug-logging.md

@ -0,0 +1,178 @@
# Debug Logging Control
## Overview
Debug logging in TimeSafari can be controlled via environment variables to reduce console noise during development and production.
## Current Behavior
By default, debug logging is **disabled** to reduce console noise. Debug logs are very verbose and include detailed information about:
- Camera operations (ImageMethodDialog, PhotoDialog)
- Database operations (CapacitorPlatformService)
- QR Scanner operations
- Platform service operations
- Component lifecycle events
## How to Enable Debug Logging
### Option 1: Environment Variable (Recommended)
Set the `VITE_DEBUG_LOGGING` environment variable to `true`:
```bash
# For development
VITE_DEBUG_LOGGING=true npm run dev
# For web builds
VITE_DEBUG_LOGGING=true npm run build:web:dev
# For Electron builds
VITE_DEBUG_LOGGING=true npm run build:electron:dev
```
### Option 2: .env File
Create or modify `.env.local` file:
```bash
# Enable debug logging
VITE_DEBUG_LOGGING=true
```
### Option 3: Package.json Scripts
Add debug variants to your package.json scripts:
```json
{
"scripts": {
"dev:debug": "VITE_DEBUG_LOGGING=true npm run dev",
"build:web:debug": "VITE_DEBUG_LOGGING=true npm run build:web:dev",
"build:electron:debug": "VITE_DEBUG_LOGGING=true npm run build:electron:dev"
}
}
```
## Debug Logging Rules
Debug logging follows these rules:
1. **Only shows in development mode** (not production)
2. **Only shows for web platform** (not Electron)
3. **Must be explicitly enabled** via `VITE_DEBUG_LOGGING=true`
4. **Never logged to database** (to reduce noise)
5. **Very verbose** - includes detailed component state and operations
## Components with Debug Logging
The following components include debug logging:
- **ImageMethodDialog.vue** - Camera operations, preview state
- **PhotoDialog.vue** - Camera operations, video setup
- **AmountInput.vue** - Input validation, increment/decrement
- **GiftedDialog.vue** - Amount updates, form state
- **CapacitorPlatformService.ts** - Database operations, migrations
- **QRScanner services** - Camera permissions, scanner state
- **PlatformServiceMixin.ts** - Service initialization
## Example Debug Output
When enabled, you'll see output like:
```
[ImageMethodDialog] open called
[ImageMethodDialog] Camera facing mode: user
[ImageMethodDialog] Should mirror video: true
[ImageMethodDialog] Platform capabilities: {isMobile: false, hasCamera: true}
[ImageMethodDialog] Starting camera preview from open()
[ImageMethodDialog] startCameraPreview called
[ImageMethodDialog] Current showCameraPreview state: true
[ImageMethodDialog] MediaDevices available: true
[ImageMethodDialog] getUserMedia constraints: {video: {facingMode: "user"}}
[ImageMethodDialog] Setting video element srcObject
[ImageMethodDialog] Video metadata loaded, starting playback
[ImageMethodDialog] Video element started playing successfully
```
## Disabling Debug Logging
To disable debug logging:
1. **Remove the environment variable:**
```bash
unset VITE_DEBUG_LOGGING
```
2. **Or set it to false:**
```bash
VITE_DEBUG_LOGGING=false npm run dev
```
3. **Or remove from .env file:**
```bash
# Comment out or remove this line
# VITE_DEBUG_LOGGING=true
```
## Production Behavior
In production builds, debug logging is **always disabled** regardless of the environment variable setting. This ensures:
- No debug output in production
- No performance impact from debug logging
- Clean console output for end users
## Troubleshooting
### Debug Logging Not Working
1. **Check environment variable:**
```bash
echo $VITE_DEBUG_LOGGING
```
2. **Verify it's set to "true":**
```bash
VITE_DEBUG_LOGGING=true npm run dev
```
3. **Check if you're in development mode:**
- Debug logging only works in development (`NODE_ENV !== "production"`)
- Production builds always disable debug logging
### Too Much Debug Output
If debug logging is too verbose:
1. **Disable it completely:**
```bash
unset VITE_DEBUG_LOGGING
```
2. **Or modify specific components** to use `logger.log` instead of `logger.debug`
3. **Or add conditional logging** in components:
```typescript
if (process.env.VITE_DEBUG_LOGGING === "true") {
logger.debug("Detailed debug info");
}
```
## Best Practices
1. **Use debug logging sparingly** - only for troubleshooting
2. **Disable in production** - debug logging is automatically disabled
3. **Use specific component prefixes** - makes it easier to filter output
4. **Consider log levels** - use `logger.log` for important info, `logger.debug` for verbose details
5. **Test without debug logging** - ensure your app works without debug output
## Future Improvements
Potential enhancements to the debug logging system:
1. **Component-specific debug flags** - enable debug for specific components only
2. **Log level filtering** - show only certain types of debug messages
3. **Debug UI panel** - in-app debug information display
4. **Structured logging** - JSON format for better parsing
5. **Performance monitoring** - track impact of debug logging

9
electron/src/index.ts

@ -14,7 +14,10 @@ import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher }
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
// Debug logging - only show when VITE_DEBUG_LOGGING is enabled
if (process.env.VITE_DEBUG_LOGGING === 'true') {
console.log('[Electron] Another instance is already running. Exiting immediately...');
}
process.exit(0);
}
@ -90,7 +93,10 @@ if (electronIsDev) {
// Handle second instance launch (focus existing window and show dialog)
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Debug logging - only show when VITE_DEBUG_LOGGING is enabled
if (process.env.VITE_DEBUG_LOGGING === 'true') {
console.log('[Electron] Second instance attempted to launch');
}
// Someone tried to run a second instance, we should focus our window instead
const mainWindow = myCapacitorApp.getMainWindow();
@ -163,7 +169,10 @@ ipcMain.handle('export-data-to-downloads', async (_event, fileName: string, data
// Write the file to the Downloads directory
await fs.writeFile(filePath, data, 'utf-8');
// Debug logging - only show when VITE_DEBUG_LOGGING is enabled
if (process.env.VITE_DEBUG_LOGGING === 'true') {
console.log(`[Electron Main] File exported successfully: ${filePath}`);
}
return {
success: true,

3
electron/src/preload.ts

@ -3,7 +3,10 @@ import { contextBridge, ipcRenderer } from 'electron';
require('./rt/electron-rt');
//////////////////////////////
// User Defined Preload scripts below
// Debug logging - only show when VITE_DEBUG_LOGGING is enabled
if (process.env.VITE_DEBUG_LOGGING === 'true') {
console.log('User Preload!');
}
/**
* Expose secure IPC APIs to the renderer process.

2
src/components/ContactInputForm.vue

@ -167,7 +167,7 @@ export default class ContactInputForm extends Vue {
*/
@Emit("qr-scan")
private handleQRScan(): void {
console.log("[ContactInputForm] QR scan button clicked");
// QR scan button clicked - event emitted to parent
}
}
</script>

3
src/components/MembersList.vue

@ -162,8 +162,6 @@
/* TODO: Human Testing Required - PlatformServiceMixin Migration */
// Priority: High | Migrated: 2025-07-06 | Author: Matthew Raymer
//
// TESTING NEEDED: Component migrated from legacy logConsoleAndDb to PlatformServiceMixin
// but requires human validation due to meeting component accessibility limitations.
//
// Test Scenarios Required:
// 1. Load members list with valid meeting password
@ -174,7 +172,6 @@
// 6. Cross-platform testing: web, mobile, desktop
//
// Reference: docs/migration-testing/migration-checklist-MembersList.md
// Migration Details: Replaced 3 logConsoleAndDb() calls with this.$logAndConsole()
// Validation: Passes lint checks and TypeScript compilation
// Navigation: Contacts Chair Icon Start/Join Meeting Members List

4
src/db/databaseUtil.ts

@ -273,8 +273,8 @@ export async function logToDb(
// Prevent infinite logging loops - if we're already trying to log to database,
// just log to console instead to break circular dependency
if (isLoggingToDatabase) {
// eslint-disable-next-line no-console
console.log(`[DB-PREVENTED-${level.toUpperCase()}] ${message}`);
// Use logger.debug for controlled debug output instead of direct console.log
logger.debug(`[DB-PREVENTED-${level.toUpperCase()}] ${message}`);
return;
}

10
src/libs/util.ts

@ -502,15 +502,7 @@ export function findAllVisibleToDids(
import * as R from 'ramda';
//import { findAllVisibleToDids } from './src/libs/util'; // doesn't work because other dependencies fail so gotta copy-and-paste function
console.log(R.equals(findAllVisibleToDids(null), {}));
console.log(R.equals(findAllVisibleToDids(9), {}));
console.log(R.equals(findAllVisibleToDids([]), {}));
console.log(R.equals(findAllVisibleToDids({}), {}));
console.log(R.equals(findAllVisibleToDids({ issuer: "abc" }), {}));
console.log(R.equals(findAllVisibleToDids({ issuerVisibleToDids: ["abc"] }), { ".issuer": ["abc"] }));
console.log(R.equals(findAllVisibleToDids([{ issuerVisibleToDids: ["abc"] }]), { "[0].issuer": ["abc"] }));
console.log(R.equals(findAllVisibleToDids(["xyz", { fluff: { issuerVisibleToDids: ["abc"] } }]), { "[1].fluff.issuer": ["abc"] }));
console.log(R.equals(findAllVisibleToDids(["xyz", { fluff: { issuerVisibleToDids: ["abc"] }, stuff: [ { did: "HIDDEN", agentDidVisibleToDids: ["def", "ghi"] } ] }]), { "[1].fluff.issuer": ["abc"], "[1].stuff[0].agentDid": ["def", "ghi"] }));
// Test/debug console.log statements removed - use logger.debug() if needed
*
**/

12
src/registerSQLWorker.js

@ -250,6 +250,12 @@ onerror = function (error) {
* Auto-initialize on worker startup (removed to prevent circular dependency)
* Initialization now happens on first database operation
*/
// Use console for critical startup message to avoid circular dependency
// eslint-disable-next-line no-console
console.log("[SQLWorker] Worker loaded, ready to receive messages");
// Use logger.debug for controlled debug output instead of direct console.log
// Note: This is a worker context, so we use a simple debug message
if (typeof self !== "undefined" && self.name) {
// Worker context - use simple debug output
self.postMessage({
type: "debug",
message: "[SQLWorker] Worker loaded, ready to receive messages",
});
}

5
src/services/PlatformServiceFactory.ts

@ -42,9 +42,8 @@ export class PlatformServiceFactory {
const platform = process.env.VITE_PLATFORM || "web";
if (!PlatformServiceFactory.creationLogged) {
// Use console for critical startup message to avoid circular dependency
// eslint-disable-next-line no-console
console.log(
// Use logger.debug for controlled debug output instead of direct console.log
logger.debug(
`[PlatformServiceFactory] Creating singleton instance for platform: ${platform}`,
);
PlatformServiceFactory.creationLogged = true;

8
src/services/deepLinks.ts

@ -174,7 +174,7 @@ export class DeepLinkHandler {
const validRoute = routeSchema.parse(path) as DeepLinkRoute;
routeName = ROUTE_MAP[validRoute].name;
} catch (error) {
console.error(`[DeepLink] Invalid route path: ${path}`);
logger.error(`[DeepLink] Invalid route path: ${path}`);
// Redirect to error page with information about the invalid link
await this.router.replace({
@ -201,7 +201,7 @@ export class DeepLinkHandler {
validatedQuery = await schema.parseAsync(query);
} catch (error) {
// For parameter validation errors, provide specific error feedback
console.error(
logger.error(
`[DeepLink] Invalid parameters for route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with params: ${JSON.stringify(params)} ... and query: ${JSON.stringify(query)}`,
);
await this.router.replace({
@ -226,7 +226,7 @@ export class DeepLinkHandler {
query: validatedQuery,
});
} catch (error) {
console.error(
logger.error(
`[DeepLink] Error routing to route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with validated params: ${JSON.stringify(validatedParams)} ... and validated query: ${JSON.stringify(validatedQuery)}`,
);
// For parameter validation errors, provide specific error feedback
@ -260,7 +260,7 @@ export class DeepLinkHandler {
await this.validateAndRoute(path, sanitizedParams, query);
} catch (error) {
const deepLinkError = error as DeepLinkError;
console.error(
logger.error(
`[DeepLink] Error (${deepLinkError.code}): ${deepLinkError.details}`,
);

11
src/services/platforms/WebPlatformService.ts

@ -97,9 +97,8 @@ export class WebPlatformService implements PlatformService {
}
} else {
// We're in a worker context - skip initBackend call
// Use console for critical startup message to avoid circular dependency
// eslint-disable-next-line no-console
console.log(
// Use logger.debug for controlled debug output instead of direct console.log
logger.debug(
"[WebPlatformService] Skipping initBackend call in worker context",
);
}
@ -693,11 +692,7 @@ export class WebPlatformService implements PlatformService {
const setClause = keys.map((key) => `${key} = ?`).join(", ");
const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
const params = [...keys.map((key) => settings[key]), did];
console.log(
"[WebPlatformService] updateDidSpecificSettings",
sql,
JSON.stringify(params, null, 2),
);
// Debug logging removed - use logger.debug() if needed
await this.dbExec(sql, params);
}

3
src/test/PlatformServiceMixinTest.vue

@ -86,6 +86,7 @@
import { Component, Vue } from "vue-facing-decorator";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { logger } from "@/utils/logger";
@Component({
mixins: [PlatformServiceMixin],
@ -267,7 +268,7 @@ This tests the complete save → retrieve cycle with actual database interaction
this.result = `User #0 settings test completed. isRegistered: ${accountSettings.isRegistered}`;
} catch (error) {
this.result = `Error testing User #0 settings: ${error}`;
console.error("Error testing User #0 settings:", error);
logger.error("Error testing User #0 settings:", error);
}
}
}

5
src/utils/PlatformServiceMixin.ts

@ -757,10 +757,7 @@ export const PlatformServiceMixin = {
// Merge with any provided defaults (these take highest precedence)
const finalSettings = { ...mergedSettings, ...defaults };
console.log(
"[PlatformServiceMixin] $accountSettings",
JSON.stringify(finalSettings, null, 2),
);
// Debug logging removed - use logger.debug() if needed
return finalSettings;
} catch (error) {
logger.error(

5
src/utils/logger.ts

@ -45,6 +45,7 @@ export function safeStringify(obj: unknown) {
// Determine if we should suppress verbose logging (for Electron)
const isElectron = process.env.VITE_PLATFORM === "electron";
const isProduction = process.env.NODE_ENV === "production";
const isDebugEnabled = process.env.VITE_DEBUG_LOGGING === "true";
// Track initialization state to prevent circular dependencies
let isInitializing = true;
@ -108,8 +109,8 @@ async function logToDatabase(
// Enhanced logger with self-contained database methods
export const logger = {
debug: (message: string, ...args: unknown[]) => {
// Debug logs are very verbose - only show in development mode for web
if (!isProduction && !isElectron) {
// Debug logs are very verbose - only show when explicitly enabled
if (isDebugEnabled && !isProduction && !isElectron) {
// eslint-disable-next-line no-console
console.debug(message, ...args);
}

4
src/views/DeepLinkErrorView.vue

@ -49,7 +49,6 @@ import {
VALID_DEEP_LINK_ROUTES,
deepLinkSchemas,
} from "../interfaces/deepLinks";
import { logConsoleAndDb } from "../db/databaseUtil";
import { logger } from "../utils/logger";
const route = useRoute();
@ -106,9 +105,8 @@ const reportIssue = () => {
// Log the error for analytics
onMounted(() => {
logConsoleAndDb(
logger.error(
`[DeepLinkError] Error page displayed for path: ${originalPath.value}, code: ${errorCode.value}, params: ${JSON.stringify(route.params)}, query: ${JSON.stringify(route.query)}`,
true,
);
});
</script>

Loading…
Cancel
Save