Skip to content

Commit

Permalink
Merge pull request #6720 from bevacqua/feature/bouncy-castle
Browse files Browse the repository at this point in the history
Support configuration reloads for logging
  • Loading branch information
bevacqua committed Jun 1, 2016
2 parents 76f349a + 59981ba commit ef1fb42
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 67 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@
"dragula": "3.7.0",
"elasticsearch": "10.1.2",
"elasticsearch-browser": "10.1.2",
"even-better": "7.0.2",
"expiry-js": "0.1.7",
"exports-loader": "0.6.2",
"expose-loader": "0.7.0",
"extract-text-webpack-plugin": "0.8.2",
"file-loader": "0.8.4",
"font-awesome": "4.4.0",
"glob-all": "3.0.1",
"good": "6.3.0",
"good-squeeze": "2.1.0",
"gridster": "0.5.6",
"hapi": "8.8.1",
Expand Down Expand Up @@ -155,6 +155,7 @@
"elasticdump": "2.1.1",
"eslint": "1.10.3",
"eslint-plugin-mocha": "1.1.0",
"event-stream": "3.3.2",
"expect.js": "0.3.1",
"faker": "1.1.0",
"grunt": "0.4.5",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
server:
port: 8274
logging:
json: true
optimize:
enabled: false
88 changes: 88 additions & 0 deletions src/cli/serve/__tests__/reload_logging_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { spawn } from 'child_process';
import { writeFileSync, readFile } from 'fs';
import { relative, resolve } from 'path';
import { safeDump } from 'js-yaml';
import es from 'event-stream';
import readYamlConfig from '../read_yaml_config';
import expect from 'expect.js';
const testConfigFile = follow(`fixtures/reload_logging_config/kibana.test.yml`);
const cli = follow(`../../../../bin/kibana`);

function follow(file) {
return relative(process.cwd(), resolve(__dirname, file));
}

function setLoggingJson(enabled) {
const conf = readYamlConfig(testConfigFile);
conf.logging = conf.logging || {};
conf.logging.json = enabled;
const yaml = safeDump(conf);
writeFileSync(testConfigFile, yaml);
return conf;
}

describe(`Server logging configuration`, function () {
it(`should be reloadable via SIGHUP process signaling`, function (done) {
let asserted = false;
let json = Infinity;
const conf = setLoggingJson(true);
const child = spawn(cli, [`--config`, testConfigFile]);

child.on('error', err => {
done(new Error(`error in child process while attempting to reload config.
${err.stack || err.message || err}`));
});

child.on('exit', code => {
expect(asserted).to.eql(true);
expect(code === null || code === 0).to.eql(true);
done();
});

child.stdout
.pipe(es.split())
.pipe(es.mapSync(function (line) {
if (!line) {
return line; // ignore empty lines
}
if (json--) {
expect(parseJsonLogLine).withArgs(line).to.not.throwError();
} else {
expectPlainTextLogLine(line);
}
}));

function parseJsonLogLine(line) {
try {
const data = JSON.parse(line);
const listening = data.tags.indexOf(`listening`) !== -1;
if (listening) {
switchToPlainTextLog();
}
} catch (err) {
expect(`Error parsing log line as JSON\n
${err.stack || err.message || err}`).to.eql(true);
}
}

function switchToPlainTextLog() {
json = 2; // ignore both "reloading" messages
setLoggingJson(false);
child.kill(`SIGHUP`); // reload logging config
}

function expectPlainTextLogLine(line) {
// assert
const tags = `[\u001b[32minfo\u001b[39m][\u001b[36mconfig\u001b[39m]`;
const status = `Reloaded logging configuration due to SIGHUP.`;
const expected = `${tags} ${status}`;
const actual = line.slice(-expected.length);
expect(actual).to.eql(expected);

// cleanup
asserted = true;
setLoggingJson(true);
child.kill();
}
});
});
17 changes: 11 additions & 6 deletions src/cli/serve/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ import _ from 'lodash';
import { statSync } from 'fs';
import { isWorker } from 'cluster';
import { resolve } from 'path';

import readYamlConfig from './read_yaml_config';
import { fromRoot } from '../../utils';

const cwd = process.cwd();
import readYamlConfig from './read_yaml_config';

let canCluster;
try {
Expand All @@ -28,7 +25,7 @@ const configPathCollector = pathCollector();
const pluginDirCollector = pathCollector();
const pluginPathCollector = pathCollector();

function initServerSettings(opts, extraCliOptions) {
function readServerSettings(opts, extraCliOptions) {
const settings = readYamlConfig(opts.config);
const set = _.partial(_.set, settings);
const get = _.partial(_.get, settings);
Expand Down Expand Up @@ -128,7 +125,8 @@ module.exports = function (program) {
}
}

const settings = initServerSettings(opts, this.getUnknownOptions());
const getCurrentSettings = () => readServerSettings(opts, this.getUnknownOptions());
const settings = getCurrentSettings();

if (canCluster && opts.dev && !isWorker) {
// stop processing the action and handoff to cluster manager
Expand Down Expand Up @@ -156,6 +154,13 @@ module.exports = function (program) {
process.exit(1); // eslint-disable-line no-process-exit
}

process.on('SIGHUP', function reloadConfig() {
const settings = getCurrentSettings();
kbnServer.server.log(['info', 'config'], 'Reloading logging configuration due to SIGHUP.');
kbnServer.applyLoggingConfiguration(settings);
kbnServer.server.log(['info', 'config'], 'Reloaded logging configuration due to SIGHUP.');
});

return kbnServer;
});
};
Expand Down
14 changes: 14 additions & 0 deletions src/server/kbn_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { constant, once, compact, flatten } from 'lodash';
import { promisify, resolve, fromNode } from 'bluebird';
import { isWorker } from 'cluster';
import { fromRoot, pkg } from '../utils';
import Config from './config/config';
import loggingConfiguration from './logging/configuration';

let rootDir = fromRoot('.');

Expand Down Expand Up @@ -107,4 +109,16 @@ module.exports = class KbnServer {
}
});
}

applyLoggingConfiguration(settings) {
const config = Config.withDefaultSchema(settings);
const loggingOptions = loggingConfiguration(config);
const subset = {
ops: config.get('ops'),
logging: config.get('logging')
};
const plain = JSON.stringify(subset, null, 2);
this.server.log(['info', 'config'], 'New logging configuration:\n' + plain);
this.server.plugins['even-better'].monitor.reconfigure(loggingOptions);
}
};
61 changes: 61 additions & 0 deletions src/server/logging/configuration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import _ from 'lodash';
import logReporter from './log_reporter';

export default function loggingConfiguration(config) {
let events = config.get('logging.events');

if (config.get('logging.silent')) {
_.defaults(events, {});
}
else if (config.get('logging.quiet')) {
_.defaults(events, {
log: ['listening', 'error', 'fatal'],
request: ['error'],
error: '*'
});
}
else if (config.get('logging.verbose')) {
_.defaults(events, {
log: '*',
ops: '*',
request: '*',
response: '*',
error: '*'
});
}
else {
_.defaults(events, {
log: ['info', 'warning', 'error', 'fatal'],
response: config.get('logging.json') ? '*' : '!',
request: ['info', 'warning', 'error', 'fatal'],
error: '*'
});
}

const options = {
opsInterval: config.get('ops.interval'),
requestHeaders: true,
requestPayload: true,
reporters: [
{
reporter: logReporter,
config: {
json: config.get('logging.json'),
dest: config.get('logging.dest'),
// I'm adding the default here because if you add another filter
// using the commandline it will remove authorization. I want users
// to have to explicitly set --logging.filter.authorization=none to
// have it show up int he logs.
filter: _.defaults(config.get('logging.filter'), {
authorization: 'remove'
})
},
events: _.transform(events, function (filtered, val, key) {
// provide a string compatible way to remove events
if (val !== '!') filtered[key] = val;
}, {})
}
]
};
return options;
}
63 changes: 5 additions & 58 deletions src/server/logging/index.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,15 @@
import _ from 'lodash';
import { fromNode } from 'bluebird';
import evenBetter from 'even-better';
import loggingConfiguration from './configuration';

module.exports = function (kbnServer, server, config) {
export default function (kbnServer, server, config) {
// prevent relying on kbnServer so this can be used with other hapi servers
kbnServer = null;

return fromNode(function (cb) {
let events = config.get('logging.events');

if (config.get('logging.silent')) {
_.defaults(events, {});
}
else if (config.get('logging.quiet')) {
_.defaults(events, {
log: ['listening', 'error', 'fatal'],
request: ['error'],
error: '*'
});
}
else if (config.get('logging.verbose')) {
_.defaults(events, {
log: '*',
ops: '*',
request: '*',
response: '*',
error: '*'
});
}
else {
_.defaults(events, {
log: ['info', 'warning', 'error', 'fatal'],
response: config.get('logging.json') ? '*' : '!',
request: ['info', 'warning', 'error', 'fatal'],
error: '*'
});
}

server.register({
register: require('good'),
options: {
opsInterval: config.get('ops.interval'),
requestHeaders: true,
requestPayload: true,
reporters: [
{
reporter: require('./log_reporter'),
config: {
json: config.get('logging.json'),
dest: config.get('logging.dest'),
// I'm adding the default here because if you add another filter
// using the commandline it will remove authorization. I want users
// to have to explicitly set --logging.filter.authorization=none to
// have it show up int he logs.
filter: _.defaults(config.get('logging.filter'), {
authorization: 'remove'
})
},
events: _.transform(events, function (filtered, val, key) {
// provide a string compatible way to remove events
if (val !== '!') filtered[key] = val;
}, {})
}
]
}
register: evenBetter,
options: loggingConfiguration(config)
}, cb);
});
};
1 change: 1 addition & 0 deletions src/server/logging/log_format_string.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ let typeColors = {
req: 'green',
res: 'green',
ops: 'cyan',
config: 'cyan',
err: 'red',
info: 'green',
error: 'red',
Expand Down
2 changes: 1 addition & 1 deletion src/server/status/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { join } from 'path';
export default function (kbnServer, server, config) {
kbnServer.status = new ServerStatus(kbnServer.server);

if (server.plugins.good) {
if (server.plugins['even-better']) {
kbnServer.mixin(require('./metrics'));
}

Expand Down
2 changes: 1 addition & 1 deletion src/server/status/metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = function (kbnServer, server, config) {

kbnServer.metrics = new Samples(12);

server.plugins.good.monitor.on('ops', function (event) {
server.plugins['even-better'].monitor.on('ops', function (event) {
let now = Date.now();
let secSinceLast = (now - lastReport) / 1000;
lastReport = now;
Expand Down

0 comments on commit ef1fb42

Please sign in to comment.