Skip to main content

Production Logging Guidelines

8 min read

8 min read Level: Intermediate For: Developer

TL;DR

Only log user-visible events and errors in production console. Never log tokens, credentials, user IDs, PII, or internal implementation details. Use environment-based logging to separate production-safe logs from verbose debug logs. Treat the console like a billboardβ€”only show what you'd want customers to see.

Production Logging Guidelines

Overview

This guide defines what logging is appropriate for production JavaScript console output to ensure security, privacy, and professionalism.


βœ… ALLOWED in Production Console

1. Application Lifecycle Events

// Good: High-level initialization
console.log('Surmado initialized');
console.log('βœ… Authentication ready');
console.log('βœ… Organization loaded');

2. User-Visible Actions (Success)

// Good: Confirmations of user actions
console.log('βœ… Report submitted successfully');
console.log('βœ… Brand created:', brandName);  // brandName is user-provided, not sensitive
console.log('βœ… Credits purchased: 12');

3. User-Facing Errors (Non-Sensitive)

// Good: Errors the user can see and act on
console.error('❌ Failed to load reports. Please refresh the page.');
console.error('❌ Insufficient credits. Please purchase more to continue.');
console.warn('⚠️ Network error. Retrying...');

4. Feature Flags / Environment Info (Public Only)

// Good: Non-sensitive configuration
console.log('Environment: production');
console.log('API Endpoint: https://api.surmado.com');
console.log('Debug mode: disabled');

5. Branded Messages

// Good: Professional branding
console.log('%c🌊 Surmado', 'font-size: 20px; font-weight: bold; color: #2C5F5D;');
console.log('Need help? Visit https://surmado.com/support');

❌ NEVER LOG in Production Console

1. Authentication Tokens / Credentials

// BAD - NEVER DO THIS
console.log('Token:', token);  // ❌ Security risk
console.log('API Key:', apiKey);  // ❌ Security risk
console.log('Bearer:', authHeader);  // ❌ Security risk
console.log('Session:', clerk.session);  // ❌ Contains sensitive data

2. User Identifiers / PII

// BAD - Privacy violation
console.log('User ID:', userId);  // ❌ PII
console.log('Organization ID:', orgId);  // ❌ PII
console.log('Email:', user.email);  // ❌ PII
console.log('Clerk User:', clerk.user);  // ❌ Contains PII
console.log('Organization:', clerk.organization);  // ❌ Contains PII

3. Internal Implementation Details

// BAD - Leaks architecture
console.log('Calling /.netlify/functions/submit-report');  // ❌ Internal paths
console.log('Database query:', query);  // ❌ Internal logic
console.log('Python API response:', fullResponse);  // ❌ May contain sensitive data
console.debug('Session state:', sessionObject);  // ❌ Internal state

4. Detailed Error Stack Traces

// BAD - Exposes code structure
console.error('Error:', error);  // ❌ Full stack trace
console.error('Failed:', error.stack);  // ❌ Code paths exposed

5. Verbose Debugging Info

// BAD - Too much noise
console.log('Entering function submitReport');  // ❌ Implementation detail
console.log('Variable x:', x, 'Variable y:', y);  // ❌ Internal state
console.debug('Loop iteration:', i);  // ❌ Verbose

6. Request/Response Payloads with Sensitive Data

// BAD - May contain sensitive info
console.log('Request body:', JSON.stringify(body));  // ❌ May contain tokens/PII
console.log('API response:', response);  // ❌ May contain sensitive data
console.log('Headers:', headers);  // ❌ May contain auth headers

🎯 Best Practices

Use Environment-Based Logging

const isDevelopment = window.location.hostname === 'localhost' ||
                      window.location.hostname.includes('netlify.app');

// Verbose logging only in development
if (isDevelopment) {
    console.debug('πŸ” Authenticating request...');
    console.debug('Session ready:', session);
}

// Always log user-visible events
console.log('βœ… Report submitted successfully');

Create a Logging Utility

// logger.js
const Logger = {
    // Always logs (production-safe)
    info: (message) => {
        console.log(`ℹ️ ${message}`);
    },

    success: (message) => {
        console.log(`βœ… ${message}`);
    },

    error: (message) => {
        // Only user-facing error message, no sensitive details
        console.error(`❌ ${message}`);
    },

    // Only logs in development
    debug: (message, data = null) => {
        if (isDevelopment) {
            console.debug(`πŸ” ${message}`, data);
        }
    }
};

// Usage
Logger.success('Brand created successfully');
Logger.debug('API response', response);  // Only in dev

Sanitize Before Logging

// Good: Redact sensitive fields
function sanitizeForLogging(obj) {
    const safe = { ...obj };

    // Remove sensitive fields
    delete safe.token;
    delete safe.apiKey;
    delete safe.userId;
    delete safe.orgId;
    delete safe.email;

    // Redact values that look like tokens
    Object.keys(safe).forEach(key => {
        if (typeof safe[key] === 'string' && safe[key].length > 50) {
            safe[key] = '[REDACTED]';
        }
    });

    return safe;
}

// Usage
console.log('Config:', sanitizeForLogging(config));

πŸ“‹ Production Logging Checklist

Before deploying to production, verify:

  • No API tokens, keys, or credentials logged
  • No user IDs, org IDs, or email addresses logged
  • No full error stack traces (only user-friendly messages)
  • No internal API endpoints or implementation details
  • No verbose debug logs (or wrapped in isDevelopment check)
  • No request/response bodies with sensitive data
  • Only user-visible events and errors logged
  • Branded/professional console messages
  • Error messages are actionable for users

πŸ” Audit Your Codebase

Search for these patterns and review each instance:

# Find potentially problematic logging
grep -r "console.log.*token" .
grep -r "console.log.*userId" .
grep -r "console.log.*orgId" .
grep -r "console.log.*email" .
grep -r "console.log.*clerk" .
grep -r "console.debug" .
grep -r "console.error.*error\)" .

πŸ“Š Example: Before & After

❌ Before (Development/Debug Logging)

console.log('πŸ” Authenticating request...');
console.log('User ID:', userId);
console.log('Org ID:', orgId);
console.log('Token:', token.substring(0, 20) + '...');
console.log('Calling API:', apiEndpoint);
console.log('Request headers:', headers);
console.log('Response:', response);

βœ… After (Production-Ready Logging)

// Only log high-level success
console.log('βœ… Authentication successful');

// Or use conditional logging
if (isDevelopment) {
    console.debug('πŸ” Auth flow completed');
    // Detailed logging only in dev
}

πŸš€ Implementation Steps

  1. Create js/utils/logger.js with environment-aware logging utility
  2. Replace all console.log/debug/error with Logger utility
  3. Audit existing logs using grep commands above
  4. Test in production mode locally (disable dev mode)
  5. Review console for any leaked sensitive data
  6. Deploy with confidence

πŸ”’ Security Note

Remember: The JavaScript console is public. Anyone can open DevTools and see console output. Treat it like you would a billboard - only show what you’d want customers, competitors, and security researchers to see.


πŸ“ž Questions?

If unsure whether something should be logged, ask:

  1. Would showing this to a stranger compromise security? β†’ Don’t log
  2. Does this contain user data or PII? β†’ Don’t log
  3. Is this only useful for internal debugging? β†’ Dev-only or don’t log
  4. Would a user find this helpful or confusing? β†’ Log if helpful

When in doubt, don’t log it.

Help Us Improve This Article

Know a better way to explain this? Have a real-world example or tip to share?

Contribute and earn credits:

  • Submit: Get $25 credit (Signal, Scan, or Solutions)
  • If accepted: Get an additional $25 credit ($50 total)
  • Plus: Byline credit on this article
Contribute to This Article