In which file should I initialize objects in Express.js? - node.js

I'm new to Node.js and Express.
I want to use log4js but not certain about in which file I should configure my logger.
Is there a conventional file for the initialization? If not, where should I create a new configuration file?
Thanks :)
Answer (based on #jfriend00 answer)
In logger.js
'use strict';
var log4js = require('log4js');
log4js.configure({
"appenders": [...]
});
var logger = log4js.getLogger("structuredLogger");
module.exports = logger
In client.js
var logger = require('../../../../../config/logger.js');
logger.info('My message');
This module will allow me to:
Easily configure the the log4js
Easily replace the log4js with another package.

One common option for a module that needs to get initialized once is to create your own container module that does the initialization. Then, every other module that wants to use the logging can just load your container module and, if not already initialized, your container module will initialize the logging.
// mylog.js
// initialization code will only be called the first time the module is loaded
// after that, the module is cached by the `require()` infrastructure
var log4js = require('log4js');
log4js.configure({
appenders: [
{ type: 'console' },
{ type: 'file', filename: 'logs/cheese.log', category: 'cheese' }
]
});
module.exports = log4js;
Then, every module that wishes to use the common configuration logging can just do this near the top of the module:
var log4js = require('./mylog.js');

Related

How to share config folder between main app and library module?

This is my plan to setup myNodeApp, which has these folders:
config, src, node_modules/#myCompany/logging
in the library #myCompany/logging, i have code like this,
myWinston.js
const { createLogger } = require('winston');
const logger = createLogger({
level: config.get('logging').level, // this config is the one from myNodeApp
prettyPrint: true』);
module.exports = logger;
myLogger.js
const logger = require(./myWinston');
const warning = (myWarningMsg) => {
logger.warn(myWarningMsg);
}
module.exports = { warning };
Now in the main app code,
myApp.js
const { logger } = require('#myCompany/logging');
...
logger.warning('my warning msg here');
...
The problem is myWinston.js within the library #myCompany/logging, it needs a logging level from the main app.
What's the best way to pass this info from myNodeApp/config pls ?
Another idea see if it can work,
#myCompany/logging this lib has a config folder, can be used for testing within this lib.
When install #myCompany/logging for the myNodeApp, i can exclude config folder by using .npmignore. So logging will use the config folder from myNodeApp.
Comments pls ?
I think it is possible this way:
logger from npm modules can export constructor function:
// file in node_modules/#myCompany/logging/index.js
const { createLogger } = require('winston');
module.exports = exports = function(loggerConfig){
return createLogger({
level: loggerConfig.level,
prettyPrint: loggerConfig.prettyPring
});
};
and it can be used this way in index.js file, so config is provided properly
const config = require('./lib/config'); // all top level app config is loaded from some file in your project
config.logger.appName = 'web'; // you can customize global config here, for example, you have 2 components - web server and background process, and appName can depict it
const logger = require('#myCompany/logging')(config.logger); //here you instantiate logger with global config loaded
logger.info('Web application is preparing to start!');
//lot of code here

Can I add a second function export to module.exports without changing the way it's called?

I have a logging module that I use in many of my projects, which generally exports a single Winston logger, so all I did was define a logger and it's transports, then export it:
module.exports = logger;
when importing using const logger = require('mylogger.js') I then use the various levels built in (logger.info logger.debug etc).
I've now decided that I want to create a second logging function that will write logs to a different file, so I need to create and export a new transport. Thing is, if I switch to module.exports = {logger, mynewlogger}, that will change the way I import and call the functions, and I have that in many places.
Besides creating second file and importing both, is there any other way to add a second export without having to change my code everywhere else?
It's either new module that re-exports both:
logger-and-mynewlogger.js
module.exports = {logger, mynewlogger}
Or a separate module:
mynewlogger.js
module.exports = mynewlogger
Or using existing function as module object:
logger.mynewlogger = ...
module.exports = logger;
The first two options are preferable because they result in reasonably designed modules, while the last one is a quick and dirty fix.
Yes, you can define multiple transports for a single exported logger. When creating your Winston log, the 'transports' property is an array which allows you to define multiple outputs.
Here's an example of one I have that has two transports. Firstly, console and the second a daily rotating log.
const winston = require('winston');
const Rotate = require('winston-daily-rotate-file');
const tsFormat = () => (new Date()).toLocaleTimeString();
const logger = new (winston.Logger)({
transports: [
// colorize the output to the console
new (winston.transports.Console)({
timestamp: tsFormat,
colorize: true,
level: 'info',
}),
new (Rotate)({
filename: `${logDir}/${logName}-app.log`,
timestamp: tsFormat,
datePattern: 'YYYY-MM-DD',
prepend: true,
level: env === 'development' ? 'verbose' : 'info',
}),
],
});
module.exports = logger;

How Do I create a NodeJS Module?

I have read the details on NodeJS site : https://nodejs.org/api/modules.html. I don't understand how modules work, and what are the minimal steps for creating a module, and how npm can help me.
How can I create a module?
How do I use a module?
What does putting it on npm mean?
Note: this is a self answered question, with the purpose of sharing knowledge as a canonical.
You can create a NodeJS module using one line of code:
//mymodule.js
module.exports = 3;
Then you can load the module, by using require:
//app.js
require('./mymodule.js')
I added './' because it is a module of one file. We will cover it later.
Now if you do for example:
var mymodule = require('./mymodule.js');
console.log(mymodule); // 3
You can replace the number 3, with a function, for example:
//mymodule.js:
module.exports = function () {
console.log('function inside the module');
};
Then you can use it:
var mymodule = require('./mymodule.js');
mymodule();
Private variables:
Every variable you define inside A module will be defined only inside it:
//mymodule.js
var myPrivateVariable = 3;
publicVariable = 5; // Never user global variables in modules
//It's bad-pracrtice. Always add: var.
module.exports = function() {
// Every function of the module can use the private variables
return myPrivateVariable++
};
//app.js
var mymodule = require('./mymodule.js');
console.log(mymodule()); // return 3
console.log(mymodule()); // return 4
Reuse modules:
One more thing you need to know about NodeJS modules, is that if you use the same module twice(require it), it will return the same instance, it will not run in twice.
for example:
//app.js
var mymodule1 = require('./mymodule.js');
var mymodule2 = require('./mymodule.js');
console.log(mymodule1()); //return 3
console.log(mymodule2()); //return 4 (not 3)
console.log(mymodule1()); //return 5
As you see in the example below, that private variable is shared between all the instances of the module.
A module package
If your module contain more than one file, or you want to share the module with others, you have to create the module in separate folder, and create a package.json file for the module.
npm init will create package.json file for you.
For modules, there are 3 required parts:
package.json
{
"name" : "You module name",
"version" : "0.0.3"
}
Now, you can publish the module, using npm publish. I recommend you publish all your modules to github as well, then the module will be connected to your github page.
What you publish to NPM will be accessible by everyone. So never publish modules that contain private data. For that you can use private npm modules.
Next steps
Modules can return more than one function or one variable. See this samples in which we return an object.
module.exports.a = function() {
// ..
};
module.exports.b = function() {
// ..
};
// OR
myObj = {
a:3,
b:function() {
return this.a;
}
};
module.exports = myObj;
More info:
Read about package.json files
Versioning in you modules best practice
More best practive for NodeJS modules
Private modules, using private npm
Related Questions:
What is the purpose of Node.js module.exports and how do you use it?
module.exports vs exports in Node.js
Creating module in node.js is pretty simple!!!
You may consider module as a set of functionalities you can use in other code by simply just requiring it.
for eg:Consider a file functional.js having the content:
function display(){
console.log('i am in a display function');
}
module.exports = display;
Now just require it in any other module like:
var display = require('./functional');
display()
Output:i am in a display function
Similarly you can do:
var exports = module.exports = {};
exports.display = function(){
console.log('i am in the display function');
}
or you do the same for objects like:
var funObj = {
hello:function(){
console.log('hello function');
},
display:function(){
console.log('display function');
}
};
module.exports = funObj;
There are two main ways for wiring modules. One of them is using hard coded dependencies, explicitly loading one module into another using a require call. The other method is to use a dependency injection pattern, where we pass the components as a parameter or we have a global container (known as IoC, or Inversion of Control container), which centralizes the management of the modules.
We can allow Node.js to manage the modules life cycle by using hard coded module loading. It organizes your packages in an intuitive way, which makes understanding and debugging easy.
Dependency Injection is rarely used in a Node.js environment, although it is a useful concept. The DI pattern can result in an improved decoupling of the modules. Instead of explicitly defining dependencies for a module, they are received from the outside. Therefore they can be easily replaced with modules having the same interfaces.
Let’s see an example for DI modules using the factory pattern:
class Car {
constructor (options) {
this.engine = options.engine
}
start () {
this.engine.start()
}
}
function create (options) {
return new Car(options)
}
module.exports = create

Restify - Best practice for accessing logger from a module

I have looked for the answer to this for a while and just have not come up with a solution. I understand that I have access to the builtin logger from req.log.xxxxx(...), but what about a module that I have required into my controller? For example:
in my controller file, someController.js
var myModule = require('myModule');
SomeController.listUsers = function listUsers(req, res, next){
req.log.info('Some log message'); // <---- this works just fine
//...
}
In myModule.js:
module.exports = {
getUsers: function () {
// ...
// I would like to be able to log from here, but don't have access to the req object.
}
}
I don't really like the idea of passing the log object to the module's method, as that seems sloppy to me. If that's the only solution, then I'll live with it.
Restify uses bunyan to provide logging.
Looking through the documentation, the logger that's used for req.log is the one that's created at server startup (or at least a child of that logger). Since you can also create your own logger instance, I think that this should work:
// logger.js
var bunyan = require('bunyan');
module.exports = bunyan.createLogger(...);
// app.js
var server = restify.createServer({
log : require('./logger')
});
// someController.js
var logger = require('./logger');
...
This shares the same logger between the Restify server and other parts of your application. If you don't necessarily require that the logger is the same, you can also just create a new Bunyan instance in someController.js.
There aren't many options here. Typically I pass the "req" object. Another way around it is utilizing the "this" argument as a context. Something like this:
function listUsers(req, res, next) {
req.log.info('Some log message');
myModule.getUsers.call({log: req.log});
}
and...
module.exports = {
getUsers: function () {
(this.log || logger.log)() // ...
}
}
Usually, loggers get required into modules, implemented in the middleware, or inside a custom error object.
Another option you have in Events. Node.js is an Event driven language. You can just create a logger module that listens for log events. That's called neat programming.
However, I'd choose to require my logger into files, so that when we go into prod I have it deactivated/replaced with another more specific logger object with the exact interface.

How to set log level in Winston/Node.js

I am using Winston logging with my Node.js app and have defined a file transport. Throughout my code, I log using either logger.error, logger.warn, or logger.info.
My question is, how do I specify the log level? Is there a config file and value that I can set so that only the appropriate log messages are logged? For example, I'd like the log level to be "info" in my development environment but "error" in production.
If you are using the default logger, you can adjust the log levels like this:
const winston = require('winston');
// ...
winston.level = 'debug';
will set the log level to 'debug'. (Tested with winston 0.7.3, default logger is still around in 3.2.1).
However, the documentation recommends creating a new logger with the appropriate log levels and then using that logger:
const myLogger = winston.createLogger({
level: 'debug'
});
myLogger.debug('hello world');
If you are already using the default logger in your code base this may require you to replace all usages with this new logger that you are using:
const winston = require('winston');
// default logger
winston.log('debug', 'default logger being used');
// custom logger
myLogger.log('debug', 'custom logger being used');
Looks like there is a level option in the options passed covered here
From that doc:
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({ level: 'error' }),
new (winston.transports.File)({ filename: 'somefile.log' })
]
});
Now, those examples show passing level in the option object to the console transport. When you use a file transport, I believe you would pass an options object that not only contains the filepath but also the level.
That should lead to something like:
var logger = new (winston.Logger)({
transports: [
new (winston.transports.File)({ filename: 'somefile.log', level: 'error' })
]
});
Per that doc, note also that as of 2.0, it exposes a setLevel method to change at runtime. Look in the Using Log Levels section of that doc.
There are 6 default levels in winston: silly=0(lowest), debug=1, verbose=2, info=3, warn=4, error=5(highest)
While creating the logger transports, you can specify the log level like:
new (winston.transports.File)({ filename: 'somefile.log', level: 'warn' })
Above code will set log level to warn, which means silly, verbose and info will not be output to somefile.log, while warn, debug and error will.
You can also define your own levels:
var myCustomLevels = {
levels: {
foo: 0,
bar: 1,
baz: 2,
foobar: 3
}
};
var customLevelLogger = new (winston.Logger)({ levels: myCustomLevels.levels });
customLevelLogger.foobar('some foobar level-ed message');
Note that it's better to always include the 6 predefined levels in your own custom levels, in case somewhere used the predefined levels.
You can change the logging level in runtime by modifying the level property of the appropriate transport:
var log = new (winston.Logger)({
transports: [
new (winston.transports.Console)({ level : 'silly' })
]
});
...
// Only messages with level 'info' or higher will be logged after this.
log.transports.Console.level = 'info';
I guess, it works similarly for file but I haven't tried that.
If you want to change the log level on the fly. Like for when you need to trace production issue for short amount of time; then revert to error log level. You can use a dynamic logger provided you can expose a service on the web https://github.com/yannvr/Winston-dynamic-loglevel
apart from this you can cleanly achieve this by imlplementing runtime-node-refresh follow this link for more.

Resources