From 003cd4371e9e289c71d76a92e27155149db2f31c Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 31 Jan 2020 12:07:04 +0100 Subject: [PATCH] add integration tests --- .../legacy/integration_tests/logging.test.ts | 220 ++++++++++++++++++ .../logging/integration_tests/logging.test.ts | 114 +++++++++ .../server/logging/integration_tests/utils.ts | 57 +++++ 3 files changed, 391 insertions(+) create mode 100644 src/core/server/legacy/integration_tests/logging.test.ts create mode 100644 src/core/server/logging/integration_tests/logging.test.ts create mode 100644 src/core/server/logging/integration_tests/utils.ts diff --git a/src/core/server/legacy/integration_tests/logging.test.ts b/src/core/server/legacy/integration_tests/logging.test.ts new file mode 100644 index 00000000000000..a91a4e1278453c --- /dev/null +++ b/src/core/server/legacy/integration_tests/logging.test.ts @@ -0,0 +1,220 @@ +/* + * 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 Fs from 'fs'; +import Path from 'path'; +import Util from 'util'; +import del from 'del'; + +import * as kbnTestServer from '../../../../test_utils/kbn_server'; +import { fromRoot } from '../../utils'; +import { + getPlatformLoggingContent as _getPlatformLoggingContent, + getLegacyPlatformLoggingContent as _getLegacyPlatformLoggingContent, +} from '../../logging/integration_tests/utils'; + +import { LegacyLoggingConfig } from '../config/legacy_object_to_config_adapter'; + +const mkdir = Util.promisify(Fs.mkdir); +const truncate = Util.promisify(Fs.truncate); + +const tempFolderPath = fromRoot('data/test/tmp-logging-service'); +const platformDestination = Path.join(tempFolderPath, 'compatibility-np.txt'); +const legacyPlatformDestination = Path.join(tempFolderPath, 'compatibility-lp.txt'); + +const getPlatformLoggingContent = () => _getPlatformLoggingContent(platformDestination); +const getLegacyPlatformLoggingContent = () => + _getLegacyPlatformLoggingContent(legacyPlatformDestination); + +function createRoot(legacyLoggingConfig: LegacyLoggingConfig = {}) { + return kbnTestServer.createRoot({ + logging: { + // legacy platform config + silent: false, + dest: legacyPlatformDestination, + json: false, + ...legacyLoggingConfig, + events: { + log: ['test-file-legacy'], + }, + // platform config + appenders: { + file: { + kind: 'file', + path: platformDestination, + layout: { + kind: 'pattern', + }, + }, + }, + loggers: [ + { + context: 'test-file', + appenders: ['file'], + level: 'info', + }, + ], + }, + }); +} + +describe('logging service', () => { + describe('compatibility', () => { + beforeAll(async () => { + await mkdir(tempFolderPath, { recursive: true }); + }); + afterAll(async () => { + await del(tempFolderPath); + }); + afterEach(async () => { + await truncate(platformDestination); + await truncate(legacyPlatformDestination); + }); + describe('uses configured loggers', () => { + let root: ReturnType; + beforeAll(async () => { + root = createRoot(); + + await root.setup(); + await root.start(); + }, 30000); + + afterAll(async () => { + await root.shutdown(); + }); + + it('when context matches', async () => { + root.logger.get('test-file').info('handled by NP'); + + expect(await getPlatformLoggingContent()).toMatchInlineSnapshot(` + "[xxxx-xx-xxTxx:xx:xx.xxxZ][INFO ][test-file] handled by NP + " + `); + + expect(await getLegacyPlatformLoggingContent()).toHaveLength(0); + }); + + it('falls back to the root legacy logger otherwise', async () => { + root.logger.get('test-file-legacy').info('handled by LP'); + + expect(await getLegacyPlatformLoggingContent()).toMatchInlineSnapshot(` + " log [xx:xx:xx.xxx] [info][test-file-legacy] handled by LP + " + `); + expect(await getPlatformLoggingContent()).toHaveLength(0); + }); + }); + + describe('logging config respects legacy logging settings', () => { + it('"silent": true', async () => { + const root = createRoot({ silent: true }); + + await root.setup(); + await root.start(); + + const platformLogger = root.logger.get('test-file'); + platformLogger.info('info'); + platformLogger.warn('warn'); + platformLogger.error('error'); + + const legacyPlatformLogger = root.logger.get('test-file-legacy'); + legacyPlatformLogger.info('info'); + legacyPlatformLogger.warn('warn'); + legacyPlatformLogger.error('error'); + + // calls shutdown to close write stream and flush logged messages + await root.shutdown(); + + expect(await getPlatformLoggingContent()).toMatchInlineSnapshot(` + "[xxxx-xx-xxTxx:xx:xx.xxxZ][INFO ][test-file] info + [xxxx-xx-xxTxx:xx:xx.xxxZ][WARN ][test-file] warn + [xxxx-xx-xxTxx:xx:xx.xxxZ][ERROR][test-file] error + " + `); + + expect(await getLegacyPlatformLoggingContent()).toHaveLength(0); + }); + + it('"quiet": true', async () => { + const root = createRoot({ quiet: true }); + + await root.setup(); + await root.start(); + + const platformLogger = root.logger.get('test-file'); + platformLogger.info('info'); + platformLogger.warn('warn'); + platformLogger.error('error'); + + const legacyPlatformLogger = root.logger.get('test-file-legacy'); + legacyPlatformLogger.info('info'); + legacyPlatformLogger.warn('warn'); + legacyPlatformLogger.error('error'); + + // calls shutdown to close write stream and flush logged messages + await root.shutdown(); + + expect(await getPlatformLoggingContent()).toMatchInlineSnapshot(` + "[xxxx-xx-xxTxx:xx:xx.xxxZ][INFO ][test-file] info + [xxxx-xx-xxTxx:xx:xx.xxxZ][WARN ][test-file] warn + [xxxx-xx-xxTxx:xx:xx.xxxZ][ERROR][test-file] error + " + `); + + expect(await getLegacyPlatformLoggingContent()).toMatchInlineSnapshot(` + " log [xx:xx:xx.xxx] [error][test-file-legacy] error + " + `); + }); + + it('"verbose": true', async () => { + const root = createRoot({ verbose: true }); + + await root.setup(); + await root.start(); + + const platformLogger = root.logger.get('test-file'); + platformLogger.info('info'); + platformLogger.warn('warn'); + platformLogger.error('error'); + + const legacyPlatformLogger = root.logger.get('test-file-legacy'); + legacyPlatformLogger.info('info'); + legacyPlatformLogger.warn('warn'); + legacyPlatformLogger.error('error'); + + // calls shutdown to close write stream and flush logged messages + await root.shutdown(); + + expect(await getPlatformLoggingContent()).toMatchInlineSnapshot(` + "[xxxx-xx-xxTxx:xx:xx.xxxZ][INFO ][test-file] info + [xxxx-xx-xxTxx:xx:xx.xxxZ][WARN ][test-file] warn + [xxxx-xx-xxTxx:xx:xx.xxxZ][ERROR][test-file] error + " + `); + + expect(await getLegacyPlatformLoggingContent()).toMatchInlineSnapshot(` + " log [xx:xx:xx.xxx] [info][test-file-legacy] info + log [xx:xx:xx.xxx] [warning][test-file-legacy] warn + log [xx:xx:xx.xxx] [error][test-file-legacy] error + " + `); + }); + }); + }); +}); diff --git a/src/core/server/logging/integration_tests/logging.test.ts b/src/core/server/logging/integration_tests/logging.test.ts new file mode 100644 index 00000000000000..7142f91300f124 --- /dev/null +++ b/src/core/server/logging/integration_tests/logging.test.ts @@ -0,0 +1,114 @@ +/* + * 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 * as kbnTestServer from '../../../../test_utils/kbn_server'; + +function createRoot() { + return kbnTestServer.createRoot({ + logging: { + silent: true, // set "true" in kbnTestServer + appenders: { + 'test-console': { + kind: 'console', + layout: { + highlight: false, + kind: 'pattern', + pattern: '{level}|{context}|{message}', + }, + }, + }, + loggers: [ + { + context: 'parent', + appenders: ['test-console'], + level: 'warn', + }, + { + context: 'parent.child', + appenders: ['test-console'], + level: 'error', + }, + ], + }, + }); +} + +describe('logging service', () => { + describe('logs according to context hierarchy', () => { + let root: ReturnType; + let mockConsoleLog: jest.SpyInstance; + beforeAll(async () => { + mockConsoleLog = jest.spyOn(global.console, 'log'); + root = createRoot(); + + await root.setup(); + }, 30000); + + beforeEach(() => { + mockConsoleLog.mockClear(); + }); + + afterAll(async () => { + mockConsoleLog.mockRestore(); + await root.shutdown(); + }); + + it('uses the most specific context', () => { + const logger = root.logger.get('parent.child'); + + logger.error('error from "parent.child" context'); + logger.warn('warning from "parent.child" context'); + logger.info('info from "parent.child" context'); + + expect(mockConsoleLog).toHaveBeenCalledTimes(1); + expect(mockConsoleLog).toHaveBeenCalledWith( + 'ERROR|parent.child|error from "parent.child" context' + ); + }); + + it('uses parent context', () => { + const logger = root.logger.get('parent.another-child'); + + logger.error('error from "parent.another-child" context'); + logger.warn('warning from "parent.another-child" context'); + logger.info('info from "parent.another-child" context'); + + expect(mockConsoleLog).toHaveBeenCalledTimes(2); + expect(mockConsoleLog).toHaveBeenNthCalledWith( + 1, + 'ERROR|parent.another-child|error from "parent.another-child" context' + ); + expect(mockConsoleLog).toHaveBeenNthCalledWith( + 2, + 'WARN |parent.another-child|warning from "parent.another-child" context' + ); + }); + + it('falls back to the root settings', () => { + const logger = root.logger.get('fallback'); + + logger.error('error from "fallback" context'); + logger.warn('warning from fallback" context'); + logger.info('info from "fallback" context'); + + // output muted by silent: true + expect(mockConsoleLog).toHaveBeenCalledTimes(0); + }); + }); +}); diff --git a/src/core/server/logging/integration_tests/utils.ts b/src/core/server/logging/integration_tests/utils.ts new file mode 100644 index 00000000000000..925118b2e58656 --- /dev/null +++ b/src/core/server/logging/integration_tests/utils.ts @@ -0,0 +1,57 @@ +/* + * 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 Fs from 'fs'; +import Util from 'util'; +const readFile = Util.promisify(Fs.readFile); + +function replaceAllNumbers(input: string) { + return input.replace(/\d/g, 'x'); +} + +function replaceTimestamp(input: string) { + return input.replace(/\[(.*?)\]/, (full, key) => `[${replaceAllNumbers(key)}]`); +} + +function stripColors(input: string) { + return input.replace(/\u001b[^m]+m/g, ''); +} + +export function normalizePlatformLogging(input: string) { + return replaceTimestamp(input); +} + +export function normalizeLegacyPlatformLogging(input: string) { + return replaceTimestamp(stripColors(input)); +} + +export async function getPlatformLoggingContent(path: string) { + const fileContent = await readFile(path, 'utf-8'); + return fileContent + .split('\n') + .map(s => normalizePlatformLogging(s)) + .join('\n'); +} + +export async function getLegacyPlatformLoggingContent(path: string) { + const fileContent = await readFile(path, 'utf-8'); + return fileContent + .split('\n') + .map(s => normalizeLegacyPlatformLogging(s)) + .join('\n'); +}