Console Logging Standards
Overview
Console logs in production code are now enforced at the ESLint level. All console.log() statements outside of test/story files must be wrapped in a dev check or use the devLog utility.
Pattern: Using devLog
The recommended approach for development logging is the devLog utility:
import { devLog } from '@/lib/devLog'
// All methods are available
devLog('Simple message')
devLog('Message:', { data })
devLog.warn('Warning message')
devLog.error('Error:', exception)
devLog.info('Info message')
devLog.debug('Debug info')
devLog.table(arrayOfData)Benefits:
- ✅ Automatically wrapped in
isDevelopmentcheck - ✅ Zero overhead in production (compiled away)
- ✅ Consistent logging interface across codebase
- ✅ Passes ESLint without configuration
Pattern: Manual Wrapping (if needed)
If you need to use raw console.log:
const isDevelopment = import.meta.env.DEV
if (isDevelopment) {
console.log('This only logs in development')
}When to use:
- Complex conditional logging logic
- Temporary debugging during specific workflow
- Generally prefer
devLoginstead
ESLint Configuration
Rules
| File Type | Rule | Behavior |
|---|---|---|
| Production code | no-console: error | ❌ Disallowed (must use devLog) |
| Test files | no-console: off | ✅ Allowed (for test debugging) |
| Story files | no-console: off | ✅ Allowed (for Storybook testing) |
| Utility/Logger files | no-console: off | ✅ Allowed (implementation files) |
Overrides
See .eslintrc.json for the full configuration. Key overrides:
{
"files": ["src/**/*.stories.{js,jsx}", "src/__tests__/**/*.{js,jsx}"],
"rules": { "no-console": "off" }
}Checking for Violations
Run the linter to find console statements:
npm run lintFind all console.log statements across codebase:
npm run lint:consoleThis will show:
- Which files have unwrapped console.log
- Line numbers and context
- Suggestions for fixing
Migration Path
Step 1: Add Import
import { devLog } from '@/lib/devLog'Step 2: Replace Usage
// Before
console.log('Loading venues:', venues)
// After
devLog('Loading venues:', venues)Step 3: Verify
npm run lintExamples by File Type
Service/Library File
// src/lib/venueService.js
import { devLog } from './devLog'
export async function getVenues(location) {
devLog('Fetching venues for:', location)
const venues = await fetchVenuesAPI(location)
devLog('Venues fetched:', venues)
return venues
}Component File
// src/components/VenueList.jsx
import { devLog } from '@/lib/devLog'
export default function VenueList({ venues }) {
useEffect(() => {
devLog('VenueList mounted with venues:', venues.length)
return () => {
devLog('VenueList unmounted')
}
}, [venues])
return (...)
}Story File (console allowed)
// src/components/VenueList.stories.jsx
export const Default = () => {
const handleClick = () => {
console.log('Story button clicked') // OK - stories allow console
}
return <VenueList onClick={handleClick} />
}Test File (console allowed)
// src/__tests__/venueService.test.js
describe('venueService', () => {
it('loads venues', () => {
console.log('Testing venue load') // OK - tests allow console
expect(getVenues()).toBeDefined()
})
})Benefits
- Production Ready: Console logs are eliminated in production builds
- Performance: Zero runtime overhead in production
- Consistency: Uniform logging interface across the codebase
- Maintainability: Easy to identify debug code
- Security: Prevents accidental leak of sensitive data via console
- ESLint Integration: Automated enforcement, no manual reviews needed
FAQ
Q: Why not just remove all console.logs?
A: Development debugging is essential. devLog keeps them without polluting production.
Q: Can I use devLog in browser DevTools?
A: Yes! In development mode, devLog calls console.log directly, so they appear in DevTools.
Q: What about console.warn and console.error?
A: ESLint allows these by default (they're considered safe). Use devLog.warn() and devLog.error() for consistency.
Q: Can story/test files use regular console?
A: Yes, ESLint overrides allow console in test/story files for debugging purposes.