A service returns the wrong result and nobody knows why. You put a
console.log before the suspect line, restart, look. Not enough - you add a second. And a third. A quarter of an hour later you have a dozen printouts, a console full of noise, and still no idea where the value goes bad. Then half those printouts stay in the code forever.A legion's scout does not work blind. He has a ladder of tools: first he looks from the camp, then he sends a patrol, and when he must - he halts the march and examines the ground step by step. This lesson sets up such a ladder for your code.
Four rungs, from built-in to external:
--inspect flag, it lets you halt the program and look inside it.The order is not arbitrary: each rung costs more preparation, so you climb it only once the previous one has not sufficed.
NestJS has a built-in logger with five levels:
error, warn, log, verbose, debug. In development mode all of them are available - precisely so you can go deeper without adding code.You choose the levels when creating the application:
1// main.ts
2async function bootstrap() {
3 const app = await NestFactory.create(AppModule, {
4 logger: ['error', 'warn', 'log', 'verbose', 'debug'],
5 });
6
7 await app.listen(3000);
8}The array in the
logger option says which levels should be visible. Passing only ['error', 'warn'] silences the rest - and that is usually the production setting, because debug in production would flood the disks. In development you pass the full set.Note that the configuration goes in as the second argument to
, when the application is created. There is no method like NestFactory.create
app.setLogLevel() or Logger.setLevel() - the levels are fixed once, at startup.With the levels set, we replace printouts with a real logger:
1@Injectable()
2export class LegionsService {
3 private readonly logger = new Logger(LegionsService.name);
4
5 async findOne(id: number) {
6 this.logger.debug(`Looking for legion ${id}`);
7
8 const legion = await this.repo.findOne({ where: { id } });
9
10 if (!legion) {
11 this.logger.warn(`Legion ${id} does not exist`);
12 throw new NotFoundException();
13 }
14
15 return legion;
16 }
17}new Logger(LegionsService.name) gives the logger a context - the class name that will appear in every entry. Thanks to it you see in the console where a message comes from, without writing that into the text by hand.The advantage over
console.log is practical: debug entries disappear on their own once you switch to the production configuration. You do not have to remove them before deploying or remember where you left them - which is why I recommend it as a habit, @name: not printouts, but a logger at the right level.A logger shows what the program did. Sometimes you need to see what it is doing right now - and then you halt it at a chosen place.
The
--inspect flag serves that purpose. It enables the V8 debugging protocol and lets you attach to a running process:1node --inspect dist/main.jsThe path has four steps. Run the application with the
flag. Open Chrome DevTools at --inspect
chrome://inspect and attach to the process. Set a breakpoint in the code - the place where the program should halt. Analyse the variables and the call stack, the stack of calls telling you how the program got here.That fourth step is what
console.log cannot give: you see all the variables in scope, not just the ones you guessed and printed earlier. You can also step through the program and watch exactly where a value turns bad.The flag itself checks nothing and runs nothing - it only opens the door through which a debugger can look inside.
The VS Code Debugger uses the same protocol, only you place breakpoints by clicking beside a line number and inspect variables in the editor's panel. Nothing new underneath - a more comfortable handle on the same mechanism.
External APM solves a different problem: in production you cannot halt the application with a breakpoint, because it is serving real traffic. Tools like New Relic or Datadog collect data in the background - response times, exceptions, slow queries - and let you reach the cause after the fact, from what they gathered.
The scouting is done in order, not blind:
error, warn, log, verbose, debug - in development all of them are available,NestFactory.create(AppModule, { logger: [...] }) - there is no setLogLevel or Logger.setLevel,['error', 'warn'], development the full set,new Logger(ClassName.name) gives entries a context, and debug entries vanish by themselves once the configuration changes,--inspect flag enables the V8 debugging protocol and lets a debugger attach; it checks nothing and runs nothing,--inspect, open chrome://inspect, set a breakpoint, analyse variables and the call stack,In the next lesson we will look at what happens when an application shuts down - graceful shutdown, or striking camp without abandoning the wounded. For now remember: a logger tells you what happened; the inspector lets you halt the march and see what is happening now.