Why Is Your JavaScript Code Crying for Help, and You Can't Hear It?
\n\nImagine: you've launched a cool web application. Users come in, click around, everything works... or does it? In reality, somewhere deep in the browser, an error silently occurs: Uncaught TypeError: Cannot read property 'length' of undefined. The user simply closes the tab and goes to your competitors, and you don't even know something went wrong.
Welcome to the reality of frontend development. Without monitoring and logging JavaScript applications, you're working blind. But don't panic — today we'll break down how to turn the chaos of errors into a structured alert system. You'll learn how to properly log events, track performance, and not drown in a sea of data.
\n\nThis article is your survival guide for production. Let's go!
\n\nConsole.log — Your Friend or Foe?
\n\nLet's start with the basics. console.log() is the first thing that comes to every developer's mind. And that's normal. But if you're using it in production code, stop. Why? Because:
- \n
- It clutters the user's console (yes, some people open it). \n
- It impacts performance with a large number of calls. \n
- It doesn't provide structured information. \n
It's better to use logging levels. Here's an example of the right approach:
\n\n// Bad\nconsole.log('User clicked button');\n\n// Good\nconsole.info('User interaction', { \n action: 'click', \n element: '#submit-btn', \n timestamp: Date.now() \n});\n\n// For errors\nconsole.error('API request failed', { \n status: 500, \n endpoint: '/api/users' \n});\n\n// For warnings\nconsole.warn('Deprecated method used', { \n method: 'fetchData_v1' \n});\n\nBut even this is just the tip of the iceberg. Real JavaScript application monitoring begins where the console is unavailable.
\n\nStructured Logging: JSON to the Rescue
\n\nWhen your application grows, chaotic console messages turn into a mess. The solution is structured logging. Send data in JSON format so it can be parsed and analyzed.
\n\nHere's how to make a simple logger:
\n\nclass Logger {\n constructor(context = {}) {\n this.context = context;\n }\n\n log(level, message, data = {}) {\n const logEntry = {\n timestamp: new Date().toISOString(),\n level,\n message,\n ...this.context,\n ...data\n };\n\n // In production, send to server\n if (process.env.NODE_ENV === 'production') {\n this.sendToServer(logEntry);\n } else {\n // In development, output to console\n console[level](`[${level.toUpperCase()}] ${message}`, data);\n }\n }\n\n sendToServer(entry) {\n // Use sendBeacon for guaranteed delivery\n navigator.sendBeacon('/api/logs', JSON.stringify(entry));\n }\n\n info(message, data) { this.log('info', message, data); }\n warn(message, data) { this.log('warn', message, data); }\n error(message, data) { this.log('error', message, data); }\n}\n\n// Usage\nconst logger = new Logger({ appVersion: '1.2.3', userId: getUserId() });\nlogger.info('Page loaded', { url: window.location.href, loadTime: performance.now() });\n\nNote navigator.sendBeacon() — this is the best way to send data before the user leaves the page. Regular fetch requests can be cancelled.
Error Catching: try/catch is Just the Beginning
\n\nGlobal errors are the most insidious. They can occur anywhere and are easy to miss. Here's how to set up comprehensive JavaScript error monitoring:
\n\nGlobal Error Handler
\n\n// Catch unhandled errors
w