mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Move logger configuration integration test to jest (#70378)
This commit is contained in:
parent
1d1051b1e9
commit
7ed1fe05d7
8 changed files with 161 additions and 309 deletions
|
@ -18,6 +18,9 @@
|
|||
*/
|
||||
|
||||
import * as kbnTestServer from '../../../../test_utils/kbn_server';
|
||||
import { InternalCoreSetup } from '../../internal_types';
|
||||
import { LoggerContextConfigInput } from '../logging_config';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
function createRoot() {
|
||||
return kbnTestServer.createRoot({
|
||||
|
@ -111,4 +114,162 @@ describe('logging service', () => {
|
|||
expect(mockConsoleLog).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom context configuration', () => {
|
||||
const CUSTOM_LOGGING_CONFIG: LoggerContextConfigInput = {
|
||||
appenders: {
|
||||
customJsonConsole: {
|
||||
kind: 'console',
|
||||
layout: {
|
||||
kind: 'json',
|
||||
},
|
||||
},
|
||||
customPatternConsole: {
|
||||
kind: 'console',
|
||||
layout: {
|
||||
kind: 'pattern',
|
||||
pattern: 'CUSTOM - PATTERN [%logger][%level] %message',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
loggers: [
|
||||
{ context: 'debug_json', appenders: ['customJsonConsole'], level: 'debug' },
|
||||
{ context: 'debug_pattern', appenders: ['customPatternConsole'], level: 'debug' },
|
||||
{ context: 'info_json', appenders: ['customJsonConsole'], level: 'info' },
|
||||
{ context: 'info_pattern', appenders: ['customPatternConsole'], level: 'info' },
|
||||
{
|
||||
context: 'all',
|
||||
appenders: ['customJsonConsole', 'customPatternConsole'],
|
||||
level: 'debug',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let root: ReturnType<typeof createRoot>;
|
||||
let setup: InternalCoreSetup;
|
||||
let mockConsoleLog: jest.SpyInstance;
|
||||
const loggingConfig$ = new Subject<LoggerContextConfigInput>();
|
||||
const setContextConfig = (enable: boolean) =>
|
||||
enable ? loggingConfig$.next(CUSTOM_LOGGING_CONFIG) : loggingConfig$.next({});
|
||||
beforeAll(async () => {
|
||||
mockConsoleLog = jest.spyOn(global.console, 'log');
|
||||
root = kbnTestServer.createRoot();
|
||||
|
||||
setup = await root.setup();
|
||||
setup.logging.configure(['plugins', 'myplugin'], loggingConfig$);
|
||||
}, 30000);
|
||||
|
||||
beforeEach(() => {
|
||||
mockConsoleLog.mockClear();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
mockConsoleLog.mockRestore();
|
||||
await root.shutdown();
|
||||
});
|
||||
|
||||
it('does not write to custom appenders when not configured', async () => {
|
||||
const logger = root.logger.get('plugins.myplugin.debug_pattern');
|
||||
setContextConfig(false);
|
||||
logger.info('log1');
|
||||
setContextConfig(true);
|
||||
logger.debug('log2');
|
||||
logger.info('log3');
|
||||
setContextConfig(false);
|
||||
logger.info('log4');
|
||||
expect(mockConsoleLog).toHaveBeenCalledTimes(2);
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
'CUSTOM - PATTERN [plugins.myplugin.debug_pattern][DEBUG] log2'
|
||||
);
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
'CUSTOM - PATTERN [plugins.myplugin.debug_pattern][INFO ] log3'
|
||||
);
|
||||
});
|
||||
|
||||
it('writes debug_json context to custom JSON appender', async () => {
|
||||
setContextConfig(true);
|
||||
const logger = root.logger.get('plugins.myplugin.debug_json');
|
||||
logger.debug('log1');
|
||||
logger.info('log2');
|
||||
expect(mockConsoleLog).toHaveBeenCalledTimes(2);
|
||||
|
||||
const [firstCall, secondCall] = mockConsoleLog.mock.calls.map(([jsonString]) =>
|
||||
JSON.parse(jsonString)
|
||||
);
|
||||
expect(firstCall).toMatchObject({
|
||||
level: 'DEBUG',
|
||||
context: 'plugins.myplugin.debug_json',
|
||||
message: 'log1',
|
||||
});
|
||||
expect(secondCall).toMatchObject({
|
||||
level: 'INFO',
|
||||
context: 'plugins.myplugin.debug_json',
|
||||
message: 'log2',
|
||||
});
|
||||
});
|
||||
|
||||
it('writes info_json context to custom JSON appender', async () => {
|
||||
setContextConfig(true);
|
||||
const logger = root.logger.get('plugins.myplugin.info_json');
|
||||
logger.debug('i should not be logged!');
|
||||
logger.info('log2');
|
||||
|
||||
expect(mockConsoleLog).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchObject({
|
||||
level: 'INFO',
|
||||
context: 'plugins.myplugin.info_json',
|
||||
message: 'log2',
|
||||
});
|
||||
});
|
||||
|
||||
it('writes debug_pattern context to custom pattern appender', async () => {
|
||||
setContextConfig(true);
|
||||
const logger = root.logger.get('plugins.myplugin.debug_pattern');
|
||||
logger.debug('log1');
|
||||
logger.info('log2');
|
||||
|
||||
expect(mockConsoleLog).toHaveBeenCalledTimes(2);
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
'CUSTOM - PATTERN [plugins.myplugin.debug_pattern][DEBUG] log1'
|
||||
);
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
'CUSTOM - PATTERN [plugins.myplugin.debug_pattern][INFO ] log2'
|
||||
);
|
||||
});
|
||||
|
||||
it('writes info_pattern context to custom pattern appender', async () => {
|
||||
setContextConfig(true);
|
||||
const logger = root.logger.get('plugins.myplugin.info_pattern');
|
||||
logger.debug('i should not be logged!');
|
||||
logger.info('log2');
|
||||
expect(mockConsoleLog).toHaveBeenCalledTimes(1);
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
'CUSTOM - PATTERN [plugins.myplugin.info_pattern][INFO ] log2'
|
||||
);
|
||||
});
|
||||
|
||||
it('writes all context to both appenders', async () => {
|
||||
setContextConfig(true);
|
||||
const logger = root.logger.get('plugins.myplugin.all');
|
||||
logger.debug('log1');
|
||||
logger.info('log2');
|
||||
|
||||
expect(mockConsoleLog).toHaveBeenCalledTimes(4);
|
||||
const logs = mockConsoleLog.mock.calls.map(([jsonString]) => jsonString);
|
||||
|
||||
expect(JSON.parse(logs[0])).toMatchObject({
|
||||
level: 'DEBUG',
|
||||
context: 'plugins.myplugin.all',
|
||||
message: 'log1',
|
||||
});
|
||||
expect(logs[1]).toEqual('CUSTOM - PATTERN [plugins.myplugin.all][DEBUG] log1');
|
||||
expect(JSON.parse(logs[2])).toMatchObject({
|
||||
level: 'INFO',
|
||||
context: 'plugins.myplugin.all',
|
||||
message: 'log2',
|
||||
});
|
||||
expect(logs[3]).toEqual('CUSTOM - PATTERN [plugins.myplugin.all][INFO ] log2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"id": "core_logging",
|
||||
"version": "0.0.1",
|
||||
"kibanaVersion": "kibana",
|
||||
"configPath": ["core_logging"],
|
||||
"server": true
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
/*debug.log
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* 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 type { PluginInitializerContext } from '../../../../../src/core/server';
|
||||
import { CoreLoggingPlugin } from './plugin';
|
||||
|
||||
export const plugin = (init: PluginInitializerContext) => new CoreLoggingPlugin(init);
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* 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 { resolve } from 'path';
|
||||
import { Subject } from 'rxjs';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import type {
|
||||
PluginInitializerContext,
|
||||
Plugin,
|
||||
CoreSetup,
|
||||
LoggerContextConfigInput,
|
||||
Logger,
|
||||
} from '../../../../../src/core/server';
|
||||
|
||||
const CUSTOM_LOGGING_CONFIG: LoggerContextConfigInput = {
|
||||
appenders: {
|
||||
customJsonFile: {
|
||||
kind: 'file',
|
||||
path: resolve(__dirname, 'json_debug.log'), // use 'debug.log' suffix so file watcher does not restart server
|
||||
layout: {
|
||||
kind: 'json',
|
||||
},
|
||||
},
|
||||
customPatternFile: {
|
||||
kind: 'file',
|
||||
path: resolve(__dirname, 'pattern_debug.log'),
|
||||
layout: {
|
||||
kind: 'pattern',
|
||||
pattern: 'CUSTOM - PATTERN [%logger][%level] %message',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
loggers: [
|
||||
{ context: 'debug_json', appenders: ['customJsonFile'], level: 'debug' },
|
||||
{ context: 'debug_pattern', appenders: ['customPatternFile'], level: 'debug' },
|
||||
{ context: 'info_json', appenders: ['customJsonFile'], level: 'info' },
|
||||
{ context: 'info_pattern', appenders: ['customPatternFile'], level: 'info' },
|
||||
{ context: 'all', appenders: ['customJsonFile', 'customPatternFile'], level: 'debug' },
|
||||
],
|
||||
};
|
||||
|
||||
export class CoreLoggingPlugin implements Plugin {
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(init: PluginInitializerContext) {
|
||||
this.logger = init.logger.get();
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup) {
|
||||
const loggingConfig$ = new Subject<LoggerContextConfigInput>();
|
||||
core.logging.configure(loggingConfig$);
|
||||
|
||||
const router = core.http.createRouter();
|
||||
|
||||
// Expose a route that allows our test suite to write logs as this plugin
|
||||
router.post(
|
||||
{
|
||||
path: '/internal/core-logging/write-log',
|
||||
validate: {
|
||||
body: schema.object({
|
||||
level: schema.oneOf([schema.literal('debug'), schema.literal('info')]),
|
||||
message: schema.string(),
|
||||
context: schema.arrayOf(schema.string()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
(ctx, req, res) => {
|
||||
const { level, message, context } = req.body;
|
||||
const logger = this.logger.get(...context);
|
||||
|
||||
if (level === 'debug') {
|
||||
logger.debug(message);
|
||||
} else if (level === 'info') {
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
return res.ok();
|
||||
}
|
||||
);
|
||||
|
||||
// Expose a route to toggle on and off the custom config
|
||||
router.post(
|
||||
{
|
||||
path: '/internal/core-logging/update-config',
|
||||
validate: { body: schema.object({ enableCustomConfig: schema.boolean() }) },
|
||||
},
|
||||
(ctx, req, res) => {
|
||||
if (req.body.enableCustomConfig) {
|
||||
loggingConfig$.next(CUSTOM_LOGGING_CONFIG);
|
||||
} else {
|
||||
loggingConfig$.next({});
|
||||
}
|
||||
|
||||
return res.ok({ body: `Updated config: ${req.body.enableCustomConfig}` });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public start() {}
|
||||
public stop() {}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./target",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"server/**/*.ts",
|
||||
"../../../../typings/**/*",
|
||||
],
|
||||
"exclude": []
|
||||
}
|
|
@ -30,6 +30,5 @@ export default function ({ loadTestFile }: PluginFunctionalProviderContext) {
|
|||
loadTestFile(require.resolve('./application_leave_confirm'));
|
||||
loadTestFile(require.resolve('./application_status'));
|
||||
loadTestFile(require.resolve('./rendering'));
|
||||
loadTestFile(require.resolve('./logging'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
* 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 { resolve } from 'path';
|
||||
import fs from 'fs';
|
||||
import expect from '@kbn/expect';
|
||||
import { PluginFunctionalProviderContext } from '../../services';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getService }: PluginFunctionalProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('plugin logging', function describeIndexTests() {
|
||||
const LOG_FILE_DIRECTORY = resolve(__dirname, '..', '..', 'plugins', 'core_logging', 'server');
|
||||
const JSON_FILE_PATH = resolve(LOG_FILE_DIRECTORY, 'json_debug.log');
|
||||
const PATTERN_FILE_PATH = resolve(LOG_FILE_DIRECTORY, 'pattern_debug.log');
|
||||
|
||||
beforeEach(async () => {
|
||||
// "touch" each file to ensure it exists and is empty before each test
|
||||
await fs.promises.writeFile(JSON_FILE_PATH, '');
|
||||
await fs.promises.writeFile(PATTERN_FILE_PATH, '');
|
||||
});
|
||||
|
||||
async function readLines(path: string) {
|
||||
const contents = await fs.promises.readFile(path, { encoding: 'utf8' });
|
||||
return contents.trim().split('\n');
|
||||
}
|
||||
|
||||
async function readJsonLines() {
|
||||
return (await readLines(JSON_FILE_PATH))
|
||||
.filter((line) => line.length > 0)
|
||||
.map((line) => JSON.parse(line))
|
||||
.map(({ level, message, context }) => ({ level, message, context }));
|
||||
}
|
||||
|
||||
function writeLog(context: string[], level: string, message: string) {
|
||||
return supertest
|
||||
.post('/internal/core-logging/write-log')
|
||||
.set('kbn-xsrf', 'anything')
|
||||
.send({ context, level, message })
|
||||
.expect(200);
|
||||
}
|
||||
|
||||
function setContextConfig(enable: boolean) {
|
||||
return supertest
|
||||
.post('/internal/core-logging/update-config')
|
||||
.set('kbn-xsrf', 'anything')
|
||||
.send({ enableCustomConfig: enable })
|
||||
.expect(200);
|
||||
}
|
||||
|
||||
it('does not write to custom appenders when not configured', async () => {
|
||||
await setContextConfig(false);
|
||||
await writeLog(['debug_json'], 'info', 'i go to the default appender!');
|
||||
expect(await readJsonLines()).to.eql([]);
|
||||
});
|
||||
|
||||
it('writes debug_json context to custom JSON appender', async () => {
|
||||
await setContextConfig(true);
|
||||
await writeLog(['debug_json'], 'debug', 'log1');
|
||||
await writeLog(['debug_json'], 'info', 'log2');
|
||||
expect(await readJsonLines()).to.eql([
|
||||
{
|
||||
level: 'DEBUG',
|
||||
context: 'plugins.core_logging.debug_json',
|
||||
message: 'log1',
|
||||
},
|
||||
{
|
||||
level: 'INFO',
|
||||
context: 'plugins.core_logging.debug_json',
|
||||
message: 'log2',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('writes info_json context to custom JSON appender', async () => {
|
||||
await setContextConfig(true);
|
||||
await writeLog(['info_json'], 'debug', 'i should not be logged!');
|
||||
await writeLog(['info_json'], 'info', 'log2');
|
||||
expect(await readJsonLines()).to.eql([
|
||||
{
|
||||
level: 'INFO',
|
||||
context: 'plugins.core_logging.info_json',
|
||||
message: 'log2',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('writes debug_pattern context to custom pattern appender', async () => {
|
||||
await setContextConfig(true);
|
||||
await writeLog(['debug_pattern'], 'debug', 'log1');
|
||||
await writeLog(['debug_pattern'], 'info', 'log2');
|
||||
expect(await readLines(PATTERN_FILE_PATH)).to.eql([
|
||||
'CUSTOM - PATTERN [plugins.core_logging.debug_pattern][DEBUG] log1',
|
||||
'CUSTOM - PATTERN [plugins.core_logging.debug_pattern][INFO ] log2',
|
||||
]);
|
||||
});
|
||||
|
||||
it('writes info_pattern context to custom pattern appender', async () => {
|
||||
await setContextConfig(true);
|
||||
await writeLog(['info_pattern'], 'debug', 'i should not be logged!');
|
||||
await writeLog(['info_pattern'], 'info', 'log2');
|
||||
expect(await readLines(PATTERN_FILE_PATH)).to.eql([
|
||||
'CUSTOM - PATTERN [plugins.core_logging.info_pattern][INFO ] log2',
|
||||
]);
|
||||
});
|
||||
|
||||
it('writes all context to both appenders', async () => {
|
||||
await setContextConfig(true);
|
||||
await writeLog(['all'], 'debug', 'log1');
|
||||
await writeLog(['all'], 'info', 'log2');
|
||||
expect(await readJsonLines()).to.eql([
|
||||
{
|
||||
level: 'DEBUG',
|
||||
context: 'plugins.core_logging.all',
|
||||
message: 'log1',
|
||||
},
|
||||
{
|
||||
level: 'INFO',
|
||||
context: 'plugins.core_logging.all',
|
||||
message: 'log2',
|
||||
},
|
||||
]);
|
||||
expect(await readLines(PATTERN_FILE_PATH)).to.eql([
|
||||
'CUSTOM - PATTERN [plugins.core_logging.all][DEBUG] log1',
|
||||
'CUSTOM - PATTERN [plugins.core_logging.all][INFO ] log2',
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue