Introduction: Why Your TypeScript Project Needs Monitoring and Logging
\n\nImagine this situation: you launched a cool service built with TypeScript. Everything works perfectly... until the first call from a user: "The site is slow!" or "Data isn't saving!". You dive into the code, but you can't reproduce the bug. Sound familiar?
\n\nTypeScript gives us powerful static typing, but it doesn't protect against logical errors, memory leaks, or unexpected runtime exceptions. This is where monitoring and logging come into play. It's not just a checkbox on a to-do list, but a real lifesaver for developers.
\n\nIn this article, we'll break down how to properly set up monitoring and logging in TypeScript applications (both on the backend with Node.js and on the frontend), which tools to choose, and how not to drown in a sea of logs. Let's go!
\n\nWhy Is This Needed? Three Main Reasons
\n\n1. Real-time Debugging
\nLogging allows you to look inside a running application. Instead of guessing, you see specific variable values, the call stack, and the sequence of events.
\n\n2. Performance
\nMonitoring TypeScript applications helps track down "heavy" functions that slow down the UI or consume memory. You'll see where the application spends the most time.
\n\n3. Security and Audit
\nLogs are the first thing you'll show during an incident investigation. Who requested what data and when? Proper logging can save your reputation.
\n\nTools for Monitoring and Logging in TypeScript
\n\nLet's look at what is actually used in production. The choice of tool depends on what you're building: a backend on Node.js or a frontend in the browser.
\n\nFor Backend (Node.js + TypeScript)
\n- \n
- Winston — the most popular logger. Flexible, with tons of transports (file, console, remote server). \n
- Pino — extremely fast if performance is critical. \n
- Morgan — for HTTP requests (works great with Express). \n
For Frontend (Browser TypeScript)
\n- \n
- Sentry — the king of error monitoring. Catches exceptions, crashes, and slow requests. \n
- LogRocket — records user sessions + logs. \n
- Datadog / New Relic — for enterprise solutions. \n
Setting Up Logging in TypeScript: A Step-by-Step Guide
\n\nLet's start with the classic backend. We'll install Winston — it works perfectly with TypeScript.
\n\nStep 1: Installation and Basic Configuration
\n\nnpm install winston\nnpm install @types/winston --save-dev\n\nCreate a file logger.ts:
import winston from 'winston';\n\nconst logger = winston.createLogger({\n level: 'info',\n format: winston.format.combine(\n winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),\n winston.format.errors({ stack: true }),\n winston.format.json()\n ),\n defaultMeta: { service: 'user-service' },\n transports: [\n new winston.transports.File({ filename: 'error.log', level: 'error' }),\n new winston.transports.File({ filename: 'combined.log' }),\n ],\n});\n\nif (process.env.NODE_ENV !== 'production') {\n logger.add(new winston.transports.Console({\n format: winston.format.simple(),\n }));\n}\n\nexport default logger;\n\nNow, anywhere in your TypeScript code, you can write:
\n\nimport logger from './logger';\n\nlogger.info('User successfully registered', { userId: 123 });\nlogger.error('Database connection error', { error: err.message });\n\nNote: we use winston.format.errors({ stack: true }). This means that when logging an error, the call stack will be saved — gold for debugging.