I need a basic logging system for a NodeJS application, so I'm doing some tests with log4js, which seems to be the standard for these cases. I need to print the messages both to the console and a file, so I wrote this code:
// Logger
var log4js = require('log4js');
log4js.configure({
appenders: {
'console': { type: 'console' },
'file': { type: 'file', filename: 'logs/mailer.log' }
},
categories: {
default: { appenders: ['file', 'console'], level: 'DEBUG' },
}
});
var logger = log4js.getLogger("Mailer");
logger.info("Starting application");
try {
// CODE TO READ APP CONFIG FILE
}
catch(e) {
logger.error("Couldn't read app configuration file (config.yml)");
// This is the trouble maker. It kills the app without flushing the logs
process.exit(1);
}
When I run the application, the logs appear like this:
[2019-07-29T16:07:24.763] [INFO] Mailer - Starting application
The problem is that I can see the messages in the console, but the log file remains empty. If I delete it and run the application again, it's created, so I suppose that the problem is a missing option or something like that.
Any suggestion?
To avoid changing the application logic and all the process.exit() calls, I've replaced the file appender by its synchronous version fileSync and now everything works as expected. So, the only thing I changed is the line where I declare the 'file' appender:
'file': { type: 'fileSync', filename: 'logs/mailer.log' }
Cheers!
You could use loggerWinston...I used it in my nodejs application and it works fine.
You should add it in your server.js (the file you use to start node)
const winston = require('winston');
const tsFormat = () => (new Date()).toLocaleTimeString();
global.loggerWinston = new (winston.Logger)({
transports: [
// colorize the output to the console
new (winston.transports.Console)({
timestamp: tsFormat,
colorize: true,
level: 'debug'
}),
new (winston.transports.File)({
filename: 'results.log',
timestamp: tsFormat,
// level: (process.env.NODE_ENV || 'development') === 'development' ? 'debug' : 'info'
level: 'info'
})
]
});
The results.log file should be added in your project folder.
This is the reference https://www.npmjs.com/package/winston
Related
I am trying to use Wallaby in conjunction with the dotenv-flow package. I currently have my wallaby.js config file setup like below:
require("dotenv-flow").config()
module.exports = function (wallaby) {
return {
files: [
'api/*',
'controllers/*',
'config/*',
'firebase/*',
'helpers/*',
'models/*',
'services/*',
'smtp/*',
'sockets/*'
],
tests: [
"test/**/*.test.mjs"
],
testFramework: "mocha",
env: {
type: "node",
params: {
env: "NODE_ENV=test"
}
}
};
};
I've tried a few other ways of writing the file including in esm module format. However, my tests run and my sequelize code complains that it wasn't passed environment variables to use for connecting to the development DB.
You are loading your .env file but then never using it's contents. Another problem is that wallaby doesn't understand the dotenv output so you have to massage it a little bit.
const environment = Object.entries(
require("dotenv-flow").config()['parsed']).
map( x => `${x[0]}=${x[1]}`).join(';'),
Then change your environment to something like this
env: {
runner: 'node',
params: {
env: environment
}
}
I'm currently running a node application with Pm2, but am struggling to get logs.
Currently, if I start the script with just node index.js my logger function (using winston) sends the logs to the screen just fine. The first two lines in the below video ("IS THIS SHOWING UP" and "YA") are from console.log statements inside my javascript files, and the latter lines are from my logger function. It looks like this:
However, in the deployment of my server, I'm using an ecosystem.config.js file with Pm2. When I run my server with pm2 start index.js, the logger also works. HOWEVER, when I start my server using my ecosystem file, the pm2 instance only sends the console.log statements to the output, not the logs output by the winston logger.
This means that I've got something misconfigured in my ecosystem.config.js file. What's going wrong?
package.json (script that kicks off the pm2 instance)
{
"name": "graphqlCourse",
...
"scripts": {
"prod:serve": "NODE_ENV=production pm2 startOrRestart ecosystem.config.js"
},
"dependencies": {
...
ecosystem.config.js
module.exports = {
apps: [
/// I believe that something is misconfigured in here relating to logging.....
{
name: process.env.APP_NAME,
args: ["--color"],
interpreter: process.env.NODE_PATH,
cwd: path.resolve(process.env.PROJECT_PATH, 'current' ),
script: 'dist/index.js',
instances: process.env.INSTANCES || 0,
exec_mode: "cluster",
env: {
...process.env,
},
},
],
deploy: {
production: {
user: "harrison",
host: process.env.HOST,
key: "~/.ssh/id_rsa2",
ref: "origin/master",
repo: process.env.GIT_REPO,
path: process.env.PROJECT_PATH,
"pre-deploy-local": `scp -Cr envs harrison#${process.env.HOST}:${process.env.PROJECT_PATH}/current`,
"post-deploy": `yarn install --ignore-engines && \
pwd && \
yarn prod:build && \
yarn prod:serve`,
},
},
};
-- EDIT --
Here's my logger as well:
import winston, { format } from "winston";
import "winston-daily-rotate-file"; // Attaches new transport to winston.transports
// Define the custom settings for each transport (file, console)
let consoleOptions = {
level: "info",
handleExceptions: true,
stderrLevels: ["error"],
silent: process.env.SILENT === "true",
format: format.combine(
format.colorize(),
format.align(),
format.printf((info) => {
const { level, message } = info;
return `[${level}]: ${message}`;
})
),
};
let consoleTransport = new winston.transports.Console(consoleOptions);
// Log rotation
const transport = new winston.transports.DailyRotateFile({
filename: `API_${process.env.NODE_ENV}.log`,
dirname: `./${
process.env.NODE_ENV === "production" ? "dist" : "server"
}/logs`,
frequency: null, // Rely on date pattern, rotate daily
datePattern: "YYYY-MM-DD",
zippedArchive: true,
maxSize: "10m",
maxFiles: "14d",
format: format.combine(format.timestamp(), format.json()),
});
transport.on("rotate", (oldFileName, newFilename) => {
console.log(`ROTATING LOGS. OLD: ${oldFileName} -- NEW: ${newFilename}`);
});
// Handles input from Morgan.
var writer = new winston.createLogger({
transports: [transport],
});
// Handles logger.XX calls from within app.
export const logger = new winston.createLogger({
transports: [consoleTransport, transport],
exitOnError: false, // do not exit on handled exceptions
});
// Recieves message from morganToWinston
logger.stream = {
write: function (message) {
writer.info(message);
},
};
I'm new to frontend world, I would like to write some test using protractor-image-comparison. I followed installation instructions from https://github.com/wswebcreation/protractor-image-comparison. Also I make configuration according to this page.
When I try to use functions form this lib I get following error: "TypeError: Cannot read property 'checkFullPageScreen' of undefined". I'm getting a warrning in protractor.conf.js in
const protractorImageComparison = require('protractor-image-comparison');
"Could not find a declaration file for module
'protractor-image-comparison'.
'/home/rafa/repos/example/src/example/node_modules/protractor-image-comparison/index.js'
implicitly has an 'any' type. Try npm install
#types/protractor-image-comparison if it exists or add a new
declaration (.d.ts) file containing declare module
'protractor-image-comparison';"
So I did, I made simple *.d.ts file with `declare module protractor-image-comparison' in it, but it didn't solve the problem just the warning disappear. It's propably the config issue, but I can't handle it or maybe I made wrong declaration. This is my config file :
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const reporter = require("cucumber-html-reporter");
const path = require("path");
const jsonReports = path.join(process.cwd(), "/reports/json");
const htmlReports = path.join(process.cwd(), "/reports/html");
const targetJson = jsonReports + "/cucumber_report.json";
const cucumberReporterOptions = {
jsonFile: targetJson,
output: htmlReports + "/cucumber_reporter.html",
reportSuiteAsScenarios: true,
theme: "bootstrap",
};
exports.config = {
allScriptsTimeout: 110000,
restartBrowserBetweenTests: true,
//SELENIUM_PROMISE_MANAGER: false,
specs: [
'./e2e/**/login.feature'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'custom',
frameworkPath: require.resolve('protractor-cucumber-framework'),
cucumberOpts: {
format: "json:" + targetJson,
require: ['./e2e/steps/*.ts', "./e2e/timeout.ts"],
},
useAllAngular2AppRoots: true,
onPrepare: () => {
browser.ignoreSynchronization = true;
const protractorImageComparison = require('protractor-image-comparison');
browser.protractorImageComparison = new protractorImageComparison(
{
baselineFolder: "report/screens/baseline",
screenshotPath: "report/screens/actual"
}
);
},
beforeLaunch: function() {
require('ts-node').register({
project: 'e2e'
});
},
onComplete: () => {
reporter.generate(cucumberReporterOptions);
}
};
Ok, I solved it. The reason why I was getting this TypeError is that I lunched few test scenarios and onPrepare was lunched only in the begining. I move config of protractor-image-comparison to cucumber befor hook and everything works fine now.
I'm using Webpack's [hash] for cache busting locale files. But I also need to hard-code the locale file path to load it from browser. Since the file path is altered with [hash], I need to inject this value to get right path.
I don't know how can get Webpack [hash] value programmatically in config so I can inject it using WebpackDefinePlugin.
module.exports = (env) => {
return {
entry: 'app/main.js',
output: {
filename: '[name].[hash].js'
}
...
plugins: [
new webpack.DefinePlugin({
HASH: ***???***
})
]
}
}
In case you want to dump the hash to a file and load it in your server's code, you can define the following plugin in your webpack.config.js:
const fs = require('fs');
class MetaInfoPlugin {
constructor(options) {
this.options = { filename: 'meta.json', ...options };
}
apply(compiler) {
compiler.hooks.done.tap(this.constructor.name, stats => {
const metaInfo = {
// add any other information if necessary
hash: stats.hash
};
const json = JSON.stringify(metaInfo);
return new Promise((resolve, reject) => {
fs.writeFile(this.options.filename, json, 'utf8', error => {
if (error) {
reject(error);
return;
}
resolve();
});
});
});
}
}
module.exports = {
// ... your webpack config ...
plugins: [
// ... other plugins ...
new MetaInfoPlugin({ filename: 'dist/meta.json' }),
]
};
Example content of the output meta.json file:
{"hash":"64347f3b32969e10d80c"}
I've just created a dumpmeta-webpack-plugin package for this plugin. So you might use it instead:
const { DumpMetaPlugin } = require('dumpmeta-webpack-plugin');
module.exports = {
...
plugins: [
...
new DumpMetaPlugin({
filename: 'dist/meta.json',
prepare: stats => ({
// add any other information you need to dump
hash: stats.hash,
})
}),
]
}
Please refer to the Webpack documentation for all available properties of the Stats object.
Seems like it should be a basic feature but apparently it's not that simple to do.
You can accomplish what you want by using wrapper-webpack-plugin.
plugins: [
new WrapperPlugin({
header: '(function (BUILD_HASH) {',
footer: function (fileName) {
const rx = /^.+?\.([a-z0-9]+)\.js$/;
const hash = fileName.match(rx)[1];
return `})('${hash}');`;
},
})
]
A bit hacky but it works — if u don't mind the entire chunk being wrapped in an anonymous function.
Alternatively you can just add var BUILD_HASH = ... in the header option, though it could cause problem if it becomes a global.
I created this plugin a while back, I'll try to update it so it provides the chunk hash naturally.
On server, you can get the hash by reading the filenames (example: web.bundle.f4771c44ee57573fabde.js) from your bundle folder.
You can pass the version to your build using webpack.DefinePlugin
If you have a package.json with a version, you can extract it like this:
const version = require("./package.json").version;
For example (we stringified the version):
new webpack.DefinePlugin({
'process.env.VERSION': JSON.stringify(version)
}),
then in your javascript, the version will be available as:
process.env.VERSION
The WebpackManifestPlugin is officially recommended in the output management guide. It writes a JSON to the output directory mapping the input filenames to the output filenames. Then you can inject those mapped values into your server template.
It's similar to Dmitry's answer, except Dmitry's doesn't appear to support multiple chunks.
That can be done with Webpack Stats Plugin. It gives you nice and neat output file with all the data you want. And it's easy to incorporate it to the webpack config files where needed.
E.g. To get hash generated by Webpack and use it elsewhere.
Could be achieved like:
# installation
npm install --save-dev webpack-stats-plugin
yarn add --dev webpack-stats-plugin
# generating stats file
const { StatsWriterPlugin } = require("webpack-stats-plugin")
module.exports = {
plugins: [
// Everything else **first**.
// Write out stats file to build directory.
new StatsWriterPlugin({
stats: {
all: false,
hash: true,
},
filename: "stats.json" // Default and goes straight to your output folder
})
]
}
# usage
const stats = require("YOUR_PATH_TO/stats.json");
console.log("Webpack's hash is - ", stats.hash);
More usage examples in their repo
Hope that helps!
I'm getting the following error when trying to build:
Building for production...Error: ENOENT: no such file or directory, stat '/Users/me/Code/project/index.html'
Package: "prerender-spa-plugin": "^3.1.0"
File: vue.config.js:
const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
const path = require('path');
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV !== 'production') return;
return {
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname),
routes: ['/'],
minify: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
decodeEntities: true,
keepClosingSlash: true,
sortAttributes: true,
},
renderer: new Renderer({
renderAfterDocumentEvent: 'render-event',
}),
}),
],
};
},
};
I don't have any routes, only a single index.html page.
Also, when I run yarn build and get that error, I try to kill the process in terminal but it keeps returning Building for production... without anything happening, and I have to quit terminal for it to stop.
Edit: I've also tried adding staticDir: path.join(__dirname, './public') but the build hangs without any errors.
Try adding headless: false to your renderer options. This will open a Chromium browser with your specified array of routes. Open the inspector in the Chromium browser and its very likely that you will see errors in the console.