Digging Into Node.js

Console Log & Process stdout

Kyle Simpson

Kyle Simpson

You Don't Know JS
Digging Into Node.js

Check out a free preview of the full Digging Into Node.js course

The "Console Log & Process stdout" Lesson is part of the full, Digging Into Node.js course featured in this preview video. Here's what you'd learn in this lesson:

Kyle writes a Node program. First, questions are asked about how to get information into and out of the program. Then, logging to the console is compared against writing to standard output using "process".


Transcript from the "Console Log & Process stdout" Lesson

>> Kyle Simpson: When I when I want to write system automation scripts of any sort, command line scripts of any sort? My go-to is to write it in JavaScript and run it with Node. So that's where we're gonna our workshop today, is how do I run Node in that sort of enviroment?

And then we will progressively move to bigger and bigger things, like listening for web requests, and serving up files, and those other more exciting things. But we're gonna start there, and we're actually gonna start even deeper than that in the stack. So to dive in, I want to make sure you all have ex1.js from your exercises folder open.

And you should see that there is literally nothing but a code comment here. We're gonna write our very first Node program. If you're listening to this, and you've never written a Node program? We're about to fix that, cuz you're about to write your first Node program. So before we can even write a Node program, we have to ask ourselves the question.

How am I going to get information into my program, in some way? But even more importantly than that, how am I gonna get information out? And I said that Node is all about I/O, Node is all about modeling I/O in an efficient way. Which means we need to understand I/O at a deeper level than you're perhaps used to.

So how could I do the canonical hello-world of JavaScript programming, of Node programming? Well, you probably know in JavaScript, that we can do something like console.log, and you could type in hello World in a console.log. Before we even run this program, however, I want to ask the question, how is that gonna get printed out to the screen?

A lot of us just take that stuff for granted. But if you're going to write Node, and do so well, and use the system to its most advantage, and be most efficient with it? I think you need to understand something about how Node connects to the environment around it.

So we're gonna go even deeper than just an API, console.log. We need to understand a standard for I/O and system integration, system- level task calling, called POSIX, P-O-S-I-X. You Google around for that, you'll find a lot of information about that. I'm sure I'm not even gonna be completely accurate on it, because I'm not a Unix programmer from the 1970s.

But I can tell you that POSIX is essentially the way that C-style programs integrate with Linux-style operating systems. So when we talk about POSIX, one of the major things that we're talking about is the standard I/O subsystem. Standard I/O is a set of three streams that model input and output to a program.

If you've ever written a C program, for example, you might remember reading and writing from file descriptors like 0, 1, and 2. That's the POSIX subsystem being exposed to your C program by way of these file identifiers, or file descriptors. 0 being stdout, 1 being stdin, I'm sorry, 0 being stdin, 1 being stdout, and 2 being stderr.

So we have these file descriptors that reference input, output, and a second channel of output called error. And we're not writing C programs here, so it's not particularly interesting, how you do it in a C program. But how would we access, at a fundamental level, those I/O streams that are exposed to our Node program?.

Cuz Node made the choice to expose most of the system connections through a POSIX-like interface. So it'll be familiar to you, if you've ever done POSIX-style programming in some other language, you'll find bits of familiarity here. How do we access those, well, the main touchpoint that we're gonna access them on is called process.

This is an object that's available in all of your Node programs. And they hang a bunch of stuff off of process that are these connections to your overall hosting system. By the way, I keep saying this I/O thing because we have to deal with POSIX. You should be aware that the reason we have Node choosing to do it with POSIX, but we don't write something like process in the browser, is because JavaScript does not have anything in the spec for I/O.

console.log is not in the JavaScript spec. There's actually a secondary spec for console, but not everybody adheres to it. But it's not in the JavaScript spec, JavaScript is agnostic to I/O. And I actually think that's one of the reasons why JavaScript has been so successful. It's been so much easier for JavaScript to adapt to other environments, like smart watches and robots.

And light bulbs, and refrigerators, and TVs, and computers, and every other kind of device that you could imagine. Because the language doesn't make very many assumptions about how communication's gonna happen. It's up to the environment to decide that. Since Node was originally designed, and likely going to be run in those POSIX environments?

It makes an awful lot of sense that they would choose to expose the I/O to our programs through that POSIX interface. In the browser, that's not a POSIX environment, you're not dealing with processes, and process IDs, and that kind of stuff. So when you do console.log, and it's running in the browser, the browser chose to expose it in a different way.

It's just a direct connection over to the developer tools, right? And that's a difference based on the hosting environment exposing the I/O. So we're in Node here, and we've been exposed to POSIX interface, and we said there are standard I/O streams. So we can say process.stdout to access the stream for standard output.

And streams are a first-class citizen, they are an actual kind of data structure in Node that we're gonna get real familiar with, as we go throughout today. So the first exposure that we have to it is, if you want to write some content to a stream, you call .write.

So we could have said console.log, or we could have said process.stdout.write. Now, I'm gonna have both of these here, but let's first run the console.log statement, comment out the stdout for just a moment. Either one of these, they're gonna do something very similar, which is send some output.

Switch over to your console, and run node ex1.js, and you see the hello world being printed out. Now, let's go back to that code, and let's comment out the stdout.write version of it, and then rerun it. Okay, so we want to compare, what happened when I did console.log, and what happens when I do process.stdout.write?

And if we run this differently, you should notice a slight difference, which is that the process.stdout.write output did not include a trailing newline. Which is why it ended up right here, just continuing my command line prompt right after that, there was no trailing newline. So one way you might think of it is, you might think of it as console.log is a wrapper around process.stdout.write that throws on a trailing newline.

That’s not actually accurate, the console.log is doing other things, many other things besides simply calling process.stdout.write. But in the case of a very simple string, we could sort of think of it in that way. It's kind of the equivalent of if we'd put a trailing newline in our output, before sending it out.

The reason why I wanna make the point about accessing these output streams in a very bare way, is because the whole point of Node using this asynchronous I/O model is so that I/O can happen as efficiently as possible. And the least possible efficient way for I/O to happen is for you to print a character string directly to some stream.

So process.stdout.write, with a character string like that, is the least efficient way for that to possibly happen. Because it has to go through some layers of translation to end at the host environment, that is, our shell environment that we're running things in, and actually end up getting printed.

We don't see that detail, but there's layers of abstraction where that stuff is being converted or whatever. So if we were to, for example, have a stream that already had the binary bits of information for the hello world string in it, and we did .write with a binary stream, with a buffer?

Then it would go much more efficiently, directly through those subsystems, and then still get printed out as characters in our shell, okay? So console.log is a developer convenience, but under the covers, there's a lot more detail about how things are going. And as we get later into our exercises, we're gonna want to be able to efficiently write lots of information to a stream, even something like stdout.

And we're going to want to be doing so at the at the sort of binary buffer level, rather than in encoded character strings, okay? So that's our first kind of lesson here, is that we're dealing with standard output, that's kind of wrapped in a developer convenience called console.log

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now