mirror of
https://github.com/nodemailer/wildduck.git
synced 2024-12-27 02:10:52 +08:00
SUpport logging to syslog and multiple processes
This commit is contained in:
parent
ab2a0c83c2
commit
d31f261918
7 changed files with 165 additions and 56 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
40
logger.js
Normal 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
|
||||
}
|
|
@ -42,5 +42,8 @@
|
|||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/wildduck-email/wildduck.git"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"modern-syslog": "^1.1.4"
|
||||
}
|
||||
}
|
||||
|
|
77
server.js
77
server.js
|
@ -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
54
worker.js
Normal 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue