From 17555a33b6e1bdd772163073360bc60b4880368b Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 7 Oct 2019 21:23:32 +0100 Subject: [PATCH 01/73] chore(NA): add log options to config yml --- config/kibana.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/config/kibana.yml b/config/kibana.yml index 9525a6423d90a..9d74f80526d8b 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -90,7 +90,13 @@ #pid.file: /var/run/kibana.pid # Enables you specify a file where Kibana stores log output. -#logging.dest: stdout +#logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log +#logging.json: true + +#logging.rotateEveryBytes +#logging.keepFiles +#logging.interval +#logging.rotateOnStartup # Set the value of this setting to true to suppress all logging output. #logging.silent: false From a5a06d0186d434f2d000d0d761fe4da27c295259 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 10 Oct 2019 21:10:09 +0100 Subject: [PATCH 02/73] chore(NA): remove unwanted option from config declaration --- config/kibana.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 9d74f80526d8b..035f05840308f 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -91,11 +91,10 @@ # Enables you specify a file where Kibana stores log output. #logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log -#logging.json: true -#logging.rotateEveryBytes -#logging.keepFiles #logging.interval +#logging.keepFiles +#logging.rotateEveryBytes #logging.rotateOnStartup # Set the value of this setting to true to suppress all logging output. From 3f56e58d52909b4faafb57e7042808ce5fc8d8ac Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 11 Oct 2019 15:50:35 +0100 Subject: [PATCH 03/73] chore(NA): add the bootstrap for the logging rotate feature --- config/kibana.yml | 9 +++--- src/legacy/server/config/schema.js | 9 +++++- src/legacy/server/logging/index.js | 2 ++ src/legacy/server/logging/rotate/index.js | 39 +++++++++++++++++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 src/legacy/server/logging/rotate/index.js diff --git a/config/kibana.yml b/config/kibana.yml index 035f05840308f..a5c3669a92a57 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -90,12 +90,11 @@ #pid.file: /var/run/kibana.pid # Enables you specify a file where Kibana stores log output. -#logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log +logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log -#logging.interval -#logging.keepFiles -#logging.rotateEveryBytes -#logging.rotateOnStartup +# Enable you to turn on the logging rotate feature. +# Even enabled, the log rotate will only take effect if you specify a file for 'logging.dest'. +logging.rotate.enable: true # Set the value of this setting to true to suppress all logging output. #logging.silent: false diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index 2b91eafd45caa..3e63e1bb42653 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -137,7 +137,14 @@ export default () => Joi.object({ then: Joi.default(!process.stdout.isTTY), otherwise: Joi.default(true) }), - timezone: Joi.string() + timezone: Joi.string(), + rotate: Joi.object().allow(false).keys({ + enable: Joi.boolean().default(false), + interval: Joi.number().greater(-1).default(0), + everyBytes: Joi.number().greater(0).default(10485760), + keepFiles: Joi.number().greater(0).default(7), + onStartup: Joi.boolean().default(false) + }).default() }).default(), ops: Joi.object({ diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index 6e07757abf7bc..c3fc659add35b 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -20,6 +20,7 @@ import good from '@elastic/good'; import loggingConfiguration from './configuration'; import { logWithMetadata } from './log_with_metadata'; +import { setupLoggingRotate } from './rotate'; export async function setupLogging(server, config) { return await server.register({ @@ -30,5 +31,6 @@ export async function setupLogging(server, config) { export async function loggingMixin(kbnServer, server, config) { logWithMetadata.decorateServer(server); + setupLoggingRotate(config); return await setupLogging(server, config); } diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js new file mode 100644 index 0000000000000..091d140bf2e4e --- /dev/null +++ b/src/legacy/server/logging/rotate/index.js @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export function setupLoggingRotate(config) { + // We just want to start the logging rotate service once + // and we choose to use the worker server type for it + if (process.env.kbnWorkerType !== 'server') { + return; + } + + // If log rotate is not enabled we skip + if (!config.get('logging.rotate.enable')) { + return; + } + + // We don't want to run logging rotate server if + // we are not logging to a file + if (config.get('logging.rotate.enable') && config.get('logging.dest') === 'stdout') { + return; + } + + // Enable Logging Rotate Service +} From a87c0807fbe727a0186a397a6ea068f3ff08fcda Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 16 Oct 2019 17:42:29 +0100 Subject: [PATCH 04/73] feat(NA): base interface setup for log rotation feature --- src/legacy/server/logging/index.js | 11 +++-- src/legacy/server/logging/rotate/index.js | 12 +++++- .../server/logging/rotate/log_rotator.js | 43 +++++++++++++++++++ 3 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 src/legacy/server/logging/rotate/log_rotator.js diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index c3fc659add35b..4eada0baceba9 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -22,15 +22,18 @@ import loggingConfiguration from './configuration'; import { logWithMetadata } from './log_with_metadata'; import { setupLoggingRotate } from './rotate'; -export async function setupLogging(server, config) { +export async function setupLogging(server, loggingConfig) { return await server.register({ plugin: good, - options: loggingConfiguration(config) + options: loggingConfig }); } export async function loggingMixin(kbnServer, server, config) { + const generatedLoggingConfiguration = loggingConfiguration(config); + logWithMetadata.decorateServer(server); - setupLoggingRotate(config); - return await setupLogging(server, config); + setupLoggingRotate(config, generatedLoggingConfiguration.reporters.logReporter[0]); + + return await setupLogging(server, generatedLoggingConfiguration); } diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 091d140bf2e4e..784904f861bd6 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -17,7 +17,9 @@ * under the License. */ -export function setupLoggingRotate(config) { +import { LogRotator } from './log_rotator'; + +export function setupLoggingRotate(config, logInterceptor) { // We just want to start the logging rotate service once // and we choose to use the worker server type for it if (process.env.kbnWorkerType !== 'server') { @@ -32,8 +34,16 @@ export function setupLoggingRotate(config) { // We don't want to run logging rotate server if // we are not logging to a file if (config.get('logging.rotate.enable') && config.get('logging.dest') === 'stdout') { + this.logWithMetadata( + ['warning', 'logging:rotate'], + 'Logging rotate is enabled but logging.dest is configured for stdout. The logging rotate will take no action.' + ); return; } // Enable Logging Rotate Service + const logRotator = new LogRotator(config, logInterceptor); + logRotator.start(); + + return logRotator; } diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js new file mode 100644 index 0000000000000..1640c95e807c5 --- /dev/null +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -0,0 +1,43 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export class LogRotator { + constructor(config, logInterceptor) { + this.logInterceptor = logInterceptor; + this.logFilePath = config.get('logging.dest'); + this.interval = config.get('logging.rotate.interval'); + this.everyBytes = config.get('logging.rotate.everyBytes'); + this.keepFiles = config.get('logging.rotate.keepFiles'); + this.onStartup = config.get('logging.rotate.onStartup'); + } + + start() {} + + stop() {} + + _shouldRotate() {} + + _rotate() {} + + _rotateNow() { + // rename old + // reload log configuration + process.kill(process.pid, 'SIGHUP'); + } +} From 7c739d4159a975308101ffb7d173c5744e277e55 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 16 Oct 2019 19:50:58 +0100 Subject: [PATCH 05/73] docs(NA): add documentation for the new logging rotate options. chore(NA): added new schema validations --- docs/setup/settings.asciidoc | 31 ++++++++++++++++++++++++++++++ src/legacy/server/config/schema.js | 6 +++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index f02cf188d31ad..42a2ec8a9a865 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -146,6 +146,37 @@ will default to `true`. `logging.quiet:`:: *Default: false* Set the value of this setting to `true` to suppress all logging output other than error messages. +`logging.rotate:`:: *Default: false* Specifies the options for the logging rotate feature. +The following example shows a valid logging rotate configuration: ++ +-- + logging.rotate: + enable: true + everyBytes: 104857600 + interval: 60 + keepFiles: 10 + onStartup: true +-- + +`logging.rotate.enable:`:: *Default: false* Set the value of this setting to `true` to +enable logging rotate. + +`logging.rotate.everyBytes:`:: *Default: 10485760* The maximum size of a log file. If the +limit is reached, a new log file is generated. The default size limit is 10485760 (10 MB) and +this option should be at least greater than 1024. + +`logging.rotate.interval:`:: *Default: 0* The interval for the logging rotate service to check +if the conditions are met in order to rotate the log file. That option is expressed in `minutes` +and should be a positive number. If `0` is set then the interval check would be disabled. + +`logging.rotate.keepFiles:`:: *Default: 7* The number of most recent rotated log files to keep +on disk. Older files are deleted during log rotation. The default value is 7. The `logging.rotate.keepFiles` +option has to be in the range of 2 to 1024 files. + +`logging.rotate.onStartup:`:: *Default: false* Set the value of this setting to `true` to +have the logging rotate service to check if the conditions to rotate a file are met +every time Kibana starts. + `logging.silent:`:: *Default: false* Set the value of this setting to `true` to suppress all logging output. diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index 38c07139a63a2..b32d7bae08112 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -138,11 +138,11 @@ export default () => Joi.object({ otherwise: Joi.default(true) }), timezone: Joi.string(), - rotate: Joi.object().allow(false).keys({ + rotate: Joi.object().keys({ enable: Joi.boolean().default(false), interval: Joi.number().greater(-1).default(0), - everyBytes: Joi.number().greater(0).default(10485760), - keepFiles: Joi.number().greater(0).default(7), + everyBytes: Joi.number().greater(1024).default(10485760), + keepFiles: Joi.number().greater(2).less(1024).default(7), onStartup: Joi.boolean().default(false) }).default() }).default(), From 057f997b8046c59bd2cef966e9a279d7460f5d86 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 16 Oct 2019 22:06:25 +0100 Subject: [PATCH 06/73] chore(NA): base lifecycle methods and logic --- .../server/logging/rotate/log_rotator.js | 100 +++++++++++++++++- 1 file changed, 96 insertions(+), 4 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 1640c95e807c5..dcc012b5876c4 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -17,6 +17,9 @@ * under the License. */ +import fs from 'fs'; +import { throttle } from 'lodash'; + export class LogRotator { constructor(config, logInterceptor) { this.logInterceptor = logInterceptor; @@ -25,15 +28,104 @@ export class LogRotator { this.everyBytes = config.get('logging.rotate.everyBytes'); this.keepFiles = config.get('logging.rotate.keepFiles'); this.onStartup = config.get('logging.rotate.onStartup'); + this.running = false; + this.logFileSize = 0; + + throttle(this._rotate, 5000); + } + + start() { + if (this.running) { + return; + } + + // create exit listener for cleanup purposes + this._createExitListener(); + + // init log file size monitor + this._startLogFileSizeMonitor(); + + // init log file interval monitor + this._startLogFileIntervalMonitor(); + + // call rotate in case we should try + // to rotate on startup + this._callRotateOnStartup(); + + this.running = true; + } + + stop() { + if (!this.running) { + return; + } + + // cleanup exit listener + this._deleteExitListener(); + + // stop log file size monitor + this._stopLogFileSizeMonitor(); + + // stop log file interval monitor + this._stopLogFileIntervalMonitor(); + + this.running = false; + } + + _startLogFileSizeMonitor() { + + } + + _logFileSizeMonitorHandle() { + } - start() {} + _stopLogFileSizeMonitor() { - stop() {} + } + + _startLogFileIntervalMonitor() { + + } + + _logFileIntervalMonitorHandle() { - _shouldRotate() {} + } + + _stopLogFileIntervalMonitor() { + + } + + _createExitListener() { + process.on('exit', this.stop()); + } - _rotate() {} + _deleteExitListener() { + process.removeListener('exit', this.stop()); + } + + _getLogFileSize() { + const logFileStats = fs.statSync(this.logFilePath); + return logFileStats.size; + } + + _callRotateOnStartup() { + if (this.onStartup) { + this._rotate(); + } + } + + _shouldRotate() { + return false; + } + + _rotate() { + if (!this._shouldRotate()) { + return; + } + + this._rotateNow(); + } _rotateNow() { // rename old From ecd543627547ed9481d02771b442230cf96e7db6 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 17 Oct 2019 04:14:02 +0100 Subject: [PATCH 07/73] feat(NA): monitor logic for log rotate feature --- .../server/logging/rotate/log_rotator.js | 58 ++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index dcc012b5876c4..6ed87eebd81ea 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -30,6 +30,9 @@ export class LogRotator { this.onStartup = config.get('logging.rotate.onStartup'); this.running = false; this.logFileSize = 0; + this.intervalID = 0; + this.lastIntervalCycleTime = 0; + this.isRotating = false; throttle(this._rotate, 5000); } @@ -73,27 +76,43 @@ export class LogRotator { } _startLogFileSizeMonitor() { - + this.logFileSize = this._getLogFileSize(); + this.logInterceptor.on('data', this._logFileSizeMonitorHandler()); } - _logFileSizeMonitorHandle() { + _logFileSizeMonitorHandler(chunk) { + if (!chunk) { + return; + } + this.logFileSize += chunk.length; + this._rotate(); } _stopLogFileSizeMonitor() { - + this.logInterceptor.removeListener('data', this._logFileIntervalMonitorHandler()); } _startLogFileIntervalMonitor() { + if (!this.interval) { + return; + } + this.intervalID = setInterval(this._logFileIntervalMonitorHandler, this.interval * 60 * 1000); } - _logFileIntervalMonitorHandle() { - + _logFileIntervalMonitorHandler() { + this.lastIntervalCycleTime = new Date(); + this._rotate(); } _stopLogFileIntervalMonitor() { + if (!this.intervalID) { + return; + } + clearInterval(this.intervalID); + this.intervalID = 0; } _createExitListener() { @@ -116,7 +135,23 @@ export class LogRotator { } _shouldRotate() { - return false; + // should rotate evaluation + // 1. should rotate if current log size exceeds + // the defined one on everyBytes or has already + // pass the defined time on interval. + // 2. should not rotate if is already rotating or if any + // of the conditions on 1. do not apply + if (this.isRotating) { + return false; + } + + if (this.logFileSize >= this.everyBytes) { + return true; + } + + const currentTime = (new Date()).getTime(); + const elapsedTime = Math.abs(currentTime - this.lastIntervalCycleTime.getTime()); + return !!(this.lastIntervalCycleTime && elapsedTime > this.interval * 60 * 1000); } _rotate() { @@ -128,8 +163,17 @@ export class LogRotator { } _rotateNow() { + // rotate process + // 1. delete last file + // 2. rename all files to with +1 + // 3. rename + compress current log into 1 + // 4. send SIGHUP to reload log config + + this.isRotating = true; // rename old // reload log configuration - process.kill(process.pid, 'SIGHUP'); + // => process.kill(process.pid, 'SIGHUP'); + console.log('ROTATED'); + this.isRotating = false; } } From 0a0dd4a965befe4e983516dbb1c83fb0c73ba37e Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 17 Oct 2019 17:29:59 +0100 Subject: [PATCH 08/73] fix(NA): basic log rotation lifecycle --- config/kibana.yml | 2 +- src/legacy/server/logging/configuration.js | 16 +++++-- src/legacy/server/logging/index.js | 4 +- src/legacy/server/logging/log_reporter.js | 45 ++++++++++--------- .../server/logging/rotate/log_rotator.js | 31 +++++++------ 5 files changed, 55 insertions(+), 43 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index a5c3669a92a57..fc73ac69ea718 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -94,7 +94,7 @@ logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log # Enable you to turn on the logging rotate feature. # Even enabled, the log rotate will only take effect if you specify a file for 'logging.dest'. -logging.rotate.enable: true +# logging.rotate.enable: false # Set the value of this setting to true to suppress all logging output. #logging.silent: false diff --git a/src/legacy/server/logging/configuration.js b/src/legacy/server/logging/configuration.js index 6a58694a247ea..8cc6a2e1be011 100644 --- a/src/legacy/server/logging/configuration.js +++ b/src/legacy/server/logging/configuration.js @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import { getLoggerStream } from './log_reporter'; +import { LogReporter } from './log_reporter'; export default function loggingConfiguration(config) { const events = config.get('logging.events'); @@ -50,7 +50,7 @@ export default function loggingConfiguration(config) { }); } - const loggerStream = getLoggerStream({ + const logReporter = new LogReporter({ config: { json: config.get('logging.json'), dest: config.get('logging.dest'), @@ -79,8 +79,16 @@ export default function loggingConfiguration(config) { request: ['headers', 'payload'] }, reporters: { - logReporter: [loggerStream] + logReporter: [logReporter.logInterceptor] + } + }; + + return { + validOptions: { + ...options + }, + extraOptions: { + formattedLogReporterStream: logReporter.formattedLogStream } }; - return options; } diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index 4eada0baceba9..88e238b522003 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -33,7 +33,7 @@ export async function loggingMixin(kbnServer, server, config) { const generatedLoggingConfiguration = loggingConfiguration(config); logWithMetadata.decorateServer(server); - setupLoggingRotate(config, generatedLoggingConfiguration.reporters.logReporter[0]); + setupLoggingRotate(config, generatedLoggingConfiguration.extraOptions.formattedLogReporterStream); - return await setupLogging(server, generatedLoggingConfiguration); + return await setupLogging(server, generatedLoggingConfiguration.validOptions); } diff --git a/src/legacy/server/logging/log_reporter.js b/src/legacy/server/logging/log_reporter.js index 3a1b421a48c2f..8a94bf9ad2e8b 100644 --- a/src/legacy/server/logging/log_reporter.js +++ b/src/legacy/server/logging/log_reporter.js @@ -24,29 +24,30 @@ import LogFormatJson from './log_format_json'; import LogFormatString from './log_format_string'; import { LogInterceptor } from './log_interceptor'; -export function getLoggerStream({ events, config }) { - const squeeze = new Squeeze(events); - const format = config.json ? new LogFormatJson(config) : new LogFormatString(config); - const logInterceptor = new LogInterceptor(); +export class LogReporter { + constructor({ events, config }) { + this.squeeze = new Squeeze(events); + this.format = config.json ? new LogFormatJson(config) : new LogFormatString(config); + this.logInterceptor = new LogInterceptor(); + this.dest = null; + this.formattedLogStream = this.logInterceptor + .pipe(this.squeeze) + .pipe(this.format); - let dest; - if (config.dest === 'stdout') { - dest = process.stdout; - } else { - dest = writeStr(config.dest, { - flags: 'a', - encoding: 'utf8' - }); + if (config.dest === 'stdout') { + this.dest = process.stdout; + } else { + this.dest = writeStr(config.dest, { + flags: 'a', + encoding: 'utf8' + }); - logInterceptor.on('end', () => { - dest.end(); - }); - } - - logInterceptor - .pipe(squeeze) - .pipe(format) - .pipe(dest); + this.logInterceptor.on('end', () => { + this.dest.end(); + }); + } - return logInterceptor; + this.formattedLogStream + .pipe(this.dest); + } } diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 6ed87eebd81ea..dc84cc0ff5484 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -21,8 +21,8 @@ import fs from 'fs'; import { throttle } from 'lodash'; export class LogRotator { - constructor(config, logInterceptor) { - this.logInterceptor = logInterceptor; + constructor(config, logReporterStream) { + this.logReporterStream = logReporterStream; this.logFilePath = config.get('logging.dest'); this.interval = config.get('logging.rotate.interval'); this.everyBytes = config.get('logging.rotate.everyBytes'); @@ -33,8 +33,7 @@ export class LogRotator { this.intervalID = 0; this.lastIntervalCycleTime = 0; this.isRotating = false; - - throttle(this._rotate, 5000); + this.throttledRotate = throttle(() => { this._rotate(); }, 5000); } start() { @@ -77,7 +76,7 @@ export class LogRotator { _startLogFileSizeMonitor() { this.logFileSize = this._getLogFileSize(); - this.logInterceptor.on('data', this._logFileSizeMonitorHandler()); + this.logReporterStream.on('data', this._logFileSizeMonitorHandler.bind(this)); } _logFileSizeMonitorHandler(chunk) { @@ -85,12 +84,12 @@ export class LogRotator { return; } - this.logFileSize += chunk.length; - this._rotate(); + this.logFileSize += chunk.length || 0; + this.throttledRotate(); } _stopLogFileSizeMonitor() { - this.logInterceptor.removeListener('data', this._logFileIntervalMonitorHandler()); + this.logReporterStream.removeListener('data', this._logFileIntervalMonitorHandler); } _startLogFileIntervalMonitor() { @@ -98,12 +97,12 @@ export class LogRotator { return; } - this.intervalID = setInterval(this._logFileIntervalMonitorHandler, this.interval * 60 * 1000); + this.intervalID = setInterval(this._logFileIntervalMonitorHandler.bind(this), this.interval * 60 * 1000); } _logFileIntervalMonitorHandler() { this.lastIntervalCycleTime = new Date(); - this._rotate(); + this.throttledRotate(); } _stopLogFileIntervalMonitor() { @@ -116,11 +115,11 @@ export class LogRotator { } _createExitListener() { - process.on('exit', this.stop()); + process.on('exit', this.stop.bind(this)); } _deleteExitListener() { - process.removeListener('exit', this.stop()); + process.removeListener('exit', this.stop); } _getLogFileSize() { @@ -130,7 +129,7 @@ export class LogRotator { _callRotateOnStartup() { if (this.onStartup) { - this._rotate(); + this.throttledRotate(); } } @@ -149,9 +148,13 @@ export class LogRotator { return true; } + if (!this.lastIntervalCycleTime) { + return false; + } + const currentTime = (new Date()).getTime(); const elapsedTime = Math.abs(currentTime - this.lastIntervalCycleTime.getTime()); - return !!(this.lastIntervalCycleTime && elapsedTime > this.interval * 60 * 1000); + return elapsedTime > this.interval * 60 * 1000; } _rotate() { From 75d2ffa6eb88adf785fc80e96ca883698758a5c3 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 17 Oct 2019 17:32:09 +0100 Subject: [PATCH 09/73] chore(NA): fix typo on config file --- config/kibana.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index fc73ac69ea718..86153281e71b8 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -90,11 +90,11 @@ #pid.file: /var/run/kibana.pid # Enables you specify a file where Kibana stores log output. -logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log +#logging.dest: stdout # Enable you to turn on the logging rotate feature. # Even enabled, the log rotate will only take effect if you specify a file for 'logging.dest'. -# logging.rotate.enable: false +#logging.rotate.enable: false # Set the value of this setting to true to suppress all logging output. #logging.silent: false From 29779b99a26235b4530d537e617b5fee2aafa465 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 18 Oct 2019 02:48:12 +0100 Subject: [PATCH 10/73] feat(NA): add rotate files feature to log rotator --- .../server/logging/rotate/log_rotator.js | 67 ++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index dc84cc0ff5484..604a30406cfe5 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -19,6 +19,7 @@ import fs from 'fs'; import { throttle } from 'lodash'; +import { basename, dirname, sep } from 'path'; export class LogRotator { constructor(config, logReporterStream) { @@ -167,16 +168,66 @@ export class LogRotator { _rotateNow() { // rotate process - // 1. delete last file - // 2. rename all files to with +1 - // 3. rename + compress current log into 1 - // 4. send SIGHUP to reload log config + // 1. get rotated files metadata (list of log rotated files present on the log folder, numerical sorted) + // 2. delete last file + // 3. rename all files to the correct index +1 + // 4. rename + compress current log into 1 + // 5. send SIGHUP to reload log config + // rotate process is starting this.isRotating = true; - // rename old - // reload log configuration - // => process.kill(process.pid, 'SIGHUP'); - console.log('ROTATED'); + + // get rotated files metadata + const rotatedFiles = this._readRotatedFilesMetadata(); + + // delete last file + this._deleteLastRotatedFile(rotatedFiles); + + // rename all files to correct index + 1 + this._renameRotatedFilesByOne(rotatedFiles); + + // rename + compress current log into 0 + this._rotateCurrentLogFile(); + + // send SIGHUP to reload log configuration + process.kill(process.pid, 'SIGHUP'); + + // rotate process is finished this.isRotating = false; } + + _readRotatedFilesMetadata() { + const logFileBaseName = basename(this.logFilePath); + const logFilesFolder = dirname(this.logFilePath); + + return fs.readdirSync(logFilesFolder) + .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) + .sort((a, b) => Number(a.match(/(\d+)/g)[0]) - Number(b.match(/(\d+)/g)[0])) + .map(filename => `${logFilesFolder}${sep}${filename}`); + } + + _deleteLastRotatedFile(rotatedFiles) { + if (rotatedFiles.length < this.keepFiles) { + return; + } + + const lastFilePath = rotatedFiles.pop(); + fs.unlinkSync(lastFilePath); + } + + _renameRotatedFilesByOne(rotatedFiles) { + const logFileBaseName = basename(this.logFilePath); + const logFilesFolder = dirname(this.logFilePath); + + for (let i = rotatedFiles.length - 1; i > 0; i--) { + const oldFilePath = rotatedFiles[i]; + const newFilePath = `${logFilesFolder}${sep}${logFileBaseName}.${i + 1}`; + fs.renameSync(oldFilePath, newFilePath); + } + } + + _rotateCurrentLogFile() { + const newFilePath = `${this.logFilePath}.0`; + fs.renameSync(this.logFilePath, newFilePath); + } } From 73cc67859dcda7fc2bee17ca8cff7ce88562bac0 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 19 Oct 2019 18:31:54 +0100 Subject: [PATCH 11/73] chore(NA): fix log rotate config --- config/kibana.yml | 8 +++- src/legacy/server/logging/index.js | 1 + .../server/logging/rotate/log_rotator.js | 46 +++++++++++++------ 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 86153281e71b8..1ba98b236bb9d 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -90,11 +90,15 @@ #pid.file: /var/run/kibana.pid # Enables you specify a file where Kibana stores log output. -#logging.dest: stdout +logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log # Enable you to turn on the logging rotate feature. # Even enabled, the log rotate will only take effect if you specify a file for 'logging.dest'. -#logging.rotate.enable: false +logging.rotate.enable: true + +### TO DELETE +logging.rotate.interval: 1 +logging.rotate.onStartup: true # Set the value of this setting to true to suppress all logging output. #logging.silent: false diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index 88e238b522003..246599b40bb9c 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -23,6 +23,7 @@ import { logWithMetadata } from './log_with_metadata'; import { setupLoggingRotate } from './rotate'; export async function setupLogging(server, loggingConfig) { + console.log('MOTHER FUCKER OLA'); return await server.register({ plugin: good, options: loggingConfig diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 604a30406cfe5..01b55d5488c33 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -32,9 +32,10 @@ export class LogRotator { this.running = false; this.logFileSize = 0; this.intervalID = 0; - this.lastIntervalCycleTime = 0; + this.lastRotateTime = (new Date()).getTime(); this.isRotating = false; this.throttledRotate = throttle(() => { this._rotate(); }, 5000); + console.log(process.pid); } start() { @@ -102,8 +103,7 @@ export class LogRotator { } _logFileIntervalMonitorHandler() { - this.lastIntervalCycleTime = new Date(); - this.throttledRotate(); + this._rotate(); } _stopLogFileIntervalMonitor() { @@ -124,13 +124,17 @@ export class LogRotator { } _getLogFileSize() { - const logFileStats = fs.statSync(this.logFilePath); - return logFileStats.size; + try { + const logFileStats = fs.statSync(this.logFilePath); + return logFileStats.size; + } catch { + return 0; + } } _callRotateOnStartup() { if (this.onStartup) { - this.throttledRotate(); + this._rotate(); } } @@ -149,20 +153,21 @@ export class LogRotator { return true; } - if (!this.lastIntervalCycleTime) { - return false; - } - const currentTime = (new Date()).getTime(); - const elapsedTime = Math.abs(currentTime - this.lastIntervalCycleTime.getTime()); - return elapsedTime > this.interval * 60 * 1000; + const elapsedTime = Math.round((currentTime - this.lastRotateTime) / 1000); + + console.log('elapsedTime: ' + elapsedTime + ' interval: ' + this.interval * 60); + return elapsedTime >= this.interval * 30; } _rotate() { + console.log('logSize: ' + this.logFileSize + ' limit: ' + this.everyBytes); if (!this._shouldRotate()) { + console.log('no'); return; } + console.log('yes'); this._rotateNow(); } @@ -173,6 +178,9 @@ export class LogRotator { // 3. rename all files to the correct index +1 // 4. rename + compress current log into 1 // 5. send SIGHUP to reload log config + if (this.isRotating) { + return false; + } // rotate process is starting this.isRotating = true; @@ -180,6 +188,8 @@ export class LogRotator { // get rotated files metadata const rotatedFiles = this._readRotatedFilesMetadata(); + console.log(rotatedFiles); + // delete last file this._deleteLastRotatedFile(rotatedFiles); @@ -192,6 +202,12 @@ export class LogRotator { // send SIGHUP to reload log configuration process.kill(process.pid, 'SIGHUP'); + // Reset log file size + this.logFileSize = 0; + + // Reset last rotate time + this.lastRotateTime = (new Date()).getTime(); + // rotate process is finished this.isRotating = false; } @@ -203,7 +219,8 @@ export class LogRotator { return fs.readdirSync(logFilesFolder) .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) .sort((a, b) => Number(a.match(/(\d+)/g)[0]) - Number(b.match(/(\d+)/g)[0])) - .map(filename => `${logFilesFolder}${sep}${filename}`); + .map(filename => `${logFilesFolder}${sep}${filename}`) + .filter(filepath => fs.existsSync(filepath)); } _deleteLastRotatedFile(rotatedFiles) { @@ -219,7 +236,7 @@ export class LogRotator { const logFileBaseName = basename(this.logFilePath); const logFilesFolder = dirname(this.logFilePath); - for (let i = rotatedFiles.length - 1; i > 0; i--) { + for (let i = rotatedFiles.length - 1; i >= 0; i--) { const oldFilePath = rotatedFiles[i]; const newFilePath = `${logFilesFolder}${sep}${logFileBaseName}.${i + 1}`; fs.renameSync(oldFilePath, newFilePath); @@ -229,5 +246,6 @@ export class LogRotator { _rotateCurrentLogFile() { const newFilePath = `${this.logFilePath}.0`; fs.renameSync(this.logFilePath, newFilePath); + fs.writeFileSync(this.logFilePath, ''); } } From 59e7ccacfb02ec3f83dde4017f48488495cde161 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sun, 20 Oct 2019 01:50:07 +0100 Subject: [PATCH 12/73] chore(NA): some tests to try logging rotate lifecycle --- src/legacy/server/logging/configuration.js | 16 +++--------- src/legacy/server/logging/index.js | 25 +++++++++++++------ src/legacy/server/logging/log_reporter.js | 10 +++++++- src/legacy/server/logging/rotate/index.js | 9 +++++-- .../server/logging/rotate/log_rotator.js | 4 +-- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/legacy/server/logging/configuration.js b/src/legacy/server/logging/configuration.js index 8cc6a2e1be011..ec11f9c771f09 100644 --- a/src/legacy/server/logging/configuration.js +++ b/src/legacy/server/logging/configuration.js @@ -18,9 +18,8 @@ */ import _ from 'lodash'; -import { LogReporter } from './log_reporter'; -export default function loggingConfiguration(config) { +export default function loggingConfiguration(config, logReporter) { const events = config.get('logging.events'); if (config.get('logging.silent')) { @@ -50,7 +49,7 @@ export default function loggingConfiguration(config) { }); } - const logReporter = new LogReporter({ + logReporter.configLogReporter({ config: { json: config.get('logging.json'), dest: config.get('logging.dest'), @@ -71,7 +70,7 @@ export default function loggingConfiguration(config) { }, {}) }); - const options = { + return { ops: { interval: config.get('ops.interval') }, @@ -82,13 +81,4 @@ export default function loggingConfiguration(config) { logReporter: [logReporter.logInterceptor] } }; - - return { - validOptions: { - ...options - }, - extraOptions: { - formattedLogReporterStream: logReporter.formattedLogStream - } - }; } diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index 246599b40bb9c..aaf4cf65149db 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -19,22 +19,31 @@ import good from '@elastic/good'; import loggingConfiguration from './configuration'; +import { LogReporter } from './log_reporter'; import { logWithMetadata } from './log_with_metadata'; import { setupLoggingRotate } from './rotate'; -export async function setupLogging(server, loggingConfig) { - console.log('MOTHER FUCKER OLA'); - return await server.register({ +export async function setupLogging(server, config) { + const logReporter = new LogReporter(); + + await server.register({ plugin: good, - options: loggingConfig + options: loggingConfiguration(config, logReporter) }); + setupLoggingRotate(config, logReporter); } export async function loggingMixin(kbnServer, server, config) { - const generatedLoggingConfiguration = loggingConfiguration(config); - logWithMetadata.decorateServer(server); - setupLoggingRotate(config, generatedLoggingConfiguration.extraOptions.formattedLogReporterStream); - return await setupLogging(server, generatedLoggingConfiguration.validOptions); + console.log(process.pid); + + setInterval(()=> { + server.logWithMetadata( + ['info', 'LOG'], + 'TEST LOG' + ); + }, 10000); + + return await setupLogging(server, config); } diff --git a/src/legacy/server/logging/log_reporter.js b/src/legacy/server/logging/log_reporter.js index 8a94bf9ad2e8b..f0d432104d39a 100644 --- a/src/legacy/server/logging/log_reporter.js +++ b/src/legacy/server/logging/log_reporter.js @@ -25,7 +25,15 @@ import LogFormatString from './log_format_string'; import { LogInterceptor } from './log_interceptor'; export class LogReporter { - constructor({ events, config }) { + constructor() { + this.squeeze = null; + this.format = null; + this.logInterceptor = null; + this.dest = null; + this.formattedLogStream = null; + } + + configLogReporter({ events, config }) { this.squeeze = new Squeeze(events); this.format = config.json ? new LogFormatJson(config) : new LogFormatString(config); this.logInterceptor = new LogInterceptor(); diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 784904f861bd6..5d59c94a31bf0 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -19,7 +19,9 @@ import { LogRotator } from './log_rotator'; -export function setupLoggingRotate(config, logInterceptor) { +let logRotator = null; + +export function setupLoggingRotate(config, logReporter) { // We just want to start the logging rotate service once // and we choose to use the worker server type for it if (process.env.kbnWorkerType !== 'server') { @@ -42,7 +44,10 @@ export function setupLoggingRotate(config, logInterceptor) { } // Enable Logging Rotate Service - const logRotator = new LogRotator(config, logInterceptor); + if (!logRotator) { + logRotator = new LogRotator(config, logReporter.formattedLogStream); + } + logRotator.stop(); logRotator.start(); return logRotator; diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 01b55d5488c33..bb6fbf280e8df 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -35,6 +35,7 @@ export class LogRotator { this.lastRotateTime = (new Date()).getTime(); this.isRotating = false; this.throttledRotate = throttle(() => { this._rotate(); }, 5000); + console.log(process.ppid); console.log(process.pid); } @@ -200,7 +201,7 @@ export class LogRotator { this._rotateCurrentLogFile(); // send SIGHUP to reload log configuration - process.kill(process.pid, 'SIGHUP'); + process.kill(process.ppid, 'SIGHUP'); // Reset log file size this.logFileSize = 0; @@ -246,6 +247,5 @@ export class LogRotator { _rotateCurrentLogFile() { const newFilePath = `${this.logFilePath}.0`; fs.renameSync(this.logFilePath, newFilePath); - fs.writeFileSync(this.logFilePath, ''); } } From e41335b9cf2d630e1d1ca23caf3265e40b842a1b Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 22 Oct 2019 04:01:33 +0100 Subject: [PATCH 13/73] feat(NA): correct log rotation logic --- config/kibana.yml | 4 - docs/setup/settings.asciidoc | 10 -- src/legacy/server/config/schema.js | 4 +- src/legacy/server/logging/configuration.js | 10 +- src/legacy/server/logging/index.js | 19 +-- src/legacy/server/logging/log_reporter.js | 51 ++++---- src/legacy/server/logging/rotate/index.js | 23 ++-- .../server/logging/rotate/log_rotator.js | 111 ++++++++++++++---- 8 files changed, 130 insertions(+), 102 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 1ba98b236bb9d..a5c3669a92a57 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -96,10 +96,6 @@ logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log # Even enabled, the log rotate will only take effect if you specify a file for 'logging.dest'. logging.rotate.enable: true -### TO DELETE -logging.rotate.interval: 1 -logging.rotate.onStartup: true - # Set the value of this setting to true to suppress all logging output. #logging.silent: false diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 42a2ec8a9a865..a6f8c33805bbf 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -153,9 +153,7 @@ The following example shows a valid logging rotate configuration: logging.rotate: enable: true everyBytes: 104857600 - interval: 60 keepFiles: 10 - onStartup: true -- `logging.rotate.enable:`:: *Default: false* Set the value of this setting to `true` to @@ -165,18 +163,10 @@ enable logging rotate. limit is reached, a new log file is generated. The default size limit is 10485760 (10 MB) and this option should be at least greater than 1024. -`logging.rotate.interval:`:: *Default: 0* The interval for the logging rotate service to check -if the conditions are met in order to rotate the log file. That option is expressed in `minutes` -and should be a positive number. If `0` is set then the interval check would be disabled. - `logging.rotate.keepFiles:`:: *Default: 7* The number of most recent rotated log files to keep on disk. Older files are deleted during log rotation. The default value is 7. The `logging.rotate.keepFiles` option has to be in the range of 2 to 1024 files. -`logging.rotate.onStartup:`:: *Default: false* Set the value of this setting to `true` to -have the logging rotate service to check if the conditions to rotate a file are met -every time Kibana starts. - `logging.silent:`:: *Default: false* Set the value of this setting to `true` to suppress all logging output. diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index b32d7bae08112..d4f21b7967aa2 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -140,10 +140,8 @@ export default () => Joi.object({ timezone: Joi.string(), rotate: Joi.object().keys({ enable: Joi.boolean().default(false), - interval: Joi.number().greater(-1).default(0), everyBytes: Joi.number().greater(1024).default(10485760), - keepFiles: Joi.number().greater(2).less(1024).default(7), - onStartup: Joi.boolean().default(false) + keepFiles: Joi.number().greater(2).less(1024).default(7) }).default() }).default(), diff --git a/src/legacy/server/logging/configuration.js b/src/legacy/server/logging/configuration.js index ec11f9c771f09..6a58694a247ea 100644 --- a/src/legacy/server/logging/configuration.js +++ b/src/legacy/server/logging/configuration.js @@ -18,8 +18,9 @@ */ import _ from 'lodash'; +import { getLoggerStream } from './log_reporter'; -export default function loggingConfiguration(config, logReporter) { +export default function loggingConfiguration(config) { const events = config.get('logging.events'); if (config.get('logging.silent')) { @@ -49,7 +50,7 @@ export default function loggingConfiguration(config, logReporter) { }); } - logReporter.configLogReporter({ + const loggerStream = getLoggerStream({ config: { json: config.get('logging.json'), dest: config.get('logging.dest'), @@ -70,7 +71,7 @@ export default function loggingConfiguration(config, logReporter) { }, {}) }); - return { + const options = { ops: { interval: config.get('ops.interval') }, @@ -78,7 +79,8 @@ export default function loggingConfiguration(config, logReporter) { request: ['headers', 'payload'] }, reporters: { - logReporter: [logReporter.logInterceptor] + logReporter: [loggerStream] } }; + return options; } diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index aaf4cf65149db..1436dfbde51b2 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -19,31 +19,18 @@ import good from '@elastic/good'; import loggingConfiguration from './configuration'; -import { LogReporter } from './log_reporter'; import { logWithMetadata } from './log_with_metadata'; import { setupLoggingRotate } from './rotate'; export async function setupLogging(server, config) { - const logReporter = new LogReporter(); - - await server.register({ + setupLoggingRotate(config); + return await server.register({ plugin: good, - options: loggingConfiguration(config, logReporter) + options: loggingConfiguration(config) }); - setupLoggingRotate(config, logReporter); } export async function loggingMixin(kbnServer, server, config) { logWithMetadata.decorateServer(server); - - console.log(process.pid); - - setInterval(()=> { - server.logWithMetadata( - ['info', 'LOG'], - 'TEST LOG' - ); - }, 10000); - return await setupLogging(server, config); } diff --git a/src/legacy/server/logging/log_reporter.js b/src/legacy/server/logging/log_reporter.js index f0d432104d39a..3a1b421a48c2f 100644 --- a/src/legacy/server/logging/log_reporter.js +++ b/src/legacy/server/logging/log_reporter.js @@ -24,38 +24,29 @@ import LogFormatJson from './log_format_json'; import LogFormatString from './log_format_string'; import { LogInterceptor } from './log_interceptor'; -export class LogReporter { - constructor() { - this.squeeze = null; - this.format = null; - this.logInterceptor = null; - this.dest = null; - this.formattedLogStream = null; - } +export function getLoggerStream({ events, config }) { + const squeeze = new Squeeze(events); + const format = config.json ? new LogFormatJson(config) : new LogFormatString(config); + const logInterceptor = new LogInterceptor(); - configLogReporter({ events, config }) { - this.squeeze = new Squeeze(events); - this.format = config.json ? new LogFormatJson(config) : new LogFormatString(config); - this.logInterceptor = new LogInterceptor(); - this.dest = null; - this.formattedLogStream = this.logInterceptor - .pipe(this.squeeze) - .pipe(this.format); + let dest; + if (config.dest === 'stdout') { + dest = process.stdout; + } else { + dest = writeStr(config.dest, { + flags: 'a', + encoding: 'utf8' + }); - if (config.dest === 'stdout') { - this.dest = process.stdout; - } else { - this.dest = writeStr(config.dest, { - flags: 'a', - encoding: 'utf8' - }); + logInterceptor.on('end', () => { + dest.end(); + }); + } - this.logInterceptor.on('end', () => { - this.dest.end(); - }); - } + logInterceptor + .pipe(squeeze) + .pipe(format) + .pipe(dest); - this.formattedLogStream - .pipe(this.dest); - } + return logInterceptor; } diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 5d59c94a31bf0..184552cd9349e 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -17,17 +17,12 @@ * under the License. */ +import { isMaster } from 'cluster'; import { LogRotator } from './log_rotator'; let logRotator = null; -export function setupLoggingRotate(config, logReporter) { - // We just want to start the logging rotate service once - // and we choose to use the worker server type for it - if (process.env.kbnWorkerType !== 'server') { - return; - } - +export function setupLoggingRotate(config) { // If log rotate is not enabled we skip if (!config.get('logging.rotate.enable')) { return; @@ -43,12 +38,20 @@ export function setupLoggingRotate(config, logReporter) { return; } + // We just want to start the logging rotate service once + // and we choose to use the master from the cluster + if (!isMaster) { + return; + } + // Enable Logging Rotate Service + // We need the master process and it can + // try to setupLoggingRotate more than once, + // so we'll need to assure it only loads once. if (!logRotator) { - logRotator = new LogRotator(config, logReporter.formattedLogStream); + logRotator = new LogRotator(config); + logRotator.start(); } - logRotator.stop(); - logRotator.start(); return logRotator; } diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index bb6fbf280e8df..e6d9a948a07b3 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -17,26 +17,28 @@ * under the License. */ +import * as chokidar from 'chokidar'; +import { workers } from 'cluster'; import fs from 'fs'; import { throttle } from 'lodash'; -import { basename, dirname, sep } from 'path'; +import { tmpdir } from 'os'; +import { basename, dirname, join, sep } from 'path'; export class LogRotator { - constructor(config, logReporterStream) { - this.logReporterStream = logReporterStream; + constructor(config) { this.logFilePath = config.get('logging.dest'); - this.interval = config.get('logging.rotate.interval'); + this.interval = 1; this.everyBytes = config.get('logging.rotate.everyBytes'); this.keepFiles = config.get('logging.rotate.keepFiles'); - this.onStartup = config.get('logging.rotate.onStartup'); this.running = false; this.logFileSize = 0; this.intervalID = 0; this.lastRotateTime = (new Date()).getTime(); this.isRotating = false; this.throttledRotate = throttle(() => { this._rotate(); }, 5000); - console.log(process.ppid); - console.log(process.pid); + this.stalker = null; + this.usePolling = true; + this.stalkerUsePollingPolicyTestTimeout = null; } start() { @@ -47,15 +49,14 @@ export class LogRotator { // create exit listener for cleanup purposes this._createExitListener(); + // call rotate on startup + this._callRotateOnStartup(); + // init log file size monitor this._startLogFileSizeMonitor(); // init log file interval monitor - this._startLogFileIntervalMonitor(); - - // call rotate in case we should try - // to rotate on startup - this._callRotateOnStartup(); + // this._startLogFileIntervalMonitor(); this.running = true; } @@ -72,27 +73,81 @@ export class LogRotator { this._stopLogFileSizeMonitor(); // stop log file interval monitor - this._stopLogFileIntervalMonitor(); + // this._stopLogFileIntervalMonitor(); this.running = false; } _startLogFileSizeMonitor() { - this.logFileSize = this._getLogFileSize(); - this.logReporterStream.on('data', this._logFileSizeMonitorHandler.bind(this)); + // Setup a test file in order to try the fs env + // and understand if we need to usePolling or not + const tempFileDir = fs.mkdtempSync(tmpdir()); + const tempFile = join(tempFileDir, 'kbn_log_rotation_test_file.log'); + fs.writeFileSync(tempFile, ''); + + // setup watcher and wait for changes on test file + this.stalker = chokidar.watch(tempFile, this._buildWatchCfg()); + this.stalker.on('change', () => { + this.usePolling = false; + // Sometimes chokidar is emit an exception on close due to a bug + // so we need to do this to prevent it + setTimeout(()=> this._completeUsePollingPolicyTest(tempFile), 0); + }); + this.stalker.on('ready', () => fs.writeFileSync(tempFile, 'test')); + + // in case the watchers without usePolling do not work + // we fallback after 10s and start usePolling mechanism + this.stalkerUsePollingPolicyTestTimeout = setTimeout(() => this._completeUsePollingPolicyTest(tempFile), 10000); + } + + _completeUsePollingPolicyTest(tempFile) { + // if we have already call that function, return + if (!this.stalkerUsePollingPolicyTestTimeout) { + return; + } + + // clear the timeout anyway if we reach that far + clearTimeout(this.stalkerUsePollingPolicyTestTimeout); + this.stalkerUsePollingPolicyTestTimeout = null; + + // close the watcher for test file + // and delete it + this.stalker.close(); + fs.unlinkSync(tempFile); + + // start the real watcher for the log file + // with the correct usePolling option + this.stalker = chokidar.watch(this.logFilePath, this._buildWatchCfg(this.usePolling)); + this.stalker.on('change', this._logFileSizeMonitorHandler.bind(this)); } - _logFileSizeMonitorHandler(chunk) { - if (!chunk) { + _buildWatchCfg(usePolling = false) { + return { + ignoreInitial: true, + usePolling, + interval: 10000, + binaryInterval: 10000, + alwaysStat: true, + atomic: false + }; + } + + _logFileSizeMonitorHandler(filename, stats) { + if (!filename || !stats) { return; } - this.logFileSize += chunk.length || 0; + this.logFileSize += stats.size || 0; this.throttledRotate(); } _stopLogFileSizeMonitor() { - this.logReporterStream.removeListener('data', this._logFileIntervalMonitorHandler); + if (!this.stalker) { + return; + } + + this.stalker.close(); + clearTimeout(this.stalkerUsePollingPolicyTestTimeout); } _startLogFileIntervalMonitor() { @@ -134,9 +189,8 @@ export class LogRotator { } _callRotateOnStartup() { - if (this.onStartup) { - this._rotate(); - } + this.logFileSize = this._getLogFileSize(); + this._rotate(); } _shouldRotate() { @@ -189,19 +243,18 @@ export class LogRotator { // get rotated files metadata const rotatedFiles = this._readRotatedFilesMetadata(); - console.log(rotatedFiles); - // delete last file this._deleteLastRotatedFile(rotatedFiles); // rename all files to correct index + 1 + // and normalize this._renameRotatedFilesByOne(rotatedFiles); // rename + compress current log into 0 this._rotateCurrentLogFile(); // send SIGHUP to reload log configuration - process.kill(process.ppid, 'SIGHUP'); + this._sendReloadLogConfigSignal(); // Reset log file size this.logFileSize = 0; @@ -248,4 +301,12 @@ export class LogRotator { const newFilePath = `${this.logFilePath}.0`; fs.renameSync(this.logFilePath, newFilePath); } + + _sendReloadLogConfigSignal() { + process.kill(process.pid, 'SIGHUP'); + + Object.values(workers).forEach((worker) => { + process.kill(worker.process.pid, 'SIGHUP'); + }); + } } From 08285f570a2f03a74f8cbb695e127f6838aa4674 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 26 Oct 2019 01:36:29 +0100 Subject: [PATCH 14/73] fix(NA): lifecycle for the log rotator --- docs/setup/settings.asciidoc | 7 + src/legacy/server/config/schema.js | 4 +- src/legacy/server/logging/index.js | 2 +- src/legacy/server/logging/rotate/index.js | 4 +- .../server/logging/rotate/log_rotator.js | 151 ++++++++++-------- 5 files changed, 98 insertions(+), 70 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index a6f8c33805bbf..05f4f82cb7bd9 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -167,6 +167,13 @@ this option should be at least greater than 1024. on disk. Older files are deleted during log rotation. The default value is 7. The `logging.rotate.keepFiles` option has to be in the range of 2 to 1024 files. +`logging.rotate.pollingInterval:`:: *Default: 10* The number of seconds for the polling strategy in case +the `logging.rotate.usePolling` is enabled. That option option has to be in the range of 5 to 3600 files. + +`logging.rotate.usePolling:`:: *Default: false* By default we try to understand the best way to monitoring +the log file. However there is some systems where it could not be always accurate. In those cases, if needed, +the `polling` method could be used enabling that option. + `logging.silent:`:: *Default: false* Set the value of this setting to `true` to suppress all logging output. diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index d4f21b7967aa2..473fbbd66cfd5 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -141,7 +141,9 @@ export default () => Joi.object({ rotate: Joi.object().keys({ enable: Joi.boolean().default(false), everyBytes: Joi.number().greater(1024).default(10485760), - keepFiles: Joi.number().greater(2).less(1024).default(7) + keepFiles: Joi.number().greater(2).less(1024).default(7), + pollingInterval: Joi.number().greater(5).less(3600).default(10), + usePolling: Joi.boolean().default(false) }).default() }).default(), diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index 1436dfbde51b2..39964a5bf0a1a 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -23,7 +23,7 @@ import { logWithMetadata } from './log_with_metadata'; import { setupLoggingRotate } from './rotate'; export async function setupLogging(server, config) { - setupLoggingRotate(config); + await setupLoggingRotate(config); return await server.register({ plugin: good, options: loggingConfiguration(config) diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 184552cd9349e..20aeba1c87c7d 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -22,7 +22,7 @@ import { LogRotator } from './log_rotator'; let logRotator = null; -export function setupLoggingRotate(config) { +export async function setupLoggingRotate(config) { // If log rotate is not enabled we skip if (!config.get('logging.rotate.enable')) { return; @@ -50,7 +50,7 @@ export function setupLoggingRotate(config) { // so we'll need to assure it only loads once. if (!logRotator) { logRotator = new LogRotator(config); - logRotator.start(); + await logRotator.start(); } return logRotator; diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index e6d9a948a07b3..dda853d7bd57f 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -20,9 +20,17 @@ import * as chokidar from 'chokidar'; import { workers } from 'cluster'; import fs from 'fs'; -import { throttle } from 'lodash'; +import { once, throttle } from 'lodash'; import { tmpdir } from 'os'; import { basename, dirname, join, sep } from 'path'; +import { promisify } from 'util'; + +const mkdtempAsync = promisify(fs.mkdtemp); +const readdirAsync = promisify(fs.readdir); +const renameAsync = promisify(fs.rename); +const statAsync = promisify(fs.stat); +const unlinkAsync = promisify(fs.unlink); +const writeFileAsync = promisify(fs.writeFile); export class LogRotator { constructor(config) { @@ -35,13 +43,14 @@ export class LogRotator { this.intervalID = 0; this.lastRotateTime = (new Date()).getTime(); this.isRotating = false; - this.throttledRotate = throttle(() => { this._rotate(); }, 5000); + this.throttledRotate = throttle(async () => { await this._rotate(); }, 5000); this.stalker = null; - this.usePolling = true; + this.usePolling = config.get('logging.rotate.usePolling'); + this.pollingInterval = config.get('logging.rotate.pollingInterval') * 1000; this.stalkerUsePollingPolicyTestTimeout = null; } - start() { + async start() { if (this.running) { return; } @@ -50,10 +59,10 @@ export class LogRotator { this._createExitListener(); // call rotate on startup - this._callRotateOnStartup(); + await this._callRotateOnStartup(); // init log file size monitor - this._startLogFileSizeMonitor(); + await this._startLogFileSizeMonitor(); // init log file interval monitor // this._startLogFileIntervalMonitor(); @@ -78,67 +87,73 @@ export class LogRotator { this.running = false; } - _startLogFileSizeMonitor() { + async _shouldUsePolling() { // Setup a test file in order to try the fs env // and understand if we need to usePolling or not - const tempFileDir = fs.mkdtempSync(tmpdir()); + const tempFileDir = await mkdtempAsync(tmpdir()); const tempFile = join(tempFileDir, 'kbn_log_rotation_test_file.log'); - fs.writeFileSync(tempFile, ''); - - // setup watcher and wait for changes on test file - this.stalker = chokidar.watch(tempFile, this._buildWatchCfg()); - this.stalker.on('change', () => { - this.usePolling = false; - // Sometimes chokidar is emit an exception on close due to a bug - // so we need to do this to prevent it - setTimeout(()=> this._completeUsePollingPolicyTest(tempFile), 0); - }); - this.stalker.on('ready', () => fs.writeFileSync(tempFile, 'test')); + await writeFileAsync(tempFile, ''); - // in case the watchers without usePolling do not work - // we fallback after 10s and start usePolling mechanism - this.stalkerUsePollingPolicyTestTimeout = setTimeout(() => this._completeUsePollingPolicyTest(tempFile), 10000); - } + try { + const testWatcher = fs.watch(tempFile, { persistent: false }); - _completeUsePollingPolicyTest(tempFile) { - // if we have already call that function, return - if (!this.stalkerUsePollingPolicyTestTimeout) { - return; - } + return new Promise(async (resolve) => { + let fallbackTimeout = null; - // clear the timeout anyway if we reach that far - clearTimeout(this.stalkerUsePollingPolicyTestTimeout); - this.stalkerUsePollingPolicyTestTimeout = null; + const onResolve = once(async (resolve, completeStatus) => { + clearTimeout(fallbackTimeout); - // close the watcher for test file - // and delete it - this.stalker.close(); - fs.unlinkSync(tempFile); + testWatcher.close(); + await unlinkAsync(tempFile); + resolve(completeStatus); - // start the real watcher for the log file - // with the correct usePolling option - this.stalker = chokidar.watch(this.logFilePath, this._buildWatchCfg(this.usePolling)); - this.stalker.on('change', this._logFileSizeMonitorHandler.bind(this)); + return true; + }); + + fallbackTimeout = setTimeout(async () => { + await onResolve(resolve, true); + }, 15000); + + testWatcher.on('change', async () => { + await onResolve(resolve, false); + }); + + await writeFileAsync(tempFile, 'test'); + + }); + } catch { + return false; + } } _buildWatchCfg(usePolling = false) { return { ignoreInitial: true, + awaitWriteFinish: false, + useFsEvents: false, usePolling, - interval: 10000, - binaryInterval: 10000, + interval: this.pollingInterval, + binaryInterval: this.pollingInterval, alwaysStat: true, atomic: false }; } - _logFileSizeMonitorHandler(filename, stats) { + async _startLogFileSizeMonitor() { + this.usePolling = await this._shouldUsePolling(); + this.stalker = chokidar.watch(this.logFilePath, this._buildWatchCfg(this.usePolling)); + this.stalker.on('change', async (filename, stats) => await this._logFileSizeMonitorHandler.bind(this)(filename, stats)); + } + + async _logFileSizeMonitorHandler(filename, stats) { + console.log('MONITOR'); + if (!filename || !stats) { return; } this.logFileSize += stats.size || 0; - this.throttledRotate(); + await this.throttledRotate(); } _stopLogFileSizeMonitor() { @@ -158,8 +173,8 @@ export class LogRotator { this.intervalID = setInterval(this._logFileIntervalMonitorHandler.bind(this), this.interval * 60 * 1000); } - _logFileIntervalMonitorHandler() { - this._rotate(); + async _logFileIntervalMonitorHandler() { + await this._rotate(); } _stopLogFileIntervalMonitor() { @@ -179,18 +194,21 @@ export class LogRotator { process.removeListener('exit', this.stop); } - _getLogFileSize() { + async _getLogFileSizeAndCreateIfNeeded() { try { - const logFileStats = fs.statSync(this.logFilePath); + const logFileStats = await statAsync(this.logFilePath); return logFileStats.size; } catch { + // touch the file to make the watcher being able to register + // change events + await writeFileAsync(this.logFilePath, ''); return 0; } } - _callRotateOnStartup() { - this.logFileSize = this._getLogFileSize(); - this._rotate(); + async _callRotateOnStartup() { + this.logFileSize = await this._getLogFileSizeAndCreateIfNeeded(); + await this._rotate(); } _shouldRotate() { @@ -212,10 +230,11 @@ export class LogRotator { const elapsedTime = Math.round((currentTime - this.lastRotateTime) / 1000); console.log('elapsedTime: ' + elapsedTime + ' interval: ' + this.interval * 60); - return elapsedTime >= this.interval * 30; + // return elapsedTime >= this.interval * 30; + return false; } - _rotate() { + async _rotate() { console.log('logSize: ' + this.logFileSize + ' limit: ' + this.everyBytes); if (!this._shouldRotate()) { console.log('no'); @@ -223,10 +242,10 @@ export class LogRotator { } console.log('yes'); - this._rotateNow(); + await this._rotateNow(); } - _rotateNow() { + async _rotateNow() { // rotate process // 1. get rotated files metadata (list of log rotated files present on the log folder, numerical sorted) // 2. delete last file @@ -241,17 +260,17 @@ export class LogRotator { this.isRotating = true; // get rotated files metadata - const rotatedFiles = this._readRotatedFilesMetadata(); + const rotatedFiles = await this._readRotatedFilesMetadata(); // delete last file - this._deleteLastRotatedFile(rotatedFiles); + await this._deleteLastRotatedFile(rotatedFiles); // rename all files to correct index + 1 // and normalize - this._renameRotatedFilesByOne(rotatedFiles); + await this._renameRotatedFilesByOne(rotatedFiles); // rename + compress current log into 0 - this._rotateCurrentLogFile(); + await this._rotateCurrentLogFile(); // send SIGHUP to reload log configuration this._sendReloadLogConfigSignal(); @@ -266,40 +285,40 @@ export class LogRotator { this.isRotating = false; } - _readRotatedFilesMetadata() { + async _readRotatedFilesMetadata() { const logFileBaseName = basename(this.logFilePath); const logFilesFolder = dirname(this.logFilePath); - return fs.readdirSync(logFilesFolder) + return (await readdirAsync(logFilesFolder)) .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) .sort((a, b) => Number(a.match(/(\d+)/g)[0]) - Number(b.match(/(\d+)/g)[0])) .map(filename => `${logFilesFolder}${sep}${filename}`) .filter(filepath => fs.existsSync(filepath)); } - _deleteLastRotatedFile(rotatedFiles) { + async _deleteLastRotatedFile(rotatedFiles) { if (rotatedFiles.length < this.keepFiles) { return; } const lastFilePath = rotatedFiles.pop(); - fs.unlinkSync(lastFilePath); + await unlinkAsync(lastFilePath); } - _renameRotatedFilesByOne(rotatedFiles) { + async _renameRotatedFilesByOne(rotatedFiles) { const logFileBaseName = basename(this.logFilePath); const logFilesFolder = dirname(this.logFilePath); for (let i = rotatedFiles.length - 1; i >= 0; i--) { const oldFilePath = rotatedFiles[i]; const newFilePath = `${logFilesFolder}${sep}${logFileBaseName}.${i + 1}`; - fs.renameSync(oldFilePath, newFilePath); + await renameAsync(oldFilePath, newFilePath); } } - _rotateCurrentLogFile() { + async _rotateCurrentLogFile() { const newFilePath = `${this.logFilePath}.0`; - fs.renameSync(this.logFilePath, newFilePath); + await renameAsync(this.logFilePath, newFilePath); } _sendReloadLogConfigSignal() { From efb64aaca713f0f3c28335033e6708b8642f993d Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 30 Oct 2019 15:25:12 +0000 Subject: [PATCH 15/73] test(NA): add a test case --- .../server/logging/rotate/log_rotator.js | 49 +-------------- .../server/logging/rotate/log_rotator.test.js | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 48 deletions(-) create mode 100644 src/legacy/server/logging/rotate/log_rotator.test.js diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index dda853d7bd57f..055f124253be5 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -40,8 +40,6 @@ export class LogRotator { this.keepFiles = config.get('logging.rotate.keepFiles'); this.running = false; this.logFileSize = 0; - this.intervalID = 0; - this.lastRotateTime = (new Date()).getTime(); this.isRotating = false; this.throttledRotate = throttle(async () => { await this._rotate(); }, 5000); this.stalker = null; @@ -64,9 +62,6 @@ export class LogRotator { // init log file size monitor await this._startLogFileSizeMonitor(); - // init log file interval monitor - // this._startLogFileIntervalMonitor(); - this.running = true; } @@ -81,9 +76,6 @@ export class LogRotator { // stop log file size monitor this._stopLogFileSizeMonitor(); - // stop log file interval monitor - // this._stopLogFileIntervalMonitor(); - this.running = false; } @@ -119,7 +111,6 @@ export class LogRotator { }); await writeFileAsync(tempFile, 'test'); - }); } catch { return false; @@ -146,8 +137,6 @@ export class LogRotator { } async _logFileSizeMonitorHandler(filename, stats) { - console.log('MONITOR'); - if (!filename || !stats) { return; } @@ -165,27 +154,6 @@ export class LogRotator { clearTimeout(this.stalkerUsePollingPolicyTestTimeout); } - _startLogFileIntervalMonitor() { - if (!this.interval) { - return; - } - - this.intervalID = setInterval(this._logFileIntervalMonitorHandler.bind(this), this.interval * 60 * 1000); - } - - async _logFileIntervalMonitorHandler() { - await this._rotate(); - } - - _stopLogFileIntervalMonitor() { - if (!this.intervalID) { - return; - } - - clearInterval(this.intervalID); - this.intervalID = 0; - } - _createExitListener() { process.on('exit', this.stop.bind(this)); } @@ -222,26 +190,14 @@ export class LogRotator { return false; } - if (this.logFileSize >= this.everyBytes) { - return true; - } - - const currentTime = (new Date()).getTime(); - const elapsedTime = Math.round((currentTime - this.lastRotateTime) / 1000); - - console.log('elapsedTime: ' + elapsedTime + ' interval: ' + this.interval * 60); - // return elapsedTime >= this.interval * 30; - return false; + return this.logFileSize >= this.everyBytes; } async _rotate() { - console.log('logSize: ' + this.logFileSize + ' limit: ' + this.everyBytes); if (!this._shouldRotate()) { - console.log('no'); return; } - console.log('yes'); await this._rotateNow(); } @@ -278,9 +234,6 @@ export class LogRotator { // Reset log file size this.logFileSize = 0; - // Reset last rotate time - this.lastRotateTime = (new Date()).getTime(); - // rotate process is finished this.isRotating = false; } diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js new file mode 100644 index 0000000000000..4d47afd3b6998 --- /dev/null +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import del from 'del'; +import { existsSync, mkdtempSync, writeFileSync } from 'fs'; +import { LogRotator } from './log_rotator'; +import { tmpdir } from 'os'; +import { dirname, join } from 'path'; + +let testFilePath = null; + +const createLogRotatorConfig = (logFilePath) => { + return new Map([ + ['logging.dest', logFilePath], + ['logging.rotate.everyBytes', 2], + ['logging.rotate.keepFiles', 7], + ['logging.rotate.usePolling', false], + ['logging.rotate.pollingInterval', 10] + ]); +}; + +describe('LogRotator', () => { + beforeEach(() => { + testFilePath = join(mkdtempSync(tmpdir()), 'log_rotator_test_log_file.log'); + console.log(testFilePath); + writeFileSync(testFilePath, ''); + }); + + afterEach(() => { + del.sync(dirname(testFilePath), { force: true }); + }); + + it('rotates log file when bigger than set limit on start', async () => { + writeFileSync(testFilePath, 'more than 2 bytes'); + + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + await logRotator.start(); + + const testLogFileDir = dirname(testFilePath); + expect(logRotator.running).toBe(true); + + await logRotator.stop(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); + }); +}); From 5b8b46a5a64591a68ee2e0eca95d790d811f2c46 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 30 Oct 2019 15:43:28 +0000 Subject: [PATCH 16/73] chore(NA): correctly add the new defaults to the config schema --- config/kibana.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 704bae4ac3f8a..3858ca1663297 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -91,11 +91,11 @@ #pid.file: /var/run/kibana.pid # Enables you specify a file where Kibana stores log output. -logging.dest: /Users/tiagocosta/Desktop/merd/kibana.log +#logging.dest: stdout # Enable you to turn on the logging rotate feature. # Even enabled, the log rotate will only take effect if you specify a file for 'logging.dest'. -logging.rotate.enable: true +#logging.rotate.enable: false # Set the value of this setting to true to suppress all logging output. #logging.silent: false From 2d882ba78bbf4fe06747a96ceeac0a3831c50819 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 30 Oct 2019 17:38:20 +0000 Subject: [PATCH 17/73] test(NA): change dir generation for test --- src/legacy/server/logging/rotate/log_rotator.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index 4d47afd3b6998..68b5c114b6b4e 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -37,8 +37,8 @@ const createLogRotatorConfig = (logFilePath) => { describe('LogRotator', () => { beforeEach(() => { - testFilePath = join(mkdtempSync(tmpdir()), 'log_rotator_test_log_file.log'); - console.log(testFilePath); + const tempDir = mkdtempSync(join(tmpdir(), 'log_rotator_test')); + testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); writeFileSync(testFilePath, ''); }); From 126b7c7c2be9cb0e043ca080831f69ad4db3d562 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 30 Oct 2019 19:21:44 +0000 Subject: [PATCH 18/73] chore(NA): mock log rotate for logging service test --- src/core/server/logging/logging_service.test.ts | 4 ++++ src/legacy/server/logging/rotate/log_rotator.js | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/core/server/logging/logging_service.test.ts b/src/core/server/logging/logging_service.test.ts index 380488ff9f62d..14a9e31a18702 100644 --- a/src/core/server/logging/logging_service.test.ts +++ b/src/core/server/logging/logging_service.test.ts @@ -23,6 +23,10 @@ jest.mock('fs', () => ({ createWriteStream: jest.fn(() => ({ write: mockStreamWrite })), })); +jest.mock('../../../legacy/server/logging/rotate', () => ({ + setupLoggingRotate: jest.fn(), +})); + const timestamp = new Date(Date.UTC(2012, 1, 1)); let mockConsoleLog: jest.SpyInstance; diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 055f124253be5..743e08ebe27a7 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -150,6 +150,8 @@ export class LogRotator { return; } + console.log('AQUIIUIUIUIU'); + this.stalker.close(); clearTimeout(this.stalkerUsePollingPolicyTestTimeout); } From 0f7f47d8aa748ee22c505e6e9c4fadf03a842595 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 30 Oct 2019 20:15:56 +0000 Subject: [PATCH 19/73] test(NA): fix temp dir permission issue --- src/legacy/server/logging/rotate/log_rotator.js | 2 -- src/legacy/server/logging/rotate/log_rotator.test.js | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 743e08ebe27a7..055f124253be5 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -150,8 +150,6 @@ export class LogRotator { return; } - console.log('AQUIIUIUIUIU'); - this.stalker.close(); clearTimeout(this.stalkerUsePollingPolicyTestTimeout); } diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index 68b5c114b6b4e..5403ba8cb59e8 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -18,12 +18,13 @@ */ import del from 'del'; -import { existsSync, mkdtempSync, writeFileSync } from 'fs'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { LogRotator } from './log_rotator'; import { tmpdir } from 'os'; -import { dirname, join } from 'path'; +import path, { dirname, join } from 'path'; -let testFilePath = null; +const tempDir = path.join(tmpdir(), 'kbn_log_rotator_test'); +const testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); const createLogRotatorConfig = (logFilePath) => { return new Map([ @@ -37,8 +38,7 @@ const createLogRotatorConfig = (logFilePath) => { describe('LogRotator', () => { beforeEach(() => { - const tempDir = mkdtempSync(join(tmpdir(), 'log_rotator_test')); - testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); + mkdirSync(tempDir, { recursive: true }); writeFileSync(testFilePath, ''); }); From bf7282323985305666b2ec89b22f94fcbc085ea4 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 30 Oct 2019 22:43:28 +0000 Subject: [PATCH 20/73] test(NA): try to fix test --- src/legacy/server/logging/rotate/log_rotator.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index 5403ba8cb59e8..0975876a5b981 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -20,10 +20,10 @@ import del from 'del'; import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { LogRotator } from './log_rotator'; -import { tmpdir } from 'os'; +import os from 'os'; import path, { dirname, join } from 'path'; -const tempDir = path.join(tmpdir(), 'kbn_log_rotator_test'); +const tempDir = path.join(os.tmpdir(), 'kbn_log_rotator_test'); const testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); const createLogRotatorConfig = (logFilePath) => { From 22d41152a6dcc13d0a05880b0f14d70827de391c Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 31 Oct 2019 02:50:42 +0000 Subject: [PATCH 21/73] chore(NA): remove usage of mkdtemp --- src/legacy/server/logging/rotate/log_rotator.js | 5 +++-- src/legacy/server/logging/rotate/log_rotator.test.js | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 055f124253be5..e1581c3f60fba 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -25,7 +25,7 @@ import { tmpdir } from 'os'; import { basename, dirname, join, sep } from 'path'; import { promisify } from 'util'; -const mkdtempAsync = promisify(fs.mkdtemp); +const mkdirAsync = promisify(fs.mkdir); const readdirAsync = promisify(fs.readdir); const renameAsync = promisify(fs.rename); const statAsync = promisify(fs.stat); @@ -82,7 +82,8 @@ export class LogRotator { async _shouldUsePolling() { // Setup a test file in order to try the fs env // and understand if we need to usePolling or not - const tempFileDir = await mkdtempAsync(tmpdir()); + const tempFileDir = tmpdir(); + await mkdirAsync(tempFileDir, { recursive: true }); const tempFile = join(tempFileDir, 'kbn_log_rotation_test_file.log'); await writeFileAsync(tempFile, ''); diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index 0975876a5b981..5403ba8cb59e8 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -20,10 +20,10 @@ import del from 'del'; import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { LogRotator } from './log_rotator'; -import os from 'os'; +import { tmpdir } from 'os'; import path, { dirname, join } from 'path'; -const tempDir = path.join(os.tmpdir(), 'kbn_log_rotator_test'); +const tempDir = path.join(tmpdir(), 'kbn_log_rotator_test'); const testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); const createLogRotatorConfig = (logFilePath) => { From 3ce125db8be24b21b3d16aed0b499b81b64dcb9a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 2 Nov 2019 14:53:33 +0000 Subject: [PATCH 22/73] refact(NA): feature logging rotation reimplementation in order to make it work across platforms --- src/cli/cluster/cluster_manager.js | 11 +++++++++++ src/cli/cluster/worker.js | 3 +++ src/core/server/bootstrap.ts | 13 +++++++++++-- src/core/server/logging/logging_service.test.ts | 2 +- src/legacy/server/logging/index.js | 4 ++-- src/legacy/server/logging/rotate/index.js | 10 +++++----- src/legacy/server/logging/rotate/log_rotator.js | 13 ++++++++----- 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/cli/cluster/cluster_manager.js b/src/cli/cluster/cluster_manager.js index a67593c02a593..9a2109d8f3c0e 100644 --- a/src/cli/cluster/cluster_manager.js +++ b/src/cli/cluster/cluster_manager.js @@ -90,6 +90,17 @@ export default class ClusterManager { }); }); + // when receive that event from server worker + // forward a reloadLoggingConfig message to master + // and all workers. This is only used by LogRotator service + // when the cluster mode is enabled + this.server.on('reloadLoggingConfigFromServerWorker', () => { + process.emit('message', { reloadLoggingConfig: true }); + this.workers.forEach(worker => { + worker.fork.send({ reloadLoggingConfig: true }); + }); + }); + bindAll(this, 'onWatcherAdd', 'onWatcherError', 'onWatcherChange'); if (opts.open) { diff --git a/src/cli/cluster/worker.js b/src/cli/cluster/worker.js index 8eca55a08af2c..69a123705600f 100644 --- a/src/cli/cluster/worker.js +++ b/src/cli/cluster/worker.js @@ -133,6 +133,9 @@ export default class Worker extends EventEmitter { this.listening = true; this.emit('listening'); break; + case 'RELOAD_LOGGING_CONFIG_FROM_SERVER_WORKER': + this.emit('reloadLoggingConfigFromServerWorker'); + break; } } diff --git a/src/core/server/bootstrap.ts b/src/core/server/bootstrap.ts index 2dff4430b4dbe..f2a682ae39ee4 100644 --- a/src/core/server/bootstrap.ts +++ b/src/core/server/bootstrap.ts @@ -70,7 +70,16 @@ export async function bootstrap({ const root = new Root(rawConfigService.getConfig$(), env, onRootShutdown); - process.on('SIGHUP', () => { + process.on('SIGHUP', () => reloadLoggingConfig()); + + // This is only used by the LogRotator service + process.on('message', msg => { + if (msg && msg.reloadLoggingConfig) { + reloadLoggingConfig(); + } + }); + + function reloadLoggingConfig() { const cliLogger = root.logger.get('cli'); cliLogger.info('Reloading logging configuration due to SIGHUP.', { tags: ['config'] }); @@ -81,7 +90,7 @@ export async function bootstrap({ } cliLogger.info('Reloaded logging configuration due to SIGHUP.', { tags: ['config'] }); - }); + } process.on('SIGINT', () => shutdown()); process.on('SIGTERM', () => shutdown()); diff --git a/src/core/server/logging/logging_service.test.ts b/src/core/server/logging/logging_service.test.ts index 14a9e31a18702..c58103cca5f8d 100644 --- a/src/core/server/logging/logging_service.test.ts +++ b/src/core/server/logging/logging_service.test.ts @@ -24,7 +24,7 @@ jest.mock('fs', () => ({ })); jest.mock('../../../legacy/server/logging/rotate', () => ({ - setupLoggingRotate: jest.fn(), + setupLoggingRotate: jest.fn().mockImplementation(() => Promise.resolve({})), })); const timestamp = new Date(Date.UTC(2012, 1, 1)); diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index 39964a5bf0a1a..8c61c11409206 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -23,7 +23,6 @@ import { logWithMetadata } from './log_with_metadata'; import { setupLoggingRotate } from './rotate'; export async function setupLogging(server, config) { - await setupLoggingRotate(config); return await server.register({ plugin: good, options: loggingConfiguration(config) @@ -32,5 +31,6 @@ export async function setupLogging(server, config) { export async function loggingMixin(kbnServer, server, config) { logWithMetadata.decorateServer(server); - return await setupLogging(server, config); + await setupLogging(server, config); + return await setupLoggingRotate(server, config); } diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 20aeba1c87c7d..d8a2b2b969883 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -17,12 +17,12 @@ * under the License. */ -import { isMaster } from 'cluster'; +import { isMaster, isWorker } from 'cluster'; import { LogRotator } from './log_rotator'; let logRotator = null; -export async function setupLoggingRotate(config) { +export async function setupLoggingRotate(server, config) { // If log rotate is not enabled we skip if (!config.get('logging.rotate.enable')) { return; @@ -31,7 +31,7 @@ export async function setupLoggingRotate(config) { // We don't want to run logging rotate server if // we are not logging to a file if (config.get('logging.rotate.enable') && config.get('logging.dest') === 'stdout') { - this.logWithMetadata( + server.logWithMetadata( ['warning', 'logging:rotate'], 'Logging rotate is enabled but logging.dest is configured for stdout. The logging rotate will take no action.' ); @@ -39,8 +39,8 @@ export async function setupLoggingRotate(config) { } // We just want to start the logging rotate service once - // and we choose to use the master from the cluster - if (!isMaster) { + // and we choose to use the master (prod) or the worker server (dev) + if (!isMaster && isWorker && process.env.kbnWorkerType !== 'server') { return; } diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index e1581c3f60fba..bf04e9a0371e3 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -18,7 +18,7 @@ */ import * as chokidar from 'chokidar'; -import { workers } from 'cluster'; +import { isMaster } from 'cluster'; import fs from 'fs'; import { once, throttle } from 'lodash'; import { tmpdir } from 'os'; @@ -276,10 +276,13 @@ export class LogRotator { } _sendReloadLogConfigSignal() { - process.kill(process.pid, 'SIGHUP'); + if (isMaster) { + process.emit('SIGHUP'); + return; + } - Object.values(workers).forEach((worker) => { - process.kill(worker.process.pid, 'SIGHUP'); - }); + // Send a special message to the cluster manager + // so it can forward it correctly + process.send(['RELOAD_LOGGING_CONFIG_FROM_SERVER_WORKER']); } } From de896b9809a5ed67f2047b61101da2c700ff8f7a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 4 Nov 2019 17:56:34 -0500 Subject: [PATCH 23/73] fix(NA): bug on file size monitor handle --- config/kibana.yml | 4 ---- docs/setup/settings.asciidoc | 3 ++- src/legacy/server/logging/rotate/log_rotator.js | 5 +++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 3858ca1663297..47482f9e6d59f 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -93,10 +93,6 @@ # Enables you specify a file where Kibana stores log output. #logging.dest: stdout -# Enable you to turn on the logging rotate feature. -# Even enabled, the log rotate will only take effect if you specify a file for 'logging.dest'. -#logging.rotate.enable: false - # Set the value of this setting to true to suppress all logging output. #logging.silent: false diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 7b1e8c1e1bb2d..b6ab6f095cd66 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -157,7 +157,8 @@ The following example shows a valid logging rotate configuration: -- `logging.rotate.enable:`:: *Default: false* Set the value of this setting to `true` to -enable logging rotate. +enable logging rotate. If you do not have a `logging.dest` set that is different from `stdout` +that feature would not take any effect. `logging.rotate.everyBytes:`:: *Default: 10485760* The maximum size of a log file. If the limit is reached, a new log file is generated. The default size limit is 10485760 (10 MB) and diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index bf04e9a0371e3..e4900ae51b2a7 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -142,7 +142,7 @@ export class LogRotator { return; } - this.logFileSize += stats.size || 0; + this.logFileSize = stats.size || 0; await this.throttledRotate(); } @@ -191,7 +191,8 @@ export class LogRotator { return false; } - return this.logFileSize >= this.everyBytes; + // return this.logFileSize >= this.everyBytes; + return this.logFileSize >= 25000; } async _rotate() { From 11917b73b9034705c0dadaa27aec0151e86f1822 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 4 Nov 2019 17:57:37 -0500 Subject: [PATCH 24/73] fix(NA): remove wrong commented out code --- src/legacy/server/logging/rotate/log_rotator.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index e4900ae51b2a7..50b630197ca6d 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -191,8 +191,7 @@ export class LogRotator { return false; } - // return this.logFileSize >= this.everyBytes; - return this.logFileSize >= 25000; + return this.logFileSize >= this.everyBytes; } async _rotate() { From 4cc2328243004dcd7d61ae2663643d66af13470a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 6 Nov 2019 15:20:28 -0500 Subject: [PATCH 25/73] chore(NA): correctly identify if we should use polling --- src/legacy/server/logging/rotate/log_rotator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 50b630197ca6d..f5b24bb4fb0b9 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -114,7 +114,7 @@ export class LogRotator { await writeFileAsync(tempFile, 'test'); }); } catch { - return false; + return true; } } From b7a7bc1df7bf96a40fd213cc531d99bbb658ffaa Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 6 Nov 2019 15:31:27 -0500 Subject: [PATCH 26/73] chore(NA): fix some code comment --- src/legacy/server/logging/rotate/log_rotator.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index f5b24bb4fb0b9..fb817dd5061e7 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -84,6 +84,7 @@ export class LogRotator { // and understand if we need to usePolling or not const tempFileDir = tmpdir(); await mkdirAsync(tempFileDir, { recursive: true }); + const tempFile = join(tempFileDir, 'kbn_log_rotation_test_file.log'); await writeFileAsync(tempFile, ''); @@ -99,8 +100,6 @@ export class LogRotator { testWatcher.close(); await unlinkAsync(tempFile); resolve(completeStatus); - - return true; }); fallbackTimeout = setTimeout(async () => { From d6173ad5dfdc15c3ef79db0db01af86bd694e4f7 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 7 Nov 2019 10:40:27 -0500 Subject: [PATCH 27/73] refact(NA): minor implementation details --- src/cli/cluster/cluster_manager.js | 3 ++- src/core/server/bootstrap.ts | 8 ++++++-- src/legacy/server/logging/rotate/log_rotator.js | 15 ++++++++------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/cli/cluster/cluster_manager.js b/src/cli/cluster/cluster_manager.js index 9a2109d8f3c0e..9859f2e936d1b 100644 --- a/src/cli/cluster/cluster_manager.js +++ b/src/cli/cluster/cluster_manager.js @@ -90,12 +90,13 @@ export default class ClusterManager { }); }); - // when receive that event from server worker + // When receive that event from server worker // forward a reloadLoggingConfig message to master // and all workers. This is only used by LogRotator service // when the cluster mode is enabled this.server.on('reloadLoggingConfigFromServerWorker', () => { process.emit('message', { reloadLoggingConfig: true }); + this.workers.forEach(worker => { worker.fork.send({ reloadLoggingConfig: true }); }); diff --git a/src/core/server/bootstrap.ts b/src/core/server/bootstrap.ts index f2a682ae39ee4..e54e81e98dc00 100644 --- a/src/core/server/bootstrap.ts +++ b/src/core/server/bootstrap.ts @@ -73,10 +73,14 @@ export async function bootstrap({ process.on('SIGHUP', () => reloadLoggingConfig()); // This is only used by the LogRotator service + // in order to be able to reload the log configuration + // under the cluster mode process.on('message', msg => { - if (msg && msg.reloadLoggingConfig) { - reloadLoggingConfig(); + if (!msg || msg.reloadLoggingConfig !== true) { + return; } + + reloadLoggingConfig(); }); function reloadLoggingConfig() { diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index fb817dd5061e7..06ded196c3e41 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -80,15 +80,15 @@ export class LogRotator { } async _shouldUsePolling() { - // Setup a test file in order to try the fs env - // and understand if we need to usePolling or not - const tempFileDir = tmpdir(); - await mkdirAsync(tempFileDir, { recursive: true }); + try { + // Setup a test file in order to try the fs env + // and understand if we need to usePolling or not + const tempFileDir = tmpdir(); + const tempFile = join(tempFileDir, 'kbn_log_rotation_use_polling_test_file.log'); - const tempFile = join(tempFileDir, 'kbn_log_rotation_test_file.log'); - await writeFileAsync(tempFile, ''); + await mkdirAsync(tempFileDir, { recursive: true }); + await writeFileAsync(tempFile, ''); - try { const testWatcher = fs.watch(tempFile, { persistent: false }); return new Promise(async (resolve) => { @@ -282,6 +282,7 @@ export class LogRotator { // Send a special message to the cluster manager // so it can forward it correctly + // It will only run when we are under cluster mode (not under a production environment) process.send(['RELOAD_LOGGING_CONFIG_FROM_SERVER_WORKER']); } } From 8c8e01800d20a5bb51208218ebc468c2e3b045db Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 7 Nov 2019 10:47:18 -0500 Subject: [PATCH 28/73] chore(NA): change the order of logging mix --- src/legacy/server/logging/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index 8c61c11409206..8743c0ff6853f 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -31,6 +31,6 @@ export async function setupLogging(server, config) { export async function loggingMixin(kbnServer, server, config) { logWithMetadata.decorateServer(server); - await setupLogging(server, config); - return await setupLoggingRotate(server, config); + await setupLoggingRotate(server, config); + return await setupLogging(server, config); } From 8b26b753002e672971b8a69a21760e54b84aa564 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 7 Nov 2019 12:03:51 -0500 Subject: [PATCH 29/73] test(NA): add some more test cases --- .../server/logging/rotate/log_rotator.test.js | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index 5403ba8cb59e8..37d458bf1af1a 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -21,9 +21,9 @@ import del from 'del'; import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { LogRotator } from './log_rotator'; import { tmpdir } from 'os'; -import path, { dirname, join } from 'path'; +import { dirname, join } from 'path'; -const tempDir = path.join(tmpdir(), 'kbn_log_rotator_test'); +const tempDir = join(tmpdir(), 'kbn_log_rotator_test'); const testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); const createLogRotatorConfig = (logFilePath) => { @@ -36,6 +36,10 @@ const createLogRotatorConfig = (logFilePath) => { ]); }; +const writeBytesToFile = (filePath, numberOfBytes) => { + writeFileSync(filePath, 'a'.repeat(numberOfBytes), { flag: 'a' }); +}; + describe('LogRotator', () => { beforeEach(() => { mkdirSync(tempDir, { recursive: true }); @@ -47,16 +51,47 @@ describe('LogRotator', () => { }); it('rotates log file when bigger than set limit on start', async () => { - writeFileSync(testFilePath, 'more than 2 bytes'); + writeBytesToFile(testFilePath, 3); const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); await logRotator.start(); + expect(logRotator.running).toBe(true); + + await logRotator.stop(); const testLogFileDir = dirname(testFilePath); + + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); + }); + + it.skip('rotates log file when bigger than set limit over time', async () => { + writeBytesToFile(testFilePath, 1); + + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + await logRotator.start(); + expect(logRotator.running).toBe(true); + const testLogFileDir = dirname(testFilePath); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeFalsy(); + + writeBytesToFile(testFilePath, 3); + await logRotator.stop(); expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); }); + + it('rotates log file when file size is equal to the limit', async () => { + + }); + + it('rotates log file service correctly keeps number of files', async () => { + + }); + + it('rotates log file service correctly detects if it needs to use polling', async () => { + + }); }); From c1b5b70a5a53afeebb45cbddfe416889a12cf7fe Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 9 Nov 2019 21:22:13 -0500 Subject: [PATCH 30/73] test(NA): add the majority of the test cases --- .../server/logging/rotate/log_rotator.test.js | 73 +++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index 37d458bf1af1a..db4127ab1826a 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -18,11 +18,21 @@ */ import del from 'del'; -import { existsSync, mkdirSync, writeFileSync } from 'fs'; +import { existsSync, mkdirSync, statSync, writeFileSync } from 'fs'; +import lodash from 'lodash'; import { LogRotator } from './log_rotator'; import { tmpdir } from 'os'; import { dirname, join } from 'path'; + +const mockOn = jest.fn(); +jest.mock('chokidar', () => ({ + watch: jest.fn(() => ({ + on: mockOn, + close: jest.fn() + })), +})); + const tempDir = join(tmpdir(), 'kbn_log_rotator_test'); const testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); @@ -30,7 +40,7 @@ const createLogRotatorConfig = (logFilePath) => { return new Map([ ['logging.dest', logFilePath], ['logging.rotate.everyBytes', 2], - ['logging.rotate.keepFiles', 7], + ['logging.rotate.keepFiles', 2], ['logging.rotate.usePolling', false], ['logging.rotate.pollingInterval', 10] ]); @@ -48,6 +58,7 @@ describe('LogRotator', () => { afterEach(() => { del.sync(dirname(testFilePath), { force: true }); + mockOn.mockClear(); }); it('rotates log file when bigger than set limit on start', async () => { @@ -65,7 +76,7 @@ describe('LogRotator', () => { expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); }); - it.skip('rotates log file when bigger than set limit over time', async () => { + it('rotates log file when equal than set limit over time', async () => { writeBytesToFile(testFilePath, 1); const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); @@ -77,21 +88,71 @@ describe('LogRotator', () => { const testLogFileDir = dirname(testFilePath); expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeFalsy(); - writeBytesToFile(testFilePath, 3); + writeBytesToFile(testFilePath, 1); + + // ['change', [asyncFunction]] + const onChangeCb = mockOn.mock.calls[0][1]; + await onChangeCb(testLogFileDir, { size: 2 }); await logRotator.stop(); expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); }); - it('rotates log file when file size is equal to the limit', async () => { + it('rotates log file when file size is bigger than limit', async () => { + writeBytesToFile(testFilePath, 1); + + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + await logRotator.start(); + + expect(logRotator.running).toBe(true); + const testLogFileDir = dirname(testFilePath); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeFalsy(); + + writeBytesToFile(testFilePath, 2); + + // ['change', [asyncFunction]] + const onChangeCb = mockOn.mock.calls[0][1]; + await onChangeCb(testLogFileDir, { size: 3 }); + + await logRotator.stop(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); }); it('rotates log file service correctly keeps number of files', async () => { + jest.spyOn(lodash, 'throttle').mockImplementation(fn => fn); + + writeBytesToFile(testFilePath, 3); + + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + await logRotator.start(); + + expect(logRotator.running).toBe(true); + + const testLogFileDir = dirname(testFilePath); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); + + writeBytesToFile(testFilePath, 2); + + // ['change', [asyncFunction]] + const onChangeCb = mockOn.mock.calls[0][1]; + await onChangeCb(testLogFileDir, { size: 2 }); + + writeBytesToFile(testFilePath, 5); + await onChangeCb(testLogFileDir, { size: 5 }); + + await logRotator.stop(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.1'))).toBeTruthy(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.2'))).toBeFalsy(); + expect(statSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0')).size).toBe(5); + lodash.throttle.mockRestore(); }); - it('rotates log file service correctly detects if it needs to use polling', async () => { + it.skip('rotates log file service correctly detects if it needs to use polling', async () => { }); }); From c91cc769cb6e5c6fcef3c765f01c457dd5f54234 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sun, 10 Nov 2019 22:21:39 +0000 Subject: [PATCH 31/73] test(NA): add last test case --- .../server/logging/rotate/log_rotator.test.js | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index db4127ab1826a..d4e04a28cf668 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -18,7 +18,7 @@ */ import del from 'del'; -import { existsSync, mkdirSync, statSync, writeFileSync } from 'fs'; +import fs, { existsSync, mkdirSync, statSync, writeFileSync } from 'fs'; import lodash from 'lodash'; import { LogRotator } from './log_rotator'; import { tmpdir } from 'os'; @@ -152,7 +152,37 @@ describe('LogRotator', () => { lodash.throttle.mockRestore(); }); - it.skip('rotates log file service correctly detects if it needs to use polling', async () => { + it('rotates log file service correctly detects usePolling when it should be false', async () => { + writeBytesToFile(testFilePath, 1); + + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + await logRotator.start(); + expect(logRotator.running).toBe(true); + expect(logRotator.usePolling).toBe(false); + + const usePolling = await logRotator._shouldUsePolling(); + expect(usePolling).toBe(false); + + await logRotator.stop(); }); + + it('rotates log file service correctly detects usePolling when it should be true', async () => { + writeBytesToFile(testFilePath, 1); + + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + jest.spyOn(fs, 'watch').mockImplementation(() => ({ + on: jest.fn(), + close: jest.fn() + })); + + await logRotator.start(); + + expect(logRotator.running).toBe(true); + expect(logRotator.usePolling).toBe(true); + + await logRotator.stop(); + }, 20000); }); From 8a969c4a03b97bb4eb360b691ce3b59f421a9d85 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 11 Nov 2019 19:10:51 +0000 Subject: [PATCH 32/73] test(NA): fallback conditions --- .../server/logging/rotate/log_rotator.js | 4 +++ .../server/logging/rotate/log_rotator.test.js | 34 +++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 06ded196c3e41..26a025abb7f7f 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -110,6 +110,10 @@ export class LogRotator { await onResolve(resolve, false); }); + testWatcher.on('error', async () => { + await onResolve(resolve, true); + }); + await writeFileAsync(tempFile, 'test'); }); } catch { diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index d4e04a28cf668..adfe9b59dfed4 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -168,13 +168,19 @@ describe('LogRotator', () => { await logRotator.stop(); }); + it('rotates log file service correctly detects usePolling when it should be true', async () => { writeBytesToFile(testFilePath, 1); const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + jest.spyOn(fs, 'watch').mockImplementation(() => ({ - on: jest.fn(), + on: jest.fn((eventType, cb) => { + if (eventType === 'error') { + cb(); + } + }), close: jest.fn() })); @@ -184,5 +190,29 @@ describe('LogRotator', () => { expect(logRotator.usePolling).toBe(true); await logRotator.stop(); - }, 20000); + }); + + it('rotates log file service correctly fallback to usePolling true after defined timeout', async () => { + jest.useFakeTimers(); + writeBytesToFile(testFilePath, 1); + + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + jest.spyOn(fs, 'watch').mockImplementation(() => ({ + on: jest.fn((ev) => { + if (ev === 'error') { + jest.runTimersToTime(15000); + } + }), + close: jest.fn() + })); + + await logRotator.start(); + + expect(logRotator.running).toBe(true); + expect(logRotator.usePolling).toBe(true); + + await logRotator.stop(); + jest.useRealTimers(); + }); }); From d09367e45840cf7888f13a3d5dfbc934f6d4f574 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 12 Nov 2019 17:07:14 +0000 Subject: [PATCH 33/73] chore(NA): add logging rotate config keys to the docker image --- .../docker_generator/resources/bin/kibana-docker | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index 0926ef365c894..0372c12828a4c 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -42,6 +42,12 @@ kibana_vars=( kibana.index logging.dest logging.quiet + logging.rotate + logging.rotate.enable + logging.rotate.everyBytes + logging.rotate.keepFiles + logging.rotate.pollingInterval + logging.rotate.usePolling logging.silent logging.useUTC logging.verbose From fb03cb2f1074ea4558a8db125dec8e1046583880 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 15 Nov 2019 15:58:15 +0000 Subject: [PATCH 34/73] chore(NA): move logging.rotate.enable setting to enabled --- docs/setup/settings.asciidoc | 4 ++-- .../os_packages/docker_generator/resources/bin/kibana-docker | 2 +- src/legacy/server/config/schema.js | 2 +- src/legacy/server/logging/rotate/index.js | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index d0feb77d184dc..8837ccdc5b00b 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -152,12 +152,12 @@ The following example shows a valid logging rotate configuration: + -- logging.rotate: - enable: true + enabled: true everyBytes: 104857600 keepFiles: 10 -- -`logging.rotate.enable:`:: *Default: false* Set the value of this setting to `true` to +`logging.rotate.enabled:`:: *Default: false* Set the value of this setting to `true` to enable logging rotate. If you do not have a `logging.dest` set that is different from `stdout` that feature would not take any effect. diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index 088d935edd45b..655a1432a324d 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -43,7 +43,7 @@ kibana_vars=( logging.dest logging.quiet logging.rotate - logging.rotate.enable + logging.rotate.enabled logging.rotate.everyBytes logging.rotate.keepFiles logging.rotate.pollingInterval diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index 7eab884f56345..7d5f496b22d06 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -137,7 +137,7 @@ export default () => Joi.object({ }), timezone: Joi.string(), rotate: Joi.object().keys({ - enable: Joi.boolean().default(false), + enabled: Joi.boolean().default(false), everyBytes: Joi.number().greater(1024).default(10485760), keepFiles: Joi.number().greater(2).less(1024).default(7), pollingInterval: Joi.number().greater(5).less(3600).default(10), diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index d8a2b2b969883..f09f6a764bde9 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -24,13 +24,13 @@ let logRotator = null; export async function setupLoggingRotate(server, config) { // If log rotate is not enabled we skip - if (!config.get('logging.rotate.enable')) { + if (!config.get('logging.rotate.enabled')) { return; } // We don't want to run logging rotate server if // we are not logging to a file - if (config.get('logging.rotate.enable') && config.get('logging.dest') === 'stdout') { + if (config.get('logging.rotate.enabled') && config.get('logging.dest') === 'stdout') { server.logWithMetadata( ['warning', 'logging:rotate'], 'Logging rotate is enabled but logging.dest is configured for stdout. The logging rotate will take no action.' From 118b09ebbc446f3b09df4e8a369e1b94058421e3 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 15 Nov 2019 16:10:33 +0000 Subject: [PATCH 35/73] chore(NA): clarify documentation for logging rotate --- docs/setup/settings.asciidoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 8837ccdc5b00b..6da65b99166b9 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -147,7 +147,8 @@ will default to `true`. `logging.quiet:`:: *Default: false* Set the value of this setting to `true` to suppress all logging output other than error messages. -`logging.rotate:`:: *Default: false* Specifies the options for the logging rotate feature. +`logging.rotate:`:: Specifies the options for the logging rotate feature. +When not defined, all the sub options defaults would be applied. The following example shows a valid logging rotate configuration: + -- From 6f2510e0d4a6a0d5c3338f88c99a04364e5b2e81 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 15 Nov 2019 18:12:06 +0000 Subject: [PATCH 36/73] chore(NA): use regular instead of logWithMetadata --- src/legacy/server/logging/rotate/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index f09f6a764bde9..51b02bec78161 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -31,7 +31,7 @@ export async function setupLoggingRotate(server, config) { // We don't want to run logging rotate server if // we are not logging to a file if (config.get('logging.rotate.enabled') && config.get('logging.dest') === 'stdout') { - server.logWithMetadata( + server.log( ['warning', 'logging:rotate'], 'Logging rotate is enabled but logging.dest is configured for stdout. The logging rotate will take no action.' ); From 2a27e8505e64968f6aa9a1eb0656f46dc600f493 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 15 Nov 2019 18:15:35 +0000 Subject: [PATCH 37/73] chore(NA): move chokidar to a prod dep --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3abd69a616682..9418ada5ea851 100644 --- a/package.json +++ b/package.json @@ -147,6 +147,7 @@ "cache-loader": "^4.1.0", "chalk": "^2.4.2", "check-disk-space": "^2.1.0", + "chokidar": "3.2.1", "color": "1.0.3", "commander": "3.0.0", "compare-versions": "3.5.1", @@ -359,7 +360,6 @@ "chai": "3.5.0", "chance": "1.0.18", "cheerio": "0.22.0", - "chokidar": "3.2.1", "chromedriver": "78.0.1", "classnames": "2.2.6", "dedent": "^0.7.0", From e0d67ec7ccaf8d4eee9382574e475ab8c38b43fa Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 15 Nov 2019 18:31:55 +0000 Subject: [PATCH 38/73] chore(NA): add log explaining why we had fallback to use polling --- src/legacy/server/logging/rotate/index.js | 2 +- src/legacy/server/logging/rotate/log_rotator.js | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 51b02bec78161..21feddfeead47 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -49,7 +49,7 @@ export async function setupLoggingRotate(server, config) { // try to setupLoggingRotate more than once, // so we'll need to assure it only loads once. if (!logRotator) { - logRotator = new LogRotator(config); + logRotator = new LogRotator(config, server); await logRotator.start(); } diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 26a025abb7f7f..73a50cd3b7ddc 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -33,7 +33,9 @@ const unlinkAsync = promisify(fs.unlink); const writeFileAsync = promisify(fs.writeFile); export class LogRotator { - constructor(config) { + constructor(config, server) { + this.config = config; + this.log = server.log; this.logFilePath = config.get('logging.dest'); this.interval = 1; this.everyBytes = config.get('logging.rotate.everyBytes'); @@ -136,6 +138,14 @@ export class LogRotator { async _startLogFileSizeMonitor() { this.usePolling = await this._shouldUsePolling(); + + if (this.usePolling && this.usePolling !== this.config.get('logging.rotate.usePolling')) { + this.log( + ['warning', 'logging:rotate'], + 'The current environment does not support `fs.watch`. Falling back to polling using `fs.watchFile`' + ); + } + this.stalker = chokidar.watch(this.logFilePath, this._buildWatchCfg(this.usePolling)); this.stalker.on('change', async (filename, stats) => await this._logFileSizeMonitorHandler.bind(this)(filename, stats)); } From 5e7a4893abce17aad84e6717682418c7f843c16f Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 15 Nov 2019 20:29:17 +0000 Subject: [PATCH 39/73] test(NA): fix unit tests --- .../server/logging/rotate/log_rotator.test.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index adfe9b59dfed4..d52033a51a2ae 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -46,6 +46,10 @@ const createLogRotatorConfig = (logFilePath) => { ]); }; +const mockServer = jest.fn(() => ({ + log: jest.fn() +})); + const writeBytesToFile = (filePath, numberOfBytes) => { writeFileSync(filePath, 'a'.repeat(numberOfBytes), { flag: 'a' }); }; @@ -64,7 +68,7 @@ describe('LogRotator', () => { it('rotates log file when bigger than set limit on start', async () => { writeBytesToFile(testFilePath, 3); - const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); await logRotator.start(); @@ -79,7 +83,7 @@ describe('LogRotator', () => { it('rotates log file when equal than set limit over time', async () => { writeBytesToFile(testFilePath, 1); - const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); await logRotator.start(); @@ -101,7 +105,7 @@ describe('LogRotator', () => { it('rotates log file when file size is bigger than limit', async () => { writeBytesToFile(testFilePath, 1); - const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); await logRotator.start(); @@ -125,7 +129,7 @@ describe('LogRotator', () => { writeBytesToFile(testFilePath, 3); - const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); await logRotator.start(); @@ -155,7 +159,7 @@ describe('LogRotator', () => { it('rotates log file service correctly detects usePolling when it should be false', async () => { writeBytesToFile(testFilePath, 1); - const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); await logRotator.start(); @@ -172,7 +176,7 @@ describe('LogRotator', () => { it('rotates log file service correctly detects usePolling when it should be true', async () => { writeBytesToFile(testFilePath, 1); - const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); jest.spyOn(fs, 'watch').mockImplementation(() => ({ @@ -196,7 +200,7 @@ describe('LogRotator', () => { jest.useFakeTimers(); writeBytesToFile(testFilePath, 1); - const logRotator = new LogRotator(createLogRotatorConfig(testFilePath)); + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); jest.spyOn(fs, 'watch').mockImplementation(() => ({ on: jest.fn((ev) => { From e3df5754d602c926e1f8a373cf94b3f6275eb4fe Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 16 Nov 2019 19:20:21 +0000 Subject: [PATCH 40/73] test(NA): fix unit tests --- src/legacy/server/logging/rotate/log_rotator.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index d52033a51a2ae..6d43fd37d8192 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -46,9 +46,9 @@ const createLogRotatorConfig = (logFilePath) => { ]); }; -const mockServer = jest.fn(() => ({ +const mockServer = { log: jest.fn() -})); +}; const writeBytesToFile = (filePath, numberOfBytes) => { writeFileSync(filePath, 'a'.repeat(numberOfBytes), { flag: 'a' }); From 8345e0998390e5bd2ced0a13139060121759d6c1 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 18 Nov 2019 11:00:33 +0000 Subject: [PATCH 41/73] chore(NA): correctly place this.running condition --- src/legacy/server/logging/rotate/log_rotator.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 73a50cd3b7ddc..45b393c3323cc 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -55,6 +55,8 @@ export class LogRotator { return; } + this.running = true; + // create exit listener for cleanup purposes this._createExitListener(); @@ -63,8 +65,6 @@ export class LogRotator { // init log file size monitor await this._startLogFileSizeMonitor(); - - this.running = true; } stop() { From 7f532192431ec6102a75fae4480c6d64de0f8aa3 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 18 Nov 2019 11:28:33 +0000 Subject: [PATCH 42/73] chore(NA): remove redundant call --- src/legacy/server/logging/rotate/log_rotator.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 45b393c3323cc..0d2398e61f07b 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -222,9 +222,6 @@ export class LogRotator { // 3. rename all files to the correct index +1 // 4. rename + compress current log into 1 // 5. send SIGHUP to reload log config - if (this.isRotating) { - return false; - } // rotate process is starting this.isRotating = true; From 3eda24b2946bdc8e05a5e656f49d8061794c1dab Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 19 Nov 2019 17:30:31 +0000 Subject: [PATCH 43/73] fix(NA): log filename containing numbers would produce invalid sorting --- src/legacy/server/logging/rotate/log_rotator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 0d2398e61f07b..9f1866b825bd5 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -255,7 +255,7 @@ export class LogRotator { return (await readdirAsync(logFilesFolder)) .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) - .sort((a, b) => Number(a.match(/(\d+)/g)[0]) - Number(b.match(/(\d+)/g)[0])) + .sort((a, b) => Number(a.match(/(\d+)/g).slice(-1)) - Number(b.match(/(\d+)/g).slice(-1))) .map(filename => `${logFilesFolder}${sep}${filename}`) .filter(filepath => fs.existsSync(filepath)); } From 92498642fc8abd143162a94d92f7fc0bc79e095a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 20 Nov 2019 01:55:21 +0000 Subject: [PATCH 44/73] chore(NA): remove existsSync function call from readRotatedFilesMetadata function --- src/legacy/server/logging/rotate/log_rotator.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 9f1866b825bd5..17d63b51f3278 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -256,8 +256,7 @@ export class LogRotator { return (await readdirAsync(logFilesFolder)) .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) .sort((a, b) => Number(a.match(/(\d+)/g).slice(-1)) - Number(b.match(/(\d+)/g).slice(-1))) - .map(filename => `${logFilesFolder}${sep}${filename}`) - .filter(filepath => fs.existsSync(filepath)); + .map(filename => `${logFilesFolder}${sep}${filename}`); } async _deleteLastRotatedFile(rotatedFiles) { From 8acea0e1b9c4d1ad2716088037c93ae679bdf6bd Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 26 Nov 2019 23:11:48 +0000 Subject: [PATCH 45/73] chore(NA): Update docs/setup/settings.asciidoc Co-Authored-By: Tyler Smalley --- docs/setup/settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index e7b85d889bfbd..e61e6557268c9 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -154,7 +154,7 @@ The following example shows a valid logging rotate configuration: -- logging.rotate: enabled: true - everyBytes: 104857600 + everyBytes: 10485760 keepFiles: 10 -- From 1cf9e06a5df6b88b12d2941eab6fce587e2bace7 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 26 Nov 2019 23:12:01 +0000 Subject: [PATCH 46/73] chore(NA): Update docs/setup/settings.asciidoc Co-Authored-By: Tyler Smalley --- docs/setup/settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index e61e6557268c9..5fd9fe9aa6c7a 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -171,7 +171,7 @@ on disk. Older files are deleted during log rotation. The default value is 7. Th option has to be in the range of 2 to 1024 files. `logging.rotate.pollingInterval:`:: *Default: 10* The number of seconds for the polling strategy in case -the `logging.rotate.usePolling` is enabled. That option option has to be in the range of 5 to 3600 files. +the `logging.rotate.usePolling` is enabled. That option has to be in the range of 5 to 3600 files. `logging.rotate.usePolling:`:: *Default: false* By default we try to understand the best way to monitoring the log file. However there is some systems where it could not be always accurate. In those cases, if needed, From 83fb84de31261c4ec477d6571e1e189427ccd6ce Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 26 Nov 2019 23:12:13 +0000 Subject: [PATCH 47/73] chore(NA): Update docs/setup/settings.asciidoc Co-Authored-By: Tyler Smalley --- docs/setup/settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 5fd9fe9aa6c7a..aaf3c5c11bb66 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -174,7 +174,7 @@ option has to be in the range of 2 to 1024 files. the `logging.rotate.usePolling` is enabled. That option has to be in the range of 5 to 3600 files. `logging.rotate.usePolling:`:: *Default: false* By default we try to understand the best way to monitoring -the log file. However there is some systems where it could not be always accurate. In those cases, if needed, +the log file. However, there is some systems where it could not be always accurate. In those cases, if needed, the `polling` method could be used enabling that option. `logging.silent:`:: *Default: false* Set the value of this setting to `true` to From 5ac11f4f0f5a64a7d4a9a9c658c2a91ed6eb90cb Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 26 Nov 2019 23:12:29 +0000 Subject: [PATCH 48/73] chore(NA): Update docs/setup/settings.asciidoc Co-Authored-By: Tyler Smalley --- docs/setup/settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index aaf3c5c11bb66..89af59149678c 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -159,7 +159,7 @@ The following example shows a valid logging rotate configuration: -- `logging.rotate.enabled:`:: *Default: false* Set the value of this setting to `true` to -enable logging rotate. If you do not have a `logging.dest` set that is different from `stdout` +enable log rotation. If you do not have a `logging.dest` set that is different from `stdout` that feature would not take any effect. `logging.rotate.everyBytes:`:: *Default: 10485760* The maximum size of a log file. If the From 6398a398e9ec06ed059ceb8523c5bfaa4e0460f6 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 2 Dec 2019 11:09:08 +0000 Subject: [PATCH 49/73] chore(na): update src/legacy/server/logging/rotate/index.js Co-Authored-By: Tyler Smalley --- src/legacy/server/logging/rotate/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 21feddfeead47..651987bd9bfc8 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -33,7 +33,7 @@ export async function setupLoggingRotate(server, config) { if (config.get('logging.rotate.enabled') && config.get('logging.dest') === 'stdout') { server.log( ['warning', 'logging:rotate'], - 'Logging rotate is enabled but logging.dest is configured for stdout. The logging rotate will take no action.' + 'Log rotation is enabled but logging.dest is configured for stdout. Set logging.dest to a file for this setting to take effect.' ); return; } From 73837226491bc10266ea3b206441b61831475bbe Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 2 Dec 2019 11:18:22 +0000 Subject: [PATCH 50/73] chore(NA): remove unused config line from docker vars --- .../os_packages/docker_generator/resources/bin/kibana-docker | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index 4e57ef0b565f7..d1734e836d983 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -42,7 +42,6 @@ kibana_vars=( kibana.index logging.dest logging.quiet - logging.rotate logging.rotate.enabled logging.rotate.everyBytes logging.rotate.keepFiles From 938052bf58ad9a2b2896c887cb6da257c6bfa3e1 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 2 Dec 2019 11:21:20 +0000 Subject: [PATCH 51/73] chore(NA): update documentation to include info about non exact limits --- docs/setup/settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 89af59149678c..aa3892094964d 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -162,7 +162,7 @@ The following example shows a valid logging rotate configuration: enable log rotation. If you do not have a `logging.dest` set that is different from `stdout` that feature would not take any effect. -`logging.rotate.everyBytes:`:: *Default: 10485760* The maximum size of a log file. If the +`logging.rotate.everyBytes:`:: *Default: 10485760* The maximum size of a log file (that is `not an exact` limit). After the limit is reached, a new log file is generated. The default size limit is 10485760 (10 MB) and this option should be at least greater than 1024. From 7db2d5962b58bc68fd2d5f5d864f9fb7bf1a75f0 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 2 Dec 2019 11:23:20 +0000 Subject: [PATCH 52/73] chore(NA): remove redudant if clause --- src/legacy/server/logging/rotate/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 651987bd9bfc8..05706a3aabcb1 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -30,7 +30,7 @@ export async function setupLoggingRotate(server, config) { // We don't want to run logging rotate server if // we are not logging to a file - if (config.get('logging.rotate.enabled') && config.get('logging.dest') === 'stdout') { + if (config.get('logging.dest') === 'stdout') { server.log( ['warning', 'logging:rotate'], 'Log rotation is enabled but logging.dest is configured for stdout. Set logging.dest to a file for this setting to take effect.' From 35627b028bc685e843f291cd8c451560ee4f3654 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 2 Dec 2019 16:00:39 +0000 Subject: [PATCH 53/73] fix(NA): correctly work with new keepFiles limit after start --- .../server/logging/rotate/log_rotator.js | 20 ++++++++- .../server/logging/rotate/log_rotator.test.js | 44 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 17d63b51f3278..bcfa3dc37a60c 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -227,7 +227,10 @@ export class LogRotator { this.isRotating = true; // get rotated files metadata - const rotatedFiles = await this._readRotatedFilesMetadata(); + const foundRotatedFiles = await this._readRotatedFilesMetadata(); + + // Truncate number of rotated files to the settings limit + const rotatedFiles = await this._truncateFoundRotatedFilesToKeepLimit(foundRotatedFiles); // delete last file await this._deleteLastRotatedFile(rotatedFiles); @@ -259,6 +262,21 @@ export class LogRotator { .map(filename => `${logFilesFolder}${sep}${filename}`); } + async _truncateFoundRotatedFilesToKeepLimit(foundRotatedFiles) { + if (foundRotatedFiles.length <= this.keepFiles) { + return foundRotatedFiles; + } + + const finalRotatedFiles = foundRotatedFiles.slice(0, this.keepFiles); + const rotatedFilesToDelete = foundRotatedFiles.slice(finalRotatedFiles.length, foundRotatedFiles.length); + + await Promise.all( + rotatedFilesToDelete.map(rotatedFilePath => unlinkAsync(rotatedFilePath)) + ); + + return finalRotatedFiles; + } + async _deleteLastRotatedFile(rotatedFiles) { if (rotatedFiles.length < this.keepFiles) { return; diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index 6d43fd37d8192..c5a6c5b0833c1 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -156,6 +156,50 @@ describe('LogRotator', () => { lodash.throttle.mockRestore(); }); + it('rotates log file service correctly keeps number of files even when number setting changes', async () => { + jest.spyOn(lodash, 'throttle').mockImplementation(fn => fn); + + writeBytesToFile(testFilePath, 3); + + const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); + jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); + await logRotator.start(); + + expect(logRotator.running).toBe(true); + + const testLogFileDir = dirname(testFilePath); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); + + writeBytesToFile(testFilePath, 2); + + // ['change', [asyncFunction]] + const onChangeCb = mockOn.mock.calls[0][1]; + await onChangeCb(testLogFileDir, { size: 2 }); + + writeBytesToFile(testFilePath, 5); + await onChangeCb(testLogFileDir, { size: 5 }); + + await logRotator.stop(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.1'))).toBeTruthy(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.2'))).toBeFalsy(); + expect(statSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0')).size).toBe(5); + + + logRotator.keepFiles = 1; + await logRotator.start(); + + writeBytesToFile(testFilePath, 5); + await onChangeCb(testLogFileDir, { size: 5 }); + + await logRotator.stop(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); + expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.1'))).toBeFalsy(); + expect(statSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0')).size).toBe(5); + + lodash.throttle.mockRestore(); + }); + it('rotates log file service correctly detects usePolling when it should be false', async () => { writeBytesToFile(testFilePath, 1); From 5314b879e970d3879a1acdbfc2811539c9d11ae0 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 2 Dec 2019 16:29:12 +0000 Subject: [PATCH 54/73] fix(NA): warning log for logging rotate --- src/legacy/server/logging/index.js | 3 ++- src/legacy/server/logging/rotate/index.js | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/legacy/server/logging/index.js b/src/legacy/server/logging/index.js index 8743c0ff6853f..defbcd3331df4 100644 --- a/src/legacy/server/logging/index.js +++ b/src/legacy/server/logging/index.js @@ -31,6 +31,7 @@ export async function setupLogging(server, config) { export async function loggingMixin(kbnServer, server, config) { logWithMetadata.decorateServer(server); + + await setupLogging(server, config); await setupLoggingRotate(server, config); - return await setupLogging(server, config); } diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 05706a3aabcb1..d6b959ea81a5e 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -28,22 +28,22 @@ export async function setupLoggingRotate(server, config) { return; } + // We just want to start the logging rotate service once + // and we choose to use the master (prod) or the worker server (dev) + if (!isMaster && isWorker && process.env.kbnWorkerType !== 'server') { + return; + } + // We don't want to run logging rotate server if // we are not logging to a file if (config.get('logging.dest') === 'stdout') { - server.log( + server.logWithMetadata( ['warning', 'logging:rotate'], 'Log rotation is enabled but logging.dest is configured for stdout. Set logging.dest to a file for this setting to take effect.' ); return; } - // We just want to start the logging rotate service once - // and we choose to use the master (prod) or the worker server (dev) - if (!isMaster && isWorker && process.env.kbnWorkerType !== 'server') { - return; - } - // Enable Logging Rotate Service // We need the master process and it can // try to setupLoggingRotate more than once, From 32cb7e1a3bcb213bee2b620ec1f64d6003323bb9 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 3 Dec 2019 20:14:35 +0000 Subject: [PATCH 55/73] chore(NA): replace logwithmetadate with log --- src/legacy/server/logging/rotate/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index d6b959ea81a5e..18ec911ada098 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -37,7 +37,7 @@ export async function setupLoggingRotate(server, config) { // We don't want to run logging rotate server if // we are not logging to a file if (config.get('logging.dest') === 'stdout') { - server.logWithMetadata( + server.log( ['warning', 'logging:rotate'], 'Log rotation is enabled but logging.dest is configured for stdout. Set logging.dest to a file for this setting to take effect.' ); From 844200d75bfc65cbce11669eb49779cfa78452e5 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 3 Dec 2019 20:32:16 +0000 Subject: [PATCH 56/73] docs(NA): correct log to right terms --- src/legacy/server/logging/rotate/log_rotator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index bcfa3dc37a60c..002b46676443c 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -239,7 +239,7 @@ export class LogRotator { // and normalize await this._renameRotatedFilesByOne(rotatedFiles); - // rename + compress current log into 0 + // rename current log into 0 await this._rotateCurrentLogFile(); // send SIGHUP to reload log configuration From 41605c32f04ef339590342755589f02e8572ee6a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 3 Dec 2019 20:35:04 +0000 Subject: [PATCH 57/73] docs(NA): add comment about usage of slice(-1) --- src/legacy/server/logging/rotate/log_rotator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 002b46676443c..cacbb52e80059 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -258,6 +258,7 @@ export class LogRotator { return (await readdirAsync(logFilesFolder)) .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) + // we use .slice(-1) here in order to retrieve the last number match in the read filenames .sort((a, b) => Number(a.match(/(\d+)/g).slice(-1)) - Number(b.match(/(\d+)/g).slice(-1))) .map(filename => `${logFilesFolder}${sep}${filename}`); } From ecdf0368cb450312b527f15dcc8b47148ca2b470 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 3 Dec 2019 21:28:54 +0000 Subject: [PATCH 58/73] refact(NA): changing polling interval from seconds to milliseconds --- docs/setup/settings.asciidoc | 4 ++-- src/legacy/server/config/schema.js | 2 +- src/legacy/server/logging/rotate/log_rotator.js | 2 +- src/legacy/server/logging/rotate/log_rotator.test.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index aa3892094964d..f0d38a8f8c227 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -170,8 +170,8 @@ this option should be at least greater than 1024. on disk. Older files are deleted during log rotation. The default value is 7. The `logging.rotate.keepFiles` option has to be in the range of 2 to 1024 files. -`logging.rotate.pollingInterval:`:: *Default: 10* The number of seconds for the polling strategy in case -the `logging.rotate.usePolling` is enabled. That option has to be in the range of 5 to 3600 files. +`logging.rotate.pollingInterval:`:: *Default: 10000* The number of milliseconds for the polling strategy in case +the `logging.rotate.usePolling` is enabled. That option has to be in the range of 5000 to 3600000 milliseconds. `logging.rotate.usePolling:`:: *Default: false* By default we try to understand the best way to monitoring the log file. However, there is some systems where it could not be always accurate. In those cases, if needed, diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index 7d5f496b22d06..afd2bd14076a2 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -140,7 +140,7 @@ export default () => Joi.object({ enabled: Joi.boolean().default(false), everyBytes: Joi.number().greater(1024).default(10485760), keepFiles: Joi.number().greater(2).less(1024).default(7), - pollingInterval: Joi.number().greater(5).less(3600).default(10), + pollingInterval: Joi.number().greater(5000).less(3600000).default(10000), usePolling: Joi.boolean().default(false) }).default() }).default(), diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index cacbb52e80059..58e6d2ebf35ed 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -46,7 +46,7 @@ export class LogRotator { this.throttledRotate = throttle(async () => { await this._rotate(); }, 5000); this.stalker = null; this.usePolling = config.get('logging.rotate.usePolling'); - this.pollingInterval = config.get('logging.rotate.pollingInterval') * 1000; + this.pollingInterval = config.get('logging.rotate.pollingInterval'); this.stalkerUsePollingPolicyTestTimeout = null; } diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index c5a6c5b0833c1..54a48308a951a 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -42,7 +42,7 @@ const createLogRotatorConfig = (logFilePath) => { ['logging.rotate.everyBytes', 2], ['logging.rotate.keepFiles', 2], ['logging.rotate.usePolling', false], - ['logging.rotate.pollingInterval', 10] + ['logging.rotate.pollingInterval', 10000] ]); }; From 9220525a6cdd047116cf628184fd179dd30a70ca Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 3 Dec 2019 21:32:27 +0000 Subject: [PATCH 59/73] docs(NA): fix comments for shouldRotate method --- src/legacy/server/logging/rotate/log_rotator.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 58e6d2ebf35ed..ce180c54b33ce 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -196,8 +196,7 @@ export class LogRotator { _shouldRotate() { // should rotate evaluation // 1. should rotate if current log size exceeds - // the defined one on everyBytes or has already - // pass the defined time on interval. + // the defined one on everyBytes // 2. should not rotate if is already rotating or if any // of the conditions on 1. do not apply if (this.isRotating) { From 76f8a9d933fd6df1008214ab8e872321f29a355e Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 4 Dec 2019 04:34:06 +0000 Subject: [PATCH 60/73] chore(NA): update src/legacy/server/logging/rotate/log_rotator.js Co-Authored-By: Mikhail Shustov --- src/legacy/server/logging/rotate/log_rotator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index ce180c54b33ce..1c15e4969c7d8 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -150,7 +150,7 @@ export class LogRotator { this.stalker.on('change', async (filename, stats) => await this._logFileSizeMonitorHandler.bind(this)(filename, stats)); } - async _logFileSizeMonitorHandler(filename, stats) { +_logFileSizeMonitorHandler = async (filename, stats) => { if (!filename || !stats) { return; } From b7b02989159dc829fb6ead4bf9728356f0592b6e Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 4 Dec 2019 04:34:28 +0000 Subject: [PATCH 61/73] chore(NA): update src/legacy/server/logging/rotate/log_rotator.js Co-Authored-By: Mikhail Shustov --- src/legacy/server/logging/rotate/log_rotator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 1c15e4969c7d8..639eff66c242b 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -147,7 +147,7 @@ export class LogRotator { } this.stalker = chokidar.watch(this.logFilePath, this._buildWatchCfg(this.usePolling)); - this.stalker.on('change', async (filename, stats) => await this._logFileSizeMonitorHandler.bind(this)(filename, stats)); + this.stalker.on('change', this._logFileSizeMonitorHandler); } _logFileSizeMonitorHandler = async (filename, stats) => { From a46d78411c66df1d77ff4dc7b06cdde7d95a816b Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 4 Dec 2019 04:36:39 +0000 Subject: [PATCH 62/73] refact(NA): small change --- src/legacy/server/logging/rotate/log_rotator.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 639eff66c242b..e1031438e3126 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -150,14 +150,14 @@ export class LogRotator { this.stalker.on('change', this._logFileSizeMonitorHandler); } -_logFileSizeMonitorHandler = async (filename, stats) => { + _logFileSizeMonitorHandler = async (filename, stats) => { if (!filename || !stats) { return; } this.logFileSize = stats.size || 0; await this.throttledRotate(); - } + }; _stopLogFileSizeMonitor() { if (!this.stalker) { From e99541b4b6be291ed1f5f8a39ef85d9dc0465af5 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 4 Dec 2019 04:43:01 +0000 Subject: [PATCH 63/73] refact(NA): bound stop --- src/legacy/server/logging/rotate/log_rotator.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index e1031438e3126..e1a91a083c13f 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -67,7 +67,7 @@ export class LogRotator { await this._startLogFileSizeMonitor(); } - stop() { + stop = () => { if (!this.running) { return; } @@ -79,7 +79,7 @@ export class LogRotator { this._stopLogFileSizeMonitor(); this.running = false; - } + }; async _shouldUsePolling() { try { @@ -150,7 +150,7 @@ export class LogRotator { this.stalker.on('change', this._logFileSizeMonitorHandler); } - _logFileSizeMonitorHandler = async (filename, stats) => { + _logFileSizeMonitorHandler = async (filename, stats) => { if (!filename || !stats) { return; } @@ -169,7 +169,7 @@ export class LogRotator { } _createExitListener() { - process.on('exit', this.stop.bind(this)); + process.on('exit', this.stop); } _deleteExitListener() { From 8bb24c4022a59bb9b6c1d1856b4079bf57347da1 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 4 Dec 2019 16:39:04 +0000 Subject: [PATCH 64/73] refact(NA): shouldUsePolling test function --- src/legacy/server/logging/rotate/index.js | 2 +- .../server/logging/rotate/log_rotator.js | 45 ++++++++++--------- .../server/logging/rotate/log_rotator.test.js | 16 +++---- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.js index 18ec911ada098..d6b959ea81a5e 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.js @@ -37,7 +37,7 @@ export async function setupLoggingRotate(server, config) { // We don't want to run logging rotate server if // we are not logging to a file if (config.get('logging.dest') === 'stdout') { - server.log( + server.logWithMetadata( ['warning', 'logging:rotate'], 'Log rotation is enabled but logging.dest is configured for stdout. Set logging.dest to a file for this setting to take effect.' ); diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index e1a91a083c13f..6551552dd8d0d 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -20,9 +20,11 @@ import * as chokidar from 'chokidar'; import { isMaster } from 'cluster'; import fs from 'fs'; -import { once, throttle } from 'lodash'; +import { throttle } from 'lodash'; import { tmpdir } from 'os'; import { basename, dirname, join, sep } from 'path'; +import { Observable } from 'rxjs'; +import { first } from 'rxjs/operators'; import { promisify } from 'util'; const mkdirAsync = promisify(fs.mkdir); @@ -35,7 +37,7 @@ const writeFileAsync = promisify(fs.writeFile); export class LogRotator { constructor(config, server) { this.config = config; - this.log = server.log; + this.log = server.logWithMetadata; this.logFilePath = config.get('logging.dest'); this.interval = 1; this.everyBytes = config.get('logging.rotate.everyBytes'); @@ -91,33 +93,36 @@ export class LogRotator { await mkdirAsync(tempFileDir, { recursive: true }); await writeFileAsync(tempFile, ''); + // setup fs.watch for the temp test file const testWatcher = fs.watch(tempFile, { persistent: false }); - return new Promise(async (resolve) => { - let fallbackTimeout = null; - - const onResolve = once(async (resolve, completeStatus) => { + const usePollingTest$ = new Observable(async (observer) => { + // observable complete function + const completeFn = (fallbackTimeout, completeStatus) => { clearTimeout(fallbackTimeout); - testWatcher.close(); - await unlinkAsync(tempFile); - resolve(completeStatus); - }); - - fallbackTimeout = setTimeout(async () => { - await onResolve(resolve, true); - }, 15000); - testWatcher.on('change', async () => { - await onResolve(resolve, false); - }); + observer.next(completeStatus); + observer.complete(); + }; - testWatcher.on('error', async () => { - await onResolve(resolve, true); - }); + // setup conditions that would fire the observable + const fallbackTimeout = setTimeout(() => completeFn(fallbackTimeout, true), 15000); + testWatcher.on('change', () => completeFn(fallbackTimeout, false)); + testWatcher.on('error', () => completeFn(fallbackTimeout, true)); + // fire test watcher events await writeFileAsync(tempFile, 'test'); }); + + // wait for the first observable result and consider it as the result + // for our use polling test + const usePollingTestResult = await usePollingTest$.pipe(first()).toPromise(); + + // delete the temp file used for the test + await unlinkAsync(tempFile); + + return usePollingTestResult; } catch { return true; } diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.js index 54a48308a951a..ce31f6b7c9de0 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.js @@ -19,7 +19,6 @@ import del from 'del'; import fs, { existsSync, mkdirSync, statSync, writeFileSync } from 'fs'; -import lodash from 'lodash'; import { LogRotator } from './log_rotator'; import { tmpdir } from 'os'; import { dirname, join } from 'path'; @@ -33,6 +32,11 @@ jest.mock('chokidar', () => ({ })), })); +jest.mock('lodash', () => ({ + ...require.requireActual('lodash'), + throttle: fn => fn +})); + const tempDir = join(tmpdir(), 'kbn_log_rotator_test'); const testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); @@ -47,7 +51,7 @@ const createLogRotatorConfig = (logFilePath) => { }; const mockServer = { - log: jest.fn() + logWithMetadata: jest.fn() }; const writeBytesToFile = (filePath, numberOfBytes) => { @@ -125,8 +129,6 @@ describe('LogRotator', () => { }); it('rotates log file service correctly keeps number of files', async () => { - jest.spyOn(lodash, 'throttle').mockImplementation(fn => fn); - writeBytesToFile(testFilePath, 3); const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); @@ -152,13 +154,9 @@ describe('LogRotator', () => { expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.1'))).toBeTruthy(); expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.2'))).toBeFalsy(); expect(statSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0')).size).toBe(5); - - lodash.throttle.mockRestore(); }); it('rotates log file service correctly keeps number of files even when number setting changes', async () => { - jest.spyOn(lodash, 'throttle').mockImplementation(fn => fn); - writeBytesToFile(testFilePath, 3); const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); @@ -196,8 +194,6 @@ describe('LogRotator', () => { expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0'))).toBeTruthy(); expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.1'))).toBeFalsy(); expect(statSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0')).size).toBe(5); - - lodash.throttle.mockRestore(); }); it('rotates log file service correctly detects usePolling when it should be false', async () => { From 900851cf1930aca3ddbccce410b6c80285b6e652 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 4 Dec 2019 17:04:57 +0000 Subject: [PATCH 65/73] refact(NA): move named truncate function to delete --- src/legacy/server/logging/rotate/log_rotator.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.js index 6551552dd8d0d..8ef69227c8158 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.js @@ -233,8 +233,8 @@ export class LogRotator { // get rotated files metadata const foundRotatedFiles = await this._readRotatedFilesMetadata(); - // Truncate number of rotated files to the settings limit - const rotatedFiles = await this._truncateFoundRotatedFilesToKeepLimit(foundRotatedFiles); + // delete number of rotated files exceeding the keepFiles limit setting + const rotatedFiles = await this._deleteFoundRotatedFilesAboveKeepFilesLimit(foundRotatedFiles); // delete last file await this._deleteLastRotatedFile(rotatedFiles); @@ -267,7 +267,7 @@ export class LogRotator { .map(filename => `${logFilesFolder}${sep}${filename}`); } - async _truncateFoundRotatedFilesToKeepLimit(foundRotatedFiles) { + async _deleteFoundRotatedFilesAboveKeepFilesLimit(foundRotatedFiles) { if (foundRotatedFiles.length <= this.keepFiles) { return foundRotatedFiles; } From 11e2683faf4aab79cc6416a084fcd92c365b3efd Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 5 Dec 2019 17:50:27 +0000 Subject: [PATCH 66/73] refact(NA): typescript conversion --- .../logging/rotate/{index.js => index.ts} | 13 ++- ...og_rotator.test.js => log_rotator.test.ts} | 59 ++++++------ .../rotate/{log_rotator.js => log_rotator.ts} | 94 +++++++++++++------ 3 files changed, 107 insertions(+), 59 deletions(-) rename src/legacy/server/logging/rotate/{index.js => index.ts} (81%) rename src/legacy/server/logging/rotate/{log_rotator.test.js => log_rotator.test.ts} (90%) rename src/legacy/server/logging/rotate/{log_rotator.js => log_rotator.ts} (73%) diff --git a/src/legacy/server/logging/rotate/index.js b/src/legacy/server/logging/rotate/index.ts similarity index 81% rename from src/legacy/server/logging/rotate/index.js rename to src/legacy/server/logging/rotate/index.ts index d6b959ea81a5e..ca3345326ce41 100644 --- a/src/legacy/server/logging/rotate/index.js +++ b/src/legacy/server/logging/rotate/index.ts @@ -18,11 +18,13 @@ */ import { isMaster, isWorker } from 'cluster'; +import { Server } from 'hapi'; import { LogRotator } from './log_rotator'; +import { KibanaConfig } from '../../kbn_server'; -let logRotator = null; +let logRotator: LogRotator | null = null; -export async function setupLoggingRotate(server, config) { +export async function setupLoggingRotate(server: Server, config: KibanaConfig) { // If log rotate is not enabled we skip if (!config.get('logging.rotate.enabled')) { return; @@ -37,13 +39,18 @@ export async function setupLoggingRotate(server, config) { // We don't want to run logging rotate server if // we are not logging to a file if (config.get('logging.dest') === 'stdout') { - server.logWithMetadata( + server.log( ['warning', 'logging:rotate'], 'Log rotation is enabled but logging.dest is configured for stdout. Set logging.dest to a file for this setting to take effect.' ); return; } + server.log( + ['warning', 'logging:rotate'], + 'Log rotation is enabled but logging.dest is configured for stdout. Set logging.dest to a file for this setting to take effect.' + ); + // Enable Logging Rotate Service // We need the master process and it can // try to setupLoggingRotate more than once, diff --git a/src/legacy/server/logging/rotate/log_rotator.test.js b/src/legacy/server/logging/rotate/log_rotator.test.ts similarity index 90% rename from src/legacy/server/logging/rotate/log_rotator.test.js rename to src/legacy/server/logging/rotate/log_rotator.test.ts index ce31f6b7c9de0..c2100546364d4 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.js +++ b/src/legacy/server/logging/rotate/log_rotator.test.ts @@ -18,43 +18,42 @@ */ import del from 'del'; -import fs, { existsSync, mkdirSync, statSync, writeFileSync } from 'fs'; +import fs, { existsSync, mkdirSync, statSync, writeFileSync } from 'fs'; import { LogRotator } from './log_rotator'; import { tmpdir } from 'os'; import { dirname, join } from 'path'; - const mockOn = jest.fn(); jest.mock('chokidar', () => ({ watch: jest.fn(() => ({ on: mockOn, - close: jest.fn() + close: jest.fn(), })), })); jest.mock('lodash', () => ({ ...require.requireActual('lodash'), - throttle: fn => fn + throttle: (fn: any) => fn, })); const tempDir = join(tmpdir(), 'kbn_log_rotator_test'); const testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); -const createLogRotatorConfig = (logFilePath) => { +const createLogRotatorConfig: any = (logFilePath: string) => { return new Map([ ['logging.dest', logFilePath], ['logging.rotate.everyBytes', 2], ['logging.rotate.keepFiles', 2], ['logging.rotate.usePolling', false], - ['logging.rotate.pollingInterval', 10000] - ]); + ['logging.rotate.pollingInterval', 10000], + ] as any); }; -const mockServer = { - logWithMetadata: jest.fn() +const mockServer: any = { + log: jest.fn(), }; -const writeBytesToFile = (filePath, numberOfBytes) => { +const writeBytesToFile = (filePath: string, numberOfBytes: number) => { writeFileSync(filePath, 'a'.repeat(numberOfBytes), { flag: 'a' }); }; @@ -183,7 +182,6 @@ describe('LogRotator', () => { expect(existsSync(join(testLogFileDir, 'log_rotator_test_log_file.log.2'))).toBeFalsy(); expect(statSync(join(testLogFileDir, 'log_rotator_test_log_file.log.0')).size).toBe(5); - logRotator.keepFiles = 1; await logRotator.start(); @@ -212,21 +210,23 @@ describe('LogRotator', () => { await logRotator.stop(); }); - it('rotates log file service correctly detects usePolling when it should be true', async () => { writeBytesToFile(testFilePath, 1); const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); - jest.spyOn(fs, 'watch').mockImplementation(() => ({ - on: jest.fn((eventType, cb) => { - if (eventType === 'error') { - cb(); - } - }), - close: jest.fn() - })); + jest.spyOn(fs, 'watch').mockImplementation( + () => + ({ + on: jest.fn((eventType, cb) => { + if (eventType === 'error') { + cb(); + } + }), + close: jest.fn(), + } as any) + ); await logRotator.start(); @@ -242,14 +242,17 @@ describe('LogRotator', () => { const logRotator = new LogRotator(createLogRotatorConfig(testFilePath), mockServer); jest.spyOn(logRotator, '_sendReloadLogConfigSignal').mockImplementation(() => {}); - jest.spyOn(fs, 'watch').mockImplementation(() => ({ - on: jest.fn((ev) => { - if (ev === 'error') { - jest.runTimersToTime(15000); - } - }), - close: jest.fn() - })); + jest.spyOn(fs, 'watch').mockImplementation( + () => + ({ + on: jest.fn((ev: string) => { + if (ev === 'error') { + jest.runTimersToTime(15000); + } + }), + close: jest.fn(), + } as any) + ); await logRotator.start(); diff --git a/src/legacy/server/logging/rotate/log_rotator.js b/src/legacy/server/logging/rotate/log_rotator.ts similarity index 73% rename from src/legacy/server/logging/rotate/log_rotator.js rename to src/legacy/server/logging/rotate/log_rotator.ts index 8ef69227c8158..51b8a766281e4 100644 --- a/src/legacy/server/logging/rotate/log_rotator.js +++ b/src/legacy/server/logging/rotate/log_rotator.ts @@ -20,12 +20,14 @@ import * as chokidar from 'chokidar'; import { isMaster } from 'cluster'; import fs from 'fs'; +import { Server } from 'hapi'; import { throttle } from 'lodash'; import { tmpdir } from 'os'; import { basename, dirname, join, sep } from 'path'; import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { promisify } from 'util'; +import { KibanaConfig } from '../../kbn_server'; const mkdirAsync = promisify(fs.mkdir); const readdirAsync = promisify(fs.readdir); @@ -35,17 +37,30 @@ const unlinkAsync = promisify(fs.unlink); const writeFileAsync = promisify(fs.writeFile); export class LogRotator { - constructor(config, server) { + private readonly config: KibanaConfig; + private readonly log: Server['log']; + public logFilePath: string; + public everyBytes: number; + public keepFiles: number; + public running: boolean; + private logFileSize: number; + public isRotating: boolean; + public throttledRotate: () => void; + public stalker: chokidar.FSWatcher | null; + public usePolling: boolean; + public pollingInterval: number; + private stalkerUsePollingPolicyTestTimeout: NodeJS.Timeout | null; + + constructor(config: KibanaConfig, server: Server) { this.config = config; - this.log = server.logWithMetadata; + this.log = server.log.bind(server); this.logFilePath = config.get('logging.dest'); - this.interval = 1; this.everyBytes = config.get('logging.rotate.everyBytes'); this.keepFiles = config.get('logging.rotate.keepFiles'); this.running = false; this.logFileSize = 0; this.isRotating = false; - this.throttledRotate = throttle(async () => { await this._rotate(); }, 5000); + this.throttledRotate = throttle(async () => await this._rotate(), 5000); this.stalker = null; this.usePolling = config.get('logging.rotate.usePolling'); this.pollingInterval = config.get('logging.rotate.pollingInterval'); @@ -96,10 +111,12 @@ export class LogRotator { // setup fs.watch for the temp test file const testWatcher = fs.watch(tempFile, { persistent: false }); - const usePollingTest$ = new Observable(async (observer) => { + // await writeFileAsync(tempFile, 'test'); + + const usePollingTest$ = new Observable(observer => { // observable complete function - const completeFn = (fallbackTimeout, completeStatus) => { - clearTimeout(fallbackTimeout); + const completeFn = (completeStatus: boolean) => { + clearTimeout(this.stalkerUsePollingPolicyTestTimeout as NodeJS.Timeout); testWatcher.close(); observer.next(completeStatus); @@ -107,14 +124,19 @@ export class LogRotator { }; // setup conditions that would fire the observable - const fallbackTimeout = setTimeout(() => completeFn(fallbackTimeout, true), 15000); - testWatcher.on('change', () => completeFn(fallbackTimeout, false)); - testWatcher.on('error', () => completeFn(fallbackTimeout, true)); + this.stalkerUsePollingPolicyTestTimeout = setTimeout(() => completeFn(true), 15000); + testWatcher.on('change', () => completeFn(false)); + testWatcher.on('error', () => completeFn(true)); // fire test watcher events - await writeFileAsync(tempFile, 'test'); + setTimeout(() => { + fs.writeFileSync(tempFile, 'test'); + }, 0); }); + // fs.writeFileSync(tempFile, 'test'); + // await writeFileAsync(tempFile, 'test'); + // wait for the first observable result and consider it as the result // for our use polling test const usePollingTestResult = await usePollingTest$.pipe(first()).toPromise(); @@ -128,7 +150,7 @@ export class LogRotator { } } - _buildWatchCfg(usePolling = false) { + _buildWatchCfg(usePolling: boolean = false) { return { ignoreInitial: true, awaitWriteFinish: false, @@ -137,7 +159,7 @@ export class LogRotator { interval: this.pollingInterval, binaryInterval: this.pollingInterval, alwaysStat: true, - atomic: false + atomic: false, }; } @@ -151,11 +173,16 @@ export class LogRotator { ); } + this.log( + ['warning', 'logging:rotate'], + 'The current environment does not support `fs.watch`. Falling back to polling using `fs.watchFile`' + ); + this.stalker = chokidar.watch(this.logFilePath, this._buildWatchCfg(this.usePolling)); this.stalker.on('change', this._logFileSizeMonitorHandler); } - _logFileSizeMonitorHandler = async (filename, stats) => { + _logFileSizeMonitorHandler = async (filename: string, stats: fs.Stats) => { if (!filename || !stats) { return; } @@ -170,7 +197,7 @@ export class LogRotator { } this.stalker.close(); - clearTimeout(this.stalkerUsePollingPolicyTestTimeout); + clearTimeout(this.stalkerUsePollingPolicyTestTimeout as NodeJS.Timeout); } _createExitListener() { @@ -234,7 +261,9 @@ export class LogRotator { const foundRotatedFiles = await this._readRotatedFilesMetadata(); // delete number of rotated files exceeding the keepFiles limit setting - const rotatedFiles = await this._deleteFoundRotatedFilesAboveKeepFilesLimit(foundRotatedFiles); + const rotatedFiles: string[] = await this._deleteFoundRotatedFilesAboveKeepFilesLimit( + foundRotatedFiles + ); // delete last file await this._deleteLastRotatedFile(rotatedFiles); @@ -259,39 +288,46 @@ export class LogRotator { async _readRotatedFilesMetadata() { const logFileBaseName = basename(this.logFilePath); const logFilesFolder = dirname(this.logFilePath); - - return (await readdirAsync(logFilesFolder)) - .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) - // we use .slice(-1) here in order to retrieve the last number match in the read filenames - .sort((a, b) => Number(a.match(/(\d+)/g).slice(-1)) - Number(b.match(/(\d+)/g).slice(-1))) - .map(filename => `${logFilesFolder}${sep}${filename}`); + const foundLogFiles: string[] = await readdirAsync(logFilesFolder); + + return ( + foundLogFiles + .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) + // we use .slice(-1) here in order to retrieve the last number match in the read filenames + // @ts-ignore here as the warning is not too important + .sort((a, b) => Number(a.match(/(\d+)/g).slice(-1)) - Number(b.match(/(\d+)/g).slice(-1))) + .map(filename => `${logFilesFolder}${sep}${filename}`) + ); } - async _deleteFoundRotatedFilesAboveKeepFilesLimit(foundRotatedFiles) { + async _deleteFoundRotatedFilesAboveKeepFilesLimit(foundRotatedFiles: string[]) { if (foundRotatedFiles.length <= this.keepFiles) { return foundRotatedFiles; } const finalRotatedFiles = foundRotatedFiles.slice(0, this.keepFiles); - const rotatedFilesToDelete = foundRotatedFiles.slice(finalRotatedFiles.length, foundRotatedFiles.length); + const rotatedFilesToDelete = foundRotatedFiles.slice( + finalRotatedFiles.length, + foundRotatedFiles.length + ); await Promise.all( - rotatedFilesToDelete.map(rotatedFilePath => unlinkAsync(rotatedFilePath)) + rotatedFilesToDelete.map((rotatedFilePath: string) => unlinkAsync(rotatedFilePath)) ); return finalRotatedFiles; } - async _deleteLastRotatedFile(rotatedFiles) { + async _deleteLastRotatedFile(rotatedFiles: string[]) { if (rotatedFiles.length < this.keepFiles) { return; } - const lastFilePath = rotatedFiles.pop(); + const lastFilePath: string = rotatedFiles.pop() as string; await unlinkAsync(lastFilePath); } - async _renameRotatedFilesByOne(rotatedFiles) { + async _renameRotatedFilesByOne(rotatedFiles: string[]) { const logFileBaseName = basename(this.logFilePath); const logFilesFolder = dirname(this.logFilePath); @@ -309,6 +345,7 @@ export class LogRotator { _sendReloadLogConfigSignal() { if (isMaster) { + // @ts-ignore process.emit('SIGHUP'); return; } @@ -316,6 +353,7 @@ export class LogRotator { // Send a special message to the cluster manager // so it can forward it correctly // It will only run when we are under cluster mode (not under a production environment) + // @ts-ignore process.send(['RELOAD_LOGGING_CONFIG_FROM_SERVER_WORKER']); } } From c01f8a43089727ef9b3c6a4acc71935c34b46acb Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 5 Dec 2019 18:09:43 +0000 Subject: [PATCH 67/73] chore(NA): type update for log rotation index file --- src/legacy/server/logging/rotate/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/index.ts b/src/legacy/server/logging/rotate/index.ts index ca3345326ce41..447283b4d72c1 100644 --- a/src/legacy/server/logging/rotate/index.ts +++ b/src/legacy/server/logging/rotate/index.ts @@ -22,7 +22,7 @@ import { Server } from 'hapi'; import { LogRotator } from './log_rotator'; import { KibanaConfig } from '../../kbn_server'; -let logRotator: LogRotator | null = null; +let logRotator: LogRotator; export async function setupLoggingRotate(server: Server, config: KibanaConfig) { // If log rotate is not enabled we skip From 579d21e812c9fb0b3272a408647393ccab1a4040 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 5 Dec 2019 18:23:27 +0000 Subject: [PATCH 68/73] docs(NA): add experimental tag on docs --- docs/setup/settings.asciidoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 70ca9b91cb36e..39c87d97af4ba 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -147,7 +147,7 @@ will default to `true`. `logging.quiet:`:: *Default: false* Set the value of this setting to `true` to suppress all logging output other than error messages. -`logging.rotate:`:: Specifies the options for the logging rotate feature. +`logging.rotate:`:: [experimental] Specifies the options for the logging rotate feature. When not defined, all the sub options defaults would be applied. The following example shows a valid logging rotate configuration: + @@ -158,22 +158,22 @@ The following example shows a valid logging rotate configuration: keepFiles: 10 -- -`logging.rotate.enabled:`:: *Default: false* Set the value of this setting to `true` to +`logging.rotate.enabled:`:: [experimental] *Default: false* Set the value of this setting to `true` to enable log rotation. If you do not have a `logging.dest` set that is different from `stdout` that feature would not take any effect. -`logging.rotate.everyBytes:`:: *Default: 10485760* The maximum size of a log file (that is `not an exact` limit). After the +`logging.rotate.everyBytes:`:: [experimental] *Default: 10485760* The maximum size of a log file (that is `not an exact` limit). After the limit is reached, a new log file is generated. The default size limit is 10485760 (10 MB) and this option should be at least greater than 1024. -`logging.rotate.keepFiles:`:: *Default: 7* The number of most recent rotated log files to keep +`logging.rotate.keepFiles:`:: [experimental] *Default: 7* The number of most recent rotated log files to keep on disk. Older files are deleted during log rotation. The default value is 7. The `logging.rotate.keepFiles` option has to be in the range of 2 to 1024 files. -`logging.rotate.pollingInterval:`:: *Default: 10000* The number of milliseconds for the polling strategy in case +`logging.rotate.pollingInterval:`:: [experimental] *Default: 10000* The number of milliseconds for the polling strategy in case the `logging.rotate.usePolling` is enabled. That option has to be in the range of 5000 to 3600000 milliseconds. -`logging.rotate.usePolling:`:: *Default: false* By default we try to understand the best way to monitoring +`logging.rotate.usePolling:`:: [experimental] *Default: false* By default we try to understand the best way to monitoring the log file. However, there is some systems where it could not be always accurate. In those cases, if needed, the `polling` method could be used enabling that option. From 94c625f0f5c9e3314a2ced1e5a4ddbfc71214107 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 5 Dec 2019 21:21:14 +0000 Subject: [PATCH 69/73] chore(NA): add call protection of clearTimeout --- src/legacy/server/logging/rotate/log_rotator.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.ts b/src/legacy/server/logging/rotate/log_rotator.ts index 51b8a766281e4..a438731f06a3d 100644 --- a/src/legacy/server/logging/rotate/log_rotator.ts +++ b/src/legacy/server/logging/rotate/log_rotator.ts @@ -116,7 +116,9 @@ export class LogRotator { const usePollingTest$ = new Observable(observer => { // observable complete function const completeFn = (completeStatus: boolean) => { - clearTimeout(this.stalkerUsePollingPolicyTestTimeout as NodeJS.Timeout); + if (this.stalkerUsePollingPolicyTestTimeout) { + clearTimeout(this.stalkerUsePollingPolicyTestTimeout); + } testWatcher.close(); observer.next(completeStatus); @@ -197,7 +199,10 @@ export class LogRotator { } this.stalker.close(); - clearTimeout(this.stalkerUsePollingPolicyTestTimeout as NodeJS.Timeout); + + if (this.stalkerUsePollingPolicyTestTimeout) { + clearTimeout(this.stalkerUsePollingPolicyTestTimeout); + } } _createExitListener() { From 0c7c7796a7c5314914719ae8a3e00542ad901727 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 7 Dec 2019 01:37:19 +0000 Subject: [PATCH 70/73] refact(NA): cleanup comments and wrong added logs plus inline config --- src/legacy/server/logging/rotate/index.ts | 5 --- .../server/logging/rotate/log_rotator.ts | 32 ++++++------------- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/legacy/server/logging/rotate/index.ts b/src/legacy/server/logging/rotate/index.ts index 447283b4d72c1..646c89efe8e20 100644 --- a/src/legacy/server/logging/rotate/index.ts +++ b/src/legacy/server/logging/rotate/index.ts @@ -46,11 +46,6 @@ export async function setupLoggingRotate(server: Server, config: KibanaConfig) { return; } - server.log( - ['warning', 'logging:rotate'], - 'Log rotation is enabled but logging.dest is configured for stdout. Set logging.dest to a file for this setting to take effect.' - ); - // Enable Logging Rotate Service // We need the master process and it can // try to setupLoggingRotate more than once, diff --git a/src/legacy/server/logging/rotate/log_rotator.ts b/src/legacy/server/logging/rotate/log_rotator.ts index a438731f06a3d..a8a89312f8a56 100644 --- a/src/legacy/server/logging/rotate/log_rotator.ts +++ b/src/legacy/server/logging/rotate/log_rotator.ts @@ -136,9 +136,6 @@ export class LogRotator { }, 0); }); - // fs.writeFileSync(tempFile, 'test'); - // await writeFileAsync(tempFile, 'test'); - // wait for the first observable result and consider it as the result // for our use polling test const usePollingTestResult = await usePollingTest$.pipe(first()).toPromise(); @@ -152,19 +149,6 @@ export class LogRotator { } } - _buildWatchCfg(usePolling: boolean = false) { - return { - ignoreInitial: true, - awaitWriteFinish: false, - useFsEvents: false, - usePolling, - interval: this.pollingInterval, - binaryInterval: this.pollingInterval, - alwaysStat: true, - atomic: false, - }; - } - async _startLogFileSizeMonitor() { this.usePolling = await this._shouldUsePolling(); @@ -175,12 +159,16 @@ export class LogRotator { ); } - this.log( - ['warning', 'logging:rotate'], - 'The current environment does not support `fs.watch`. Falling back to polling using `fs.watchFile`' - ); - - this.stalker = chokidar.watch(this.logFilePath, this._buildWatchCfg(this.usePolling)); + this.stalker = chokidar.watch(this.logFilePath, { + ignoreInitial: true, + awaitWriteFinish: false, + useFsEvents: false, + usePolling: this.usePolling, + interval: this.pollingInterval, + binaryInterval: this.pollingInterval, + alwaysStat: true, + atomic: false, + }); this.stalker.on('change', this._logFileSizeMonitorHandler); } From c789425da93af708747c75c6ca63ea6450416eb9 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 7 Dec 2019 01:41:11 +0000 Subject: [PATCH 71/73] chore(NA): replace ts-ignore by non null assertion operator --- src/legacy/server/logging/rotate/log_rotator.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.ts b/src/legacy/server/logging/rotate/log_rotator.ts index a8a89312f8a56..eb743014f9ffa 100644 --- a/src/legacy/server/logging/rotate/log_rotator.ts +++ b/src/legacy/server/logging/rotate/log_rotator.ts @@ -287,8 +287,7 @@ export class LogRotator { foundLogFiles .filter(file => new RegExp(`${logFileBaseName}\\.\\d`).test(file)) // we use .slice(-1) here in order to retrieve the last number match in the read filenames - // @ts-ignore here as the warning is not too important - .sort((a, b) => Number(a.match(/(\d+)/g).slice(-1)) - Number(b.match(/(\d+)/g).slice(-1))) + .sort((a, b) => Number(a.match(/(\d+)/g)!.slice(-1)) - Number(b.match(/(\d+)/g)!.slice(-1))) .map(filename => `${logFilesFolder}${sep}${filename}`) ); } From 1256e3d39ee3df2e96cef594d94167973d7483d5 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 7 Dec 2019 01:45:27 +0000 Subject: [PATCH 72/73] docs(NA): extend documentation for _renameRotatedFilesByOne call --- src/legacy/server/logging/rotate/log_rotator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.ts b/src/legacy/server/logging/rotate/log_rotator.ts index eb743014f9ffa..9590267a3631c 100644 --- a/src/legacy/server/logging/rotate/log_rotator.ts +++ b/src/legacy/server/logging/rotate/log_rotator.ts @@ -262,7 +262,9 @@ export class LogRotator { await this._deleteLastRotatedFile(rotatedFiles); // rename all files to correct index + 1 - // and normalize + // and normalize numbering if by some reason + // (for example log file deletion) that numbering + // was interrupted await this._renameRotatedFilesByOne(rotatedFiles); // rename current log into 0 From 67041cba75e027f2a838b60555f1580fa8007df3 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Sat, 7 Dec 2019 02:08:27 +0000 Subject: [PATCH 73/73] chore(NA): fix type problems for process.emit on nodejs --- src/legacy/server/logging/rotate/log_rotator.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/legacy/server/logging/rotate/log_rotator.ts b/src/legacy/server/logging/rotate/log_rotator.ts index 9590267a3631c..3662910ca5a7b 100644 --- a/src/legacy/server/logging/rotate/log_rotator.ts +++ b/src/legacy/server/logging/rotate/log_rotator.ts @@ -339,15 +339,21 @@ export class LogRotator { _sendReloadLogConfigSignal() { if (isMaster) { - // @ts-ignore - process.emit('SIGHUP'); + (process as NodeJS.EventEmitter).emit('SIGHUP'); return; } // Send a special message to the cluster manager // so it can forward it correctly // It will only run when we are under cluster mode (not under a production environment) - // @ts-ignore + if (!process.send) { + this.log( + ['error', 'logging:rotate'], + 'For some unknown reason process.send is not defined, the rotation was not successful' + ); + return; + } + process.send(['RELOAD_LOGGING_CONFIG_FROM_SERVER_WORKER']); } }