SUpport logging to syslog and multiple processes

This commit is contained in:
Andris Reinman 2017-03-11 17:21:08 +02:00
parent ab2a0c83c2
commit d31f261918
7 changed files with 165 additions and 56 deletions

View file

@ -36,7 +36,7 @@ Yes, it does. You can run the server and get a working IMAP server for mail stor
### What are the killer features?
1. Start as many instances as you want. You can start multiple Wild Duck instances in different machines and as long as they share the same MongoDB and Redis settings, users can connect to any instances. This is very different from more traditional IMAP servers where a single user always needs to connect (or proxied) to the same IMAP server. Wild Duck keeps all required state information in MongoDB, so it does not matter which IMAP instance you use.
1. Start as many instances as you want. You can start multiple Wild Duck instances in different machines and as long as they share the same MongoDB and Redis settings, users can connect to any instances. This is very different from more traditional IMAP servers where a single user always needs to connect (or be proxied) to the same IMAP server. Wild Duck keeps all required state information in MongoDB, so it does not matter which IMAP instance you use.
2. Super easy to tweak. The entire codebase is pure JavaScript, so there's nothing to compile or anything platform specific. If you need to tweak something then change the code, restart the app and you're ready to go. If it works on one machine then most probably it works in every other machine as well.
3. Works almost on any OS including Windows. At least if you get MongoDB and Redis ([Windows fork](https://github.com/MSOpenTech/redis)) running first.
4. Focus on internationalization, ie. supporting email addresses with non-ascii characters

View file

@ -5,7 +5,21 @@ module.exports = {
level: 'silly'
},
// downgrade process user after binding to ports
//user: 'wildduck',
//group: 'wildduck',
// log to syslog if true
syslog: false,
// process title and syslog ident
ident: 'wildduck',
// how many processes to start
processes: 1,
mongo: 'mongodb://127.0.0.1:27017/wildduck',
redis: {
host: 'localhost',
port: 6379,

View file

@ -5,33 +5,6 @@ module.exports = {
level: 'silly'
},
mongo: 'mongodb://127.0.0.1:27017/wildduck',
redis: {
host: 'localhost',
port: 6379,
db: 3
},
imap: {
port: 9998,
host: '127.0.0.1'
},
lmtp: {
enabled: true,
port: 3424,
host: '0.0.0.0',
maxMB: 25
},
smtp: {
enabled: true,
port: 3525,
host: '0.0.0.0',
maxMB: 25
},
api: {
port: 8380
}
syslog: false,
processes: 5
};

40
logger.js Normal file
View file

@ -0,0 +1,40 @@
'use strict';
const config = require('config');
const log = require('npmlog');
let syslog;
try {
// might not be installed
syslog = require('modern-syslog'); // eslint-disable-line global-require
} catch (E) {
// just ignore
}
if (config.syslog && syslog) {
syslog.open(config.ident, syslog.option.LOG_PID, syslog.level.LOG_INFO);
let logger = data => {
data.messageRaw[0] = '(' + data.prefix + ') ' + data.messageRaw[0];
return data.messageRaw;
};
switch (log.level) {
/* eslint-disable no-fallthrough */
case 'silly':
log.on('log.silly', data => syslog.debug(...logger(data)));
case 'verbose':
log.on('log.verbose', data => syslog.info(...logger(data)));
case 'info':
log.on('log.info', data => syslog.notice(...logger(data)));
case 'http':
log.on('log.http', data => syslog.note(...logger(data)));
case 'warn':
log.on('log.warn', data => syslog.warn(...logger(data)));
case 'error':
log.on('log.error', data => syslog.error(...logger(data)));
/* eslint-enable no-fallthrough */
}
log.level = 'silent'; // disable normal log stream
}

View file

@ -42,5 +42,8 @@
"repository": {
"type": "git",
"url": "git://github.com/wildduck-email/wildduck.git"
},
"optionalDependencies": {
"modern-syslog": "^1.1.4"
}
}

View file

@ -1,36 +1,61 @@
/* eslint global-require:0 */
'use strict';
let config = require('config');
let log = require('npmlog');
let imap = require('./imap');
let lmtp = require('./lmtp');
let smtp = require('./smtp');
let api = require('./api');
let packageData = require('./package.json');
log.level = config.log.level;
require('./logger');
imap((err, imap) => {
if (err) {
log.error('App', 'Failed to start IMAP server');
return process.exit(1);
let printLogo = () => {
log.info('App', '.##...##..######..##......#####...#####...##..##...####...##..##.');
log.info('App', '.##...##....##....##......##..##..##..##..##..##..##..##..##.##..');
log.info('App', '.##.#.##....##....##......##..##..##..##..##..##..##......####...');
log.info('App', '.#######....##....##......##..##..##..##..##..##..##..##..##.##..');
log.info('App', '..##.##...######..######..#####...#####....####....####...##..##.');
log.info('App', ' --- v' + packageData.version + ' ---');
};
if (!config.processes || config.processes <= 1) {
printLogo();
if (config.ident) {
process.title = config.ident;
}
lmtp(imap, err => {
if (err) {
log.error('App', 'Failed to start LMTP server');
return process.exit(1);
// single process mode, do not fork anything
require('./worker.js');
} else {
let cluster = require('cluster');
if (cluster.isMaster) {
printLogo();
if (config.ident) {
process.title = config.ident + ' master';
}
smtp(imap, err => {
if (err) {
log.error('App', 'Failed to start SMTP server');
return process.exit(1);
}
api(imap, err => {
if (err) {
log.error('App', 'Failed to start API server');
return process.exit(1);
}
log.info('App', 'All servers started, ready to process some mail');
});
log.info('App', `Master [${process.pid}] is running`);
let forkWorker = () => {
let worker = cluster.fork();
log.info('App', `Forked worker ${worker.process.pid}`);
};
// Fork workers.
for (let i = 0; i < config.processes; i++) {
forkWorker();
}
cluster.on('exit', worker => {
log.info('App', `Worker ${worker.process.pid} died`);
setTimeout(forkWorker, 1000);
});
});
});
} else {
if (config.ident) {
process.title = config.ident + ' worker';
}
require('./worker.js');
}
}

54
worker.js Normal file
View file

@ -0,0 +1,54 @@
'use strict';
let config = require('config');
let log = require('npmlog');
let imap = require('./imap');
let lmtp = require('./lmtp');
let smtp = require('./smtp');
let api = require('./api');
imap((err, imap) => {
if (err) {
log.error('App', 'Failed to start IMAP server');
return process.exit(1);
}
lmtp(imap, err => {
if (err) {
log.error('App', 'Failed to start LMTP server');
return process.exit(1);
}
smtp(imap, err => {
if (err) {
log.error('App', 'Failed to start SMTP server');
return process.exit(1);
}
api(imap, err => {
if (err) {
log.error('App', 'Failed to start API server');
return process.exit(1);
}
log.info('App', 'All servers started, ready to process some mail');
// downgrade user if needed
if (config.group) {
try {
process.setgid(config.group);
log.info('App', 'Changed group to "%s" (%s)', config.group, process.getgid());
} catch (E) {
log.error('App', 'Failed to change group to "%s" (%s)', config.group, E.message);
return process.exit(1);
}
}
if (config.user) {
try {
process.setuid(config.user);
log.info('App', 'Changed user to "%s" (%s)', config.user, process.getuid());
} catch (E) {
log.error('App', 'Failed to change user to "%s" (%s)', config.user, E.message);
return process.exit(1);
}
}
});
});
});
});