Winston - Logging in JavaScript & Node.js applications

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to learn about the Winston logging package for nodejs applications and this is a versatile logging package that can be used in multiple scenarios and different Frameworks in nodejs in this video we're going to dive into the core functionalities provided by Winston including things like log levels transports which determine where you can send your log files to we'll look at formatting options which determine what the output log entries look like and we'll also look at how we can use Winston to perform basic code profiling and finally we'll see how we can perform log centralization at the end of this video where we send our logs to a central service such as better stack for further analysis so we've got a lot to cover let's dive in and get started now we're on the GitHub page for Winston here as it says in the about section this is a logger for just about everything so a rather bold claim there let's go down to the readme and we're going to explore some of the functionality and features that Winston has so we're going to set this up in a second but let's have a quick look at the motivation Winston is designed to be a simple and Universal logging library with support for multiple transports and a transport is essentially a storage device for your logs and each Winston logger can have multiple transports now we'll see more about transports later but they basically determine where you're going to send your logs and that could be a file it could be the console it could be some Central service such as better stack or even a relational database or mongod DB now the point that each Winston logger can have multiple transports we're going to see an example of that in this video and one example is you might want your error logs to be stored in a persistent remote location such as a database whereas all other logs for example info and debug logs you might want to Output those levels to the console or to some local file on your file system now we're going to look at the usage section Below in a second and we're going to set up a project with Winston before we do that it would be amazing if you would like this video And subscribe to the channel if you're finding this content useful by subscribing you're going to help us reach more viewers and that would be greatly appreciated let's move on we're going to open VSS code and I'm going to set up a nodejs project so we have a terminal at the bottom we can run the npm in it- y command in order to actually set up this project and that will create a package.json file on the left hand side and then once we've done that we can install the Winston package npm install Winston with that command it's going to add Winston to the package.json file and it's going toing Winston and all of its dependencies down and store them in the node modules directory so we have Winston installed and what we're going to do now is we're going to work in this file here it's called app.js This is a Javascript file and we're going to Define some code here for logging let's go back to the GitHub page and we're going to go down here a little bit and we're going to go to the table of contents and this page is linked below the video if you need to get access to this we're going to go to the section on loging which is here and this is going to allow us to create a simple logger in Winston and then we can extend that example now you can see in this JavaScript object the levels that are defined in the Winston package and these are the default levels but you can actually change those under the hood and what we have are the levels ordered by their severity so the most severe level we have is the error log level and we also have log levels such as warn info HTTP verbose debug and of course C and each level has a number associated with it and the lower the number the more severe the level is we can take advantage of these levels and we're going to see that in a second let's now go down to this section on creating our own logger what we need to do is we need to use a function on Winston called create logger and that function you can pass configuration to that as a JavaScript object and one example of that is a list of transports that you want to be applied to this logger and the logger accepts the following options as part of its configuration we have things like the level and the transports we're going to going to see these now if we go to vs code so what I'm going to do at the top here is I'm going to import Winston so let's create a variable called Winston and we'll use the require function and we're going to import Winston we can then go down and create another variable called logger by using that winston. creat loger function and that as we saw in GitHub takes a JavaScript object of configuration we can pass a default level and we're going to set that just to debug as an example we can also pass a format for the log output and we're going to use one of the format in the winston. format object and the one we're going to use is the Json format and finally we're going to pass some transports into this logger and that's going to be a JavaScript array and the first one is going to be for the console so I'm going to use the new statement and it's going to be a new winston. transport. console object so the console object is going to Output the logs to the console basically standard output or standard error and then below that I'm going to copy this statement down and we're going to use the fail transport which obviously is going to Output the logs to a file so we can specify the name of that file by adding another configuration object and passing the file name and I'm going to use let's just say app. log for this example so what we have here is a logger object in Winston with a level of debug with an output format of Json and with two transports configured one going to the console and the other to a file called app. log so now that we have the logger we can use that and we can use the log level methods on that object let's see an example of that now we can use the logger doino method and that is going to Output a log entry with the level of info and that's going to take a message so let's pass a generic message for now and just say an info log and I'm going to copy that line just below here and we're going to use another logger method another level and that's going to be logger do error and we'll change the message there to an error log now let's run this app.js file so what I'm going to do is use the node command and we're going to run app.js and we can see the output from those two log level methods we have the first one which is an info level and we get the message for that one which just said an info log and we get the same for our error log as well so we can take advantage of these methods that are defined on the logger object in order to Output logs to whatever transports we have configured now a logger has a level associated with it and by default that's going to be info and that means that any Logs with a severity that are less severe than info are not going to be out put they're going to be discarded so let's see an example of what that means I'm going to change this level of debug to warn and this means the only levels of war and above are going to be output so for the two functions we have here we're not going to see the logger do info call because info is not as severe as the level we have configured on this logger object so that's not going to show up on the terminal or in the fail whereas the error message will so let's see an example now of that going to clear the terminal and we can see we only get the error message and if you look at the file on the left hand side the app. log file was created because we have a file transport with that file name so if we look at the contents of that file we can see our logs are also being sent to this file via that file transport and we can actually Define a different level on the transports themselves so I'm going to change this back to info but let's say this app. log file we might want to only output the error messages to this file the file transport can take a level parameter as well and we can set that to error so I'm going to clear the app. log file and we're going to see what kind of output we get in that and also in the terminal so let's rerun app.js on the console here we can see both of the logs from these functions whereas if we go to app. log because we've set the level on the transport we only get the error output so we've seen how to use multiple transports here on our logger and Winston comes with many transports many Community written transports I'm going to go to this better stack article and there's a link to this article in the description we have a section on custom transports in Winston and you can see some of these Community transports one for mongod DB one for CIS log Telegram and of course the better stack transport as well which we'll see later in the video and a transport for MySQL too and there are many more in addition to these if you're interested in videos on any of those just let us know in the comments so let's go back to VSS code we've seen how to define a logger and how to use the logger methods for each level we're now going to look at how to format the logs Now the default format just contains the level and the message that was sent into the method and that is output in Json format and we've set the format to Json here but that's actually the default in Winston so it outputs in a structured Json format but we can change this as well and there are different formats and different Utilities in the winston. format module we're going to use another one now just to demonstrate this and that's the CLI format and this is useful for command line applications so this is going to work well with the console but we're going to comment out the file transport at the moment and we're going to run this and see how the output changes so let's run app.js and you can see here we now have some colorization of the log level as well as the message being printed out as well now this is more human readable I would say rather than an object with key value pairs we're just getting an output here that's useful for the console and for debugging however the Json format has other advantages it's a structured format and that means it's easy to extract information from that data that's output in Json format now winston. format this is actually an object and we can destructure some properties from that object at the top here so I'm going to do that just now I'm going to Define some variables here and let's get the combined function and the time stamp function and finally the print F function we're going to destructure these from winston. format so we can now use these utilties ities in the format key that we have on our logger object so let's do that just now I'm going to remove the CLI function and we can use this combine function that we've imported and what we can pass to that are multiple options here so I'm going to use the time stamp and what that's going to do is add the Tim stamp to the log entries and we're also going to use the print F function which gives us a bit of control over the string output for that entry so let's do that just now we take the info that's passed in and we generate a string from from that so we can reference some variables here so we're going to use a JavaScript template string in order to do that so let's start by referencing info. timestamp here so we're going to start with the timestamp when the log was generated and then after that we can reference info. level that's the log level for this particular output on the logger and finally we should also output the message so we can reference info. message and this is going to be the format of our output entries for this logo so let's try that out on the terminal if we run node app.js we get a new output format we have the Tim stamp then we have the log level and finally the log message and that corresponds to what we've passed into this print F function and we have the Tim stamp object here which adds that time stamp to the logs if we comment that out this is not going to work as you see in the terminal for the time stamp we now have undefined so we need that timestamp function to be called as part of this combine function that we're passing to format let's now move on and we're going to see how we can add custom entries into our log output what I'm going to do here for now is remove print F and we're going to use the Json format again so I'm going to remove this print F function and we can just use Json and let's also uncomment the file transport that we have now what we want to do is we want to see custom key value pairs appearing in this output and it turns out this is very easy to do in Winston if we go down to our level methods here all we need to do or one way to do it rather is to pass a JavaScript object of key value pairs into these functions so what we're going to do is we're going to Define a variable here a JavaScript object called request log and let's imagine we were outputting some information from an HTTP request let's define that object now and we're going to give it a couple of keys the first one is for the HTTP method I'm going to set that to get just as part of this example and also let's add another key here called is authenticated and that is a bullan fi let's set that to false for this example now let's say we want to Output these keys and values as part of the Json output when we call these logger methods we can pass the object into these methods just as another parameter here and I can do the same for loger do error if we now run this on the terminal what we're going to get back is a JavaScript object and you can see the keys of is authenticated as well as the method here they have been added to the log entries and their output on each transport that's configured so we're also going to see these in the app. log file you can see that here with as authenticated so it's very easy to add these custom key value pairs into your log entries and if this is a common practice to pass the same key value pairs into multiple logger functions in different places in your applications what you can do is create a child logger in order to consolidate that in a single place so let's do that just now what we have at the moment is this request log object but we don't want to have to pass that request log to every single one of these methods so what we're going to do is create a child logger and reference that instead of the logger that we've created in order to do that we can take this Winston logger and we can call thechild method so let's do that just below this request log object and we're going to call that child logger and we're going to use the logger that we created above and call do child on that and we can pass in any keys and values that we want to be added by default when we use this child logger so let's pass the the request log object that we created above and we're going to change the references here from the parent logger to the child logger for both of these functions so let's now go back to the terminal and run node app.js I'm going to make this bigger so we can see this and you can see we still get back those new key value pairs like is authenticated so these are in the output even though we haven't passed the object itself to the methods and that's because we're now using this child logger now this looks a little bit messy on the terminal so I'm going to show one example from the format module in order to clean this up on the console here so let's go back to the file here and go to the top we're going to import another object another function from the format module and that's the pretty print function and once we've imported that we can just pass it into this combine method and call it and that's going to apply some pretty printing to the output Json file so when we rerun this we can see we get back a much cleaner format in the console so let me make this bigger and you can see we get back actual JavaScript objects with indentation so it looks a lot better than it did without the pretty print and again this is very useful for human readability but it's not so important if you're sending your logs to some aggregation service it just depends on the context of what you're doing so we've seen how to add custom keys and values to our log outputs and also how to consolidate those in a central place using a child logger let's now move on and see how we can log information about errors in our Winston outputs now when errors occur in your application it's very good to be able to get log output for those errors to help you identify what's gone wrong and in Winston what you need to do at the top here is you need to import the errors format that's from the winston. format module we have a function called errors and because this is from the format module we can use it within the format key that we're passing to the create logger statement so let's use the errors function and we can pass an object of configuration into the errors format and I'm going to pass a key called stack and set that to true and that's going to Output the stack Trace when you have an exception in your application so let's see what effect this has if we go down to the log methods here we have a child logger do error method we can pass a new error into that so let's create a new error here and we're going to pass the message 504 Gateway timeout just as a demonstration so let's save that and go to the terminal and rerun the app.js file and you can see this time in the error output here we have a stack Trace so if you need information about the error that's occurred in your application you can use this errors format that we've imported and added to the format option very simple to do using Winston JS let's now move on again and we're going to see how we can use multiple loggers in an application now it's common practice when you are building apps that you have multiple loggers at different services and different boundaries in your application and these loggers might have different requirements different s it levels and we're going to see a small example of how we might approach that now so what I'm going to do is create a new file here I'm going to call it loggers do JS and we're going to create multiple loggers in this file using the winston. loggers doad method so let's start by going to app.js and I'm going to copy the Imports that we have into loggers dogs and then we can define a logger here with Winston do loggers and then that has an add function and I'm going to create a logger here now let's say we're creating a loger for a payment application we have an order service and we have a payment service so we may have separate loggers for each of these so I'm going to create a logger here called order logger and this here this string is just a name that we're giving to the logger that we're going to reference later on the second parameter here are the options that Define that logger and for these options I'm going to go back to app.js and the logger that we have here I'm going to copy this entire object of configuration that we have I'm going to cut that out of of there and we're going to paste it into this file here so the order logger is going to have similar configuration to the logger that we've been working with so far now I'm going to change a couple of things here so let's look at the file transport I'm going to Output the logs for this to a file called orders. log and I'm going to remove the level here let's say we don't need that for the order logger and I'm going to copy this statement at the top we're going to add a second logger now for the payment service so we'll call this one payment logger and we can pass an object into that to configure that particular loger so for the format what I'm going to do is just specify that we want Jason for this loger and we've imported Jason at the top here from winston. format so we can just call it like this and let's now Define some transports I'm only going to add one transport for the payment logger and I'm going to copy the file transport that we have from the order service and we're going to paste that in here and change the file name to payments. log and that's the basic configuration for these two log loggers I'm going to add one last key to both and that's a key that we can use called default meta so any custom key value pairs that we want by default to appear on log outputs we can add them with an object here that we specify with the default meta key so we can add a service key here and that can be anything you want but we're calling this service and we're going to say that this one is the order logger service and I'm going to copy this down to the payment logger and we can specify that default metadata here as well and set that equal to in this case the payment logger and actually I'm going to change these it shouldn't be loggers it should be services so the default metadata now says order service and payment service for that key so we now have two loggers here what we can do in app.js is import them at the top here and we no longer need these options from the format module but above the Winston import what I'm going to do is just use the require statement and we're going to require that loggers dogs file and that will import the those loggers and we can remove this create logger statement we don't need that anymore what we're going to do is we're going to fetch these loggers from the other module so I'm actually going to remove all of this code and we're going to start from scratch and I'm going to create a function now called payment logger and we can get that payment logger by using Winston and again we access that loggers property and you can see the methods on that we have an add method that we used in the other file what we're going to do in order to retrieve the logger is use the do get method and we pass the name of that logger into that so this one is going to be called payment logger and that matches what we had as the string parameter to the ad method so we're fetching that logger now and we're calling the variable payment logger I'm going to copy that line of code just below and we're going to create one for the order logger as well and we need to change the name that's being referenced here to order logger and once we have these we can use the log level methods on them for example payment logger doino and let's give this a generic meth message payment received and we can also use the order logger as well so order logger do error and let's pass some message here Order failed let's now run this file and see what kind of output we get here and we get back the output for the order service and we can see the time stamp the level and the message and we can also see this service key and that matches the key that we added to the logger so if we go down here we can see that the order service here it has a default meta and we set that as the default meta object now we only see the order output because that particular logger is the only one that's outputting to the console if you look at the payment logger that only outputs to a file called payments. log it doesn't have a transport for the console now let's go to the left hand side here and you can see the files have been created we have an orders. log file and we also have a payments. log file as well and the output for the payments is not prettified it doesn't use that pretty print function it's just raw Json being output into the payment m. log file but again you can see that default meta for the service so we have that key available because of that property that we added let's now move on to log profiling now Winston allows you to do some basic profiling of your applications and what that means when you profile a block of code you're basically measuring the time that it takes for that code to run and to successfully execute let's go back to our app.js file here and we're going to add some example here that's going to demonstrate how to do this profiling so let's start by removing these two log level methods and I'm going to create a function here and let's just imagine that this was handling some kind of HTTP request on the server and it's going to take a path and we have a call back function for the logic for that particular path now let's say that when the request comes in we want to do some profiling we want to see how long is it going to take to process this request and we're going to use the payment logger for this but you can do this with any Winston logger what you do is you create a variable and I'm going to call that variable profiler and we can use the payment logger and we can call the dot start timer method in order to get back a profiler so this internally is going to be counting the time from when you call this start timer function until the time when you tell the profiler to stop calculating that time Delta so in this request Handler we're going to simulate some sort of processing here so I'm going to paste some code in we have a variable called 1 billion and we are creating a for Loop here where we are running through the numbers between 0 and 1 billion and multiplying each one of them by two so that's not very useful but it's going to simulate a request it's going to simulate some latency and then once we're done with that particular bit of compute we can call the profiler DOD method and the done method can take a JavaScript object so I'm going to pass a key here called message and we can pass a message in here and I'm actually going to use a template string and pass this message request to the path processed and the path is what coming into the function as a parameter so that is going to cancel this timer when we call the profiler DOD method so the last thing to do is actually just call the request Handler function so let's go down below the definition and we're going to call it and because this is using the payment logger let's just pass a URL path here of/ payment we can then save this and I'm going to execute this on the terminal so let's run this and we're not getting any output on the terminal we need to go to the payments. log file and you can see the new key that we have in this log so we're outputting the duration in milliseconds between calling the start timer function here and then calling profiler DOD when that processing is finished that will output to your logs the duration in milliseconds along with the level and the message and the message is just what we pass in here to the profiler DOD function and that's all we need to know here in order to do some very basic profiling with Winston so let's finish this video by showing how we can send our logs to a centralized service and we're going to send them to better stack where we can effectively search and analyze the logs and find interesting incidents that are happening in our applications now in order to do that we need to install a new transport we've already seen this concept of transports but we have a community-driven transport for better stack and I'm going to go to this page and the better stack documentation here this will be linked below the video and we can see how we can use the Winston transport for better stack now to start with we need to install these packages so I'm going to copy this and we're going to go back to vs code and I'm going to paste that npm install command in here and that's going to install these into our nodejs project now once that's installed we're going to go to the browser and we're going to create a new source and we're on the better stack log service here and a source basically represents a service where you want to collect logs from so let's create a new one at the top right with the connect source button and I'm going to call this one Winston demo and that platform for this is going to be a JavaScript nodejs application then we can click the create source button and you can see the source has been created and what we get back here is some options we have a source token and that's important we're going to need to copy that so I'm going to click this and that will copy that to the clipboard and we also have some other options here for example data retention which is set to 3 Days by default on this plan now let's go back to VSS code and I'm going to go to loggers dopy and right at the top here I'm going to create a variable called token and I'm going to set that equal to the Token that I've copied now in production you definitely don't want to do this you don't want a hardcoded token in your application what you can do instead is read this from process.env as an example if it's coming from an environment file but for the purposes of this video I'm just going to hardcode this for demonstration let's now go back to the better stack documentation and we can look at the setup section here for the Winston transport we need to have a couple of imports at the top here so I'm going to copy these and let's go back to vs code and at the top of loggers dop we're going to bring those in once we've done that we need to create a log tail object and we can do that here by instantiating it and passing the token in so I'm going to copy this line of code and just underneath the token we can paste that in there and we can replace this Source token here with the variable that we created on the line above so we now have a log toil client what we need to do is then create the transport so let's go back to the documentation and you can see that we can create a new log toil transport and pass that log toil client in so I'm going to copy that statement and go back to vs code and I'm going to go down to both of the loggers that we have here these have a transports option that's set to a JavaScript array so we can pass the log t transport as the final option here and we can do the same in the payment logger as well so as well as the file transport we can specify a second transport and that's the log tail transport once we've added that we can go back to app.js now the request Handler that function is going to do that profiling and it's going to Output some logs to the payment logger what I want to do as well is add an order logger statement so order logger doino and let's pass a message in here and just say that an order was placed now if we save this and we go back to our terminal and we rerun the node app.js command this is going to run the script that we have and hopefully we're going to see that it's going to send the logs to better stack so let's go back to the platform here and we can look at the live tail of these logs and hopefully we're going to see these and we do so the better stack Winston transport has sent this information to the platform and we can see the log entries being collected in this live tail now we can dive in and see more information about each entry for example the request to the/ payment endpoint if we expand this we can see some of the details including the duration in milliseconds that was added by that profiler and of course we can also see the level and the message and and other information and what's useful about this is that we can go to the search bar at the top here and we can Define some kind of lookup that we want to perform over our entire logs for example we know we have a key called duration in milliseconds and better stack knows that that key exists and it's indexed that so we can search very efficiently over the logs to find entries that contain that key now we don't need to specify and equality we can also do something like a greater than lookup and if we wanted to find all requests that were taking greater than 500 milliseconds in other words a request that's not performing as efficiently as we might expect we can specify greater than 500 and then we can search through the logs to find those and we can see we get this back because this particular log took 974 milliseconds so if we change this search option at the top here to 1,000 and we tried it again we don't get any matching logs for that one so we can use these searches to find interesting information from the logs that have been collected and we can use that information to build charts and dashboards we can set up alerts and do General analysis of our logs very easily when it's in a centralized location such as this so that's all for this video if you've enjoyed the video please give it a thumbs up on YouTube and subscribe to the better stack Channel and if you're interested in more of these types of videos for example how to use Winston in a next GS application or how to do log rotation and other topics like that let us know in the comments thanks again for watching and we'll see you in the next video
Info
Channel: Better Stack
Views: 3,792
Rating: undefined out of 5
Keywords:
Id: YjEqmINAQpI
Channel Id: undefined
Length: 30min 27sec (1827 seconds)
Published: Sat Dec 30 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.