Hapi good logging depends on the enviroment - node.js

Is there a way to enable / disable
depends on the enviroment in good ?
For instance I'd like to run this
only when NODE_ENV is equal to develepement
var goodOptions = {
opsInterval: 1000,
reporters: [{
reporter: require('good-console'),
events: {log: '*', response: '*' , error: '*' , request: '*'}
}]
};
server.register({
register: require('good'),
options: goodOptions
},
function(err) {
if (err) {
throw err;
}
}
);
no ternary operator :)

You could just add:
if (process.env.NODE_ENV !== 'development') {
goodOptions.reporters = [];
}
If you start getting into complex configuration then you should take a look at hapijs/confidence which is a really powerful configuration tool. It would be overkill just for this though.

Related

Can't send log from Hapi-Pino to Elasticsearch

I'm building server for a website use hapi and hapi-pino for logging.
I want to analyze log (about status code, route, timestamp) for some business purpose. I use elasticsearch and kibana to do it
Between hapi-pino and Elasticsearch, I try to use pino-elasticsearch to send log. However, it didn't work, elasticsearch didn't get anything
This is my code for registering hapi-pino:
const streamToElastic = PinoElasticsearch({
index: 'api',
type: 'log',
consistency: 'one',
node: 'http://elastic:changeme#localhost:9200',
'es-version': 6,
'bulk-size': 200,
ecs: true
});
await server.register({
plugin: HapiPino,
options: {
logPayload: true,
prettyPrint: process.env.NODE_ENV !== 'production',
redact: {
paths: ['req.headers', 'payload.user.password', 'payload.file'],
remove: true
},
stream: streamToElastic
}
});
Thanks for any helps and sorry about my poor English!

How to use express-winston and winston-mongodb together?

I'm using express-winston and winston-mongodb to log express requests to mongodb.
express-winston config:
expressWinston.logger({
meta: true,
//...other unrelated config
})
winston-mongodb config:
new MongoDB({
//...other unrelated config
})
Logging to mongodb works, but meta field is null. Logging to file/console works perfectly.
The mongo plugin doesn't recognize the express plugin's log format.
How can I get them to play nice?
When you look at the express-winston and winston-mongodb codes, you can easily see the difference.
winston-mongodb writes the value that matches the metaKey field to the specified collection.
Therefore, if you define it as follows, the meta field will not be null.
...
transports: [
new winston.transports.Console({
format: winston.format.json({
space: 2
})
}),
new winston.transports.MongoDB({
db: config.db.mongooseURI,
options: config.db.options,
collection:'logs',
capped:true,
metaKey:'meta'
}),
],
meta: true,
...
This is what finally worked for me: v6.12.1 Noticed later that while this works for db logging, the meta is empty for the file transport. Not sure why.
const winston = require('winston');
module.exports = function(err, req, res, next) {
winston.error(err.message, {metadata: { prop: err } });
res.status(500).send('Something failed.');
};
If you want to log to File and MongoDB then:
winston.add(
new winston.transports.MongoDB({
db: process.env.CONNECTIONSTRING,
options: { useUnifiedTopology: true },
metaKey: 'meta'
})
)
module.exports = function (err, req, res, next) {
// Log the exception
winston.error({message: err.message, level: err.level, stack: err.stack, meta: err})
res.status(500).send("Something failed..Cannot connect to Server");
};

Webpacking a NodeJS Express API with MySQL throws connection error in mode '-p', not in '-d'

I have a simple Express API where I use MySQL to retrieve my data. I use Webpack 4 to bundle it with a very simple configuration:
'use strict';
const path = require('path');
module.exports = {
entry: './src/main.js',
target: 'node',
output: {
filename: 'gept_api.js',
path: path.resolve(__dirname, 'dist'),
},
node: {
__dirname: true,
},
};
When I use webpack --config webpack.config.js -d for development everything works just fine.
However, when I run webpack --config webpack.config.js -p for production it suddenly doesn't work anymore, and throws an error when it's getting a connection from the pool.
TypeError: Cannot read property 'query' of undefined
at Object.getItem (C:\Users\freek\Dropbox\Code\Apps\GEPT\GEPTv2_API\dist\gept_api.js:1:154359)
at t.db_pool.getConnection (C:\Users\freek\Dropbox\Code\Apps\GEPT\GEPTv2_API\dist\gept_api.js:1:154841)
at c._callback (C:\Users\freek\Dropbox\Code\Apps\GEPT\GEPTv2_API\dist\gept_api.js:1:68269)
at c.end (C:\Users\freek\Dropbox\Code\Apps\GEPT\GEPTv2_API\dist\gept_api.js:1:8397)
at C:\Users\freek\Dropbox\Code\Apps\GEPT\GEPTv2_API\dist\gept_api.js:1:322509
at Array.forEach (<anonymous>)
at C:\Users\freek\Dropbox\Code\Apps\GEPT\GEPTv2_API\dist\gept_api.js:1:322487
at process._tickCallback (internal/process/next_tick.js:112:11)
So somehow this is broken by using the production mode in webpack 4. The connection object undefined somehow, while it isn't in development mode.
I have no idea how to fix this, since I'm a noob in using Webpack. I tried searching on google, but couldn't find anything relevant.
How I create my pool:
'use strict';
var mysql = require('mysql');
var secret = require('./db-secret');
module.exports = {
name: 'gept_api',
hostname: 'https://api.toxsickproductions.com/gept',
version: '1.3.0',
port: process.env.PORT || 1910,
db_pool: mysql.createPool({
host: secret.host,
port: secret.port,
user: secret.user,
password: secret.password,
database: secret.database,
ca: secret.ca,
}),
};
How I consume the connection:
pool.getConnection((err, connection) => {
PlayerRepository.getPlayer(req.params.username, connection, (statusCode, player) => {
connection.release();
res.status(statusCode);
res.send(player);
return next();
});
});
and
/** Get the player, and logs to HiscoreSearch if exists.
*
* Has callback with statusCode and player. Status code can be 200, 404 or 500.
* #param {string} username The player's username.
* #param {connection} connection The mysql connection object.
* #param {(statusCode: number, player: { username: string, playerType: string }) => void} callback Callback with statusCode and the player if found.
*/
function getPlayer(username, connection, callback) {
const query = 'SELECT p.*, pt.type FROM Player p JOIN PlayerType pt ON p.playerType = pt.id WHERE username = ?';
connection.query(query, [username.toLowerCase()], (outerError, results, fields) => {
if (outerError) callback(500);
else if (results && results.length > 0) {
logHiscoreSearch(results[0].id, connection, innerError => {
if (innerError) callback(500);
else callback(200, {
username: results[0].username,
playerType: results[0].type,
deIroned: results[0].deIroned,
dead: results[0].dead,
lastChecked: results[0].lastChecked,
});
});
} else callback(404);
});
}
I found what was causing the issue. Apparantly the mysql package relies on Function.prototype.name because setting keep_fnames: true fixed the production build. (https://github.com/mishoo/UglifyJS2/tree/harmony#mangle-options)
I disabled the Webpack 4 standard minification and used custom UglifyJSPlugin settings:
'use strict';
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: './src/main.js',
target: 'node',
output: {
filename: 'gept_api.js',
path: path.resolve(__dirname, 'dist'),
},
node: {
__dirname: true,
},
optimization: {
minimize: false,
},
plugins: [
new UglifyJsPlugin({
parallel: true,
uglifyOptions: {
ecma: 6,
mangle: {
keep_fnames: true,
},
},
}),
],
};

Hapijs getting started good good-console error reporter must specify events to filter on

I'm just starting to learn Hapijs
following getting started tutorial
with this example:
var Hapi = require('hapi');
var Good = require('good');
var server = new Hapi.Server();
server.connection({ port: 3000 });
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Hello, world!');
}
});
server.route({
method: 'GET',
path: '/{name}',
handler: function (request, reply) {
reply('Hello, ' + encodeURIComponent(request.params.name) + '!');
}
});
server.register({
register: Good,
options: {
reporters: [{
reporter: require('good-console'),
args:[{ log: '*', response: '*' }]
}]
}
}, function (err) {
if (err) {
throw err; // something bad happened loading the plugin
}
server.start(function () {
server.log('info', 'Server running at: ' + server.info.uri);
});
});
when I run
node server
I've got
/home/user/hapi/node_modules/good/node_modules/hoek/lib/index.js:683
throw new Error(msgs.join(' ') || 'Unknown error');
^
Error: reporter must specify events to filter on
Can you help me, please ?
The documentation is outdated. There were some breaking changes in good 6.0.0. The module good-console has a new version, however it is not published on npm yet. You can use the master branch though by specifying the GitHub repository in package.json:
"good-console": "hapijs/good-console"
You will also need to change the configuration to:
options: {
reporters: [{
reporter: require('good-console'),
events: {
response: '*',
log: '*'
}
}]
}
EDIT: Version 5.0.0 of good-console has been released. The documentation was also updated.

hapi-auth-cookie failing to load session strategy

There's not many examples out there for hapi and its auth-cookie plugin but here's what I have so far in an attempt to secure a route. Note, most of the examples I've seen are using an older version of hapi which doesn't seem to quite apply to this situation and im hoping im just missing something simple:
var Hapi = require('hapi');
var Mongoose = require('mongoose');
Mongoose.connect('mongodb://localhost/rfmproducetogo');
var server = new Hapi.Server(8080, "localhost");
server.pack.register([{
plugin: require("lout")
}, {
plugin: require('hapi-auth-cookie')
}, {
plugin: require("./plugins/togo")
}, {
plugin: require("./plugins/auth")
}], function(err) {
if (err) throw err;
server.auth.strategy('session', 'cookie', {
password: 'shhasecret',
cookie: 'wtfisthisfor',
isSecure: false,
redirectTo: false
});
server.start(function() {
console.log("hapi server started # " + server.info.uri);
});
});
And in my togo plugin I have this route setup to use the session
exports.create = function(plugin) {
plugin.route({
method: 'POST',
path: '/togo/add',
handler: function(request, reply) {
produce = new Produce();
produce.label = request.payload.label;
produce.price = request.payload.price;
produce.uom = request.payload.uom;
produce.category = request.payload.category;
produce.save(function(err) {
if (!err) {
reply(produce).created('/togo/' + produce._id);
} else {
reply(err);
}
});
},
config: {
auth: 'session'
}
});
};
The error im seeing is this:
/home/adam/Projects/bushhog/node_modules/hapi/node_modules/hoek/lib/index.js:421
throw new Error(msgs.join(' ') || 'Unknown error');
^
Error: Unknown authentication strategy: session in path: /togo/add
at Object.exports.assert (/home/adam/Projects/bushhog/node_modules/hapi/node_modules/hoek/lib/index.js:421:11)
at /home/adam/Projects/bushhog/node_modules/hapi/lib/auth.js:123:14
at Array.forEach (native)
at internals.Auth._setupRoute (/home/adam/Projects/bushhog/node_modules/hapi/lib/auth.js:121:24)
at new module.exports.internals.Route (/home/adam/Projects/bushhog/node_modules/hapi/lib/route.js:118:43)
at /home/adam/Projects/bushhog/node_modules/hapi/lib/router.js:110:25
at Array.forEach (native)
at /home/adam/Projects/bushhog/node_modules/hapi/lib/router.js:107:17
at Array.forEach (native)
at internals.Router.add (/home/adam/Projects/bushhog/node_modules/hapi/lib/router.js:104:13)
Running node 0.10.28, hapijs 6.x, hapi-auth-cookie 1.02
this issue occurs when you try to use an authentication strategy before it's actually available.
You're already following a good application setup by splitting the functionality into individual, small plugins with a given scope.
UPDATE: here's a dedicated tutorial for that problem, how to fix „unknown authentication strategy“
A good way to set up authentication and your plugins that rely on authentication is to create an extra "auth plugin" that adds your desired strategies and can be used as a dependency in your other plugins.
hapi auth plugin example
exports.register = function (server, options, next) {
// declare/register dependencies
server.register(require('hapi-auth-cookie'), err => {
/**
* Register authentication strategies to hapi server
*
* We’re using hapi-auth-cookie plugin to store user information on
* client side to remember user data on every website visit
*
* For sure, we could and will add more authentication strategies.
* What’s next: JWT (we highly welcome pull requests to add JWT functionality!)
*/
server.auth.strategy('session', 'cookie', {
password: 'ThisIsASecretPasswordThisIsASecretPassword',
cookie: 'hapi-rethink-dash',
redirectTo: '/login',
isSecure: false
});
server.log('info', 'Plugin registered: cookie authentication with strategy »session«')
next()
})
}
exports.register.attributes = {
name: 'authentication',
version: '1.0.0'
}
In your /plugins/togo you set the authentication plugin as a dependency (with server.dependency([array-of-deps])) which means hapi registers the auth plugin first and the depending ones afterwards.
You register your plugins like this:
server.register([{
plugin: require('./plugins/authentication')
}, {
plugin: require("./plugins/togo")
}], function(err) {
// handle callback
})
Check hapi-rethinkdb-dash for a detailed example.
Hope that helps!
keep in mind if you use server.dependency inside a plugin like Marcus Poehls did, you also need to register that dependency
server.register([{
plugin: require('hapi-auth-cookie')
},{
plugin: require('./plugins/authentication')
}, {
plugin: require("./plugins/togo")
}], function(err) {
// handle callback
})

Resources