Add plugin API for customizing the logging configuration (#68704)

This commit is contained in:
Josh Dover 2020-06-23 14:45:47 -06:00 committed by GitHub
parent 71d54c8cae
commit 200957bb63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
149 changed files with 1956 additions and 685 deletions

View file

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [AppenderConfigType](./kibana-plugin-core-server.appenderconfigtype.md)
## AppenderConfigType type
<b>Signature:</b>
```typescript
export declare type AppenderConfigType = TypeOf<typeof appendersSchema>;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [CoreSetup](./kibana-plugin-core-server.coresetup.md) &gt; [logging](./kibana-plugin-core-server.coresetup.logging.md)
## CoreSetup.logging property
[LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md)
<b>Signature:</b>
```typescript
logging: LoggingServiceSetup;
```

View file

@ -21,6 +21,7 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
| [elasticsearch](./kibana-plugin-core-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) |
| [getStartServices](./kibana-plugin-core-server.coresetup.getstartservices.md) | <code>StartServicesAccessor&lt;TPluginsStart, TStart&gt;</code> | [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) |
| [http](./kibana-plugin-core-server.coresetup.http.md) | <code>HttpServiceSetup &amp; {</code><br/><code> resources: HttpResources;</code><br/><code> }</code> | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) |
| [logging](./kibana-plugin-core-server.coresetup.logging.md) | <code>LoggingServiceSetup</code> | [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) |
| [metrics](./kibana-plugin-core-server.coresetup.metrics.md) | <code>MetricsServiceSetup</code> | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) |
| [savedObjects](./kibana-plugin-core-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) |
| [status](./kibana-plugin-core-server.coresetup.status.md) | <code>StatusServiceSetup</code> | [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) |

View file

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [LoggerConfigType](./kibana-plugin-core-server.loggerconfigtype.md)
## LoggerConfigType type
<b>Signature:</b>
```typescript
export declare type LoggerConfigType = TypeOf<typeof loggerSchema>;
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md) &gt; [appenders](./kibana-plugin-core-server.loggercontextconfiginput.appenders.md)
## LoggerContextConfigInput.appenders property
<b>Signature:</b>
```typescript
appenders?: Record<string, AppenderConfigType> | Map<string, AppenderConfigType>;
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md) &gt; [loggers](./kibana-plugin-core-server.loggercontextconfiginput.loggers.md)
## LoggerContextConfigInput.loggers property
<b>Signature:</b>
```typescript
loggers?: LoggerConfigType[];
```

View file

@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md)
## LoggerContextConfigInput interface
<b>Signature:</b>
```typescript
export interface LoggerContextConfigInput
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [appenders](./kibana-plugin-core-server.loggercontextconfiginput.appenders.md) | <code>Record&lt;string, AppenderConfigType&gt; &#124; Map&lt;string, AppenderConfigType&gt;</code> | |
| [loggers](./kibana-plugin-core-server.loggercontextconfiginput.loggers.md) | <code>LoggerConfigType[]</code> | |

View file

@ -0,0 +1,42 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) &gt; [configure](./kibana-plugin-core-server.loggingservicesetup.configure.md)
## LoggingServiceSetup.configure() method
Customizes the logging config for the plugin's context.
<b>Signature:</b>
```typescript
configure(config$: Observable<LoggerContextConfigInput>): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| config$ | <code>Observable&lt;LoggerContextConfigInput&gt;</code> | |
<b>Returns:</b>
`void`
## Remarks
Assumes that that the `context` property of the individual `logger` items emitted by `config$` are relative to the plugin's logging context (defaults to `plugins.<plugin_id>`<!-- -->).
## Example
Customize the configuration for the plugins.data.search context.
```ts
core.logging.configure(
of({
appenders: new Map(),
loggers: [{ context: 'search', appenders: ['default'] }]
})
)
```

View file

@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md)
## LoggingServiceSetup interface
Provides APIs to plugins for customizing the plugin's logger.
<b>Signature:</b>
```typescript
export interface LoggingServiceSetup
```
## Methods
| Method | Description |
| --- | --- |
| [configure(config$)](./kibana-plugin-core-server.loggingservicesetup.configure.md) | Customizes the logging config for the plugin's context. |

View file

@ -108,7 +108,9 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) | |
| [LegacyServiceStartDeps](./kibana-plugin-core-server.legacyservicestartdeps.md) | |
| [Logger](./kibana-plugin-core-server.logger.md) | Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. |
| [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md) | |
| [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) | The single purpose of <code>LoggerFactory</code> interface is to define a way to retrieve a context-based logger instance. |
| [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) | Provides APIs to plugins for customizing the plugin's logger. |
| [LogMeta](./kibana-plugin-core-server.logmeta.md) | Contextual metadata |
| [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | APIs to retrieves metrics gathered and exposed by the core platform. |
| [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) | |
@ -209,6 +211,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| Type Alias | Description |
| --- | --- |
| [AppenderConfigType](./kibana-plugin-core-server.appenderconfigtype.md) | |
| [AuthenticationHandler](./kibana-plugin-core-server.authenticationhandler.md) | See [AuthToolkit](./kibana-plugin-core-server.authtoolkit.md)<!-- -->. |
| [AuthHeaders](./kibana-plugin-core-server.authheaders.md) | Auth Headers map |
| [AuthResult](./kibana-plugin-core-server.authresult.md) | |
@ -242,6 +245,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) | Creates an object containing request response payload, HTTP headers, error details, and other data transmitted to the client. |
| [KnownHeaders](./kibana-plugin-core-server.knownheaders.md) | Set of well-known HTTP headers. |
| [LifecycleResponseFactory](./kibana-plugin-core-server.lifecycleresponsefactory.md) | Creates an object containing redirection or error response with error details, HTTP headers, and other data transmitted to the client. |
| [LoggerConfigType](./kibana-plugin-core-server.loggerconfigtype.md) | |
| [MIGRATION\_ASSISTANCE\_INDEX\_ACTION](./kibana-plugin-core-server.migration_assistance_index_action.md) | |
| [MIGRATION\_DEPRECATION\_LEVEL](./kibana-plugin-core-server.migration_deprecation_level.md) | |
| [MutatingOperationRefreshSetting](./kibana-plugin-core-server.mutatingoperationrefreshsetting.md) | Elasticsearch Refresh setting for mutating operation |

View file

@ -20,7 +20,7 @@
import supertest from 'supertest';
import { HttpService, InternalHttpServiceSetup } from '../../http';
import { contextServiceMock } from '../../context/context_service.mock';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { Env } from '../../config';
import { getEnvOptions } from '../../config/__mocks__/env';
import { CapabilitiesService, CapabilitiesSetup } from '..';
@ -44,7 +44,7 @@ describe('CapabilitiesService', () => {
service = new CapabilitiesService({
coreId,
env,
logger: loggingServiceMock.create(),
logger: loggingSystemMock.create(),
configService: {} as any,
});
serviceSetup = await service.setup({ http: httpSetup });

View file

@ -28,12 +28,12 @@ import { rawConfigServiceMock } from './raw_config_service.mock';
import { schema } from '@kbn/config-schema';
import { ConfigService, Env } from '.';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { getEnvOptions } from './__mocks__/env';
const emptyArgv = getEnvOptions();
const defaultEnv = new Env('/kibana', emptyArgv);
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
const getRawConfigProvider = (rawConfig: Record<string, any>) =>
rawConfigServiceMock.create({ rawConfig });
@ -443,9 +443,9 @@ test('logs deprecation warning during validation', async () => {
return config;
});
loggingServiceMock.clear(logger);
loggingSystemMock.clear(logger);
await configService.validate();
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"some deprecation message",

View file

@ -17,9 +17,9 @@
* under the License.
*/
import { loggingServiceMock } from '../../logging/logging_service.mock';
export const mockLoggingService = loggingServiceMock.create();
mockLoggingService.asLoggerFactory.mockImplementation(() => mockLoggingService);
jest.doMock('../../logging/logging_service', () => ({
LoggingService: jest.fn(() => mockLoggingService),
import { loggingSystemMock } from '../../logging/logging_system.mock';
export const mockLoggingSystem = loggingSystemMock.create();
mockLoggingSystem.asLoggerFactory.mockImplementation(() => mockLoggingSystem);
jest.doMock('../../logging/logging_system', () => ({
LoggingSystem: jest.fn(() => mockLoggingSystem),
}));

View file

@ -17,8 +17,8 @@
* under the License.
*/
import { mockLoggingService } from './config_deprecation.test.mocks';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { mockLoggingSystem } from './config_deprecation.test.mocks';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import * as kbnTestServer from '../../../../test_utils/kbn_server';
describe('configuration deprecations', () => {
@ -35,7 +35,7 @@ describe('configuration deprecations', () => {
await root.setup();
const logs = loggingServiceMock.collect(mockLoggingService);
const logs = loggingSystemMock.collect(mockLoggingSystem);
const warnings = logs.warn.flatMap((i) => i);
expect(warnings).not.toContain(
'"optimize.lazy" is deprecated and has been replaced by "optimize.watch"'
@ -55,7 +55,7 @@ describe('configuration deprecations', () => {
await root.setup();
const logs = loggingServiceMock.collect(mockLoggingService);
const logs = loggingSystemMock.collect(mockLoggingSystem);
const warnings = logs.warn.flatMap((i) => i);
expect(warnings).toContain(
'"optimize.lazy" is deprecated and has been replaced by "optimize.watch"'

View file

@ -20,17 +20,17 @@
import { CoreContext } from './core_context';
import { getEnvOptions } from './config/__mocks__/env';
import { Env, IConfigService } from './config';
import { loggingServiceMock } from './logging/logging_service.mock';
import { loggingSystemMock } from './logging/logging_system.mock';
import { configServiceMock } from './config/config_service.mock';
import { ILoggingService } from './logging';
import { ILoggingSystem } from './logging';
function create({
env = Env.createDefault(getEnvOptions()),
logger = loggingServiceMock.create(),
logger = loggingSystemMock.create(),
configService = configServiceMock.create(),
}: {
env?: Env;
logger?: jest.Mocked<ILoggingService>;
logger?: jest.Mocked<ILoggingSystem>;
configService?: jest.Mocked<IConfigService>;
} = {}): DeeplyMockedKeys<CoreContext> {
return { coreId: Symbol(), env, logger, configService };

View file

@ -28,11 +28,11 @@ import {
import { errors } from 'elasticsearch';
import { get } from 'lodash';
import { Logger } from '../logging';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { httpServerMock } from '../http/http_server.mocks';
import { ClusterClient } from './cluster_client';
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
afterEach(() => jest.clearAllMocks());
test('#constructor creates client with parsed config', () => {

View file

@ -18,12 +18,12 @@
*/
import { duration } from 'moment';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import {
ElasticsearchClientConfig,
parseElasticsearchClientConfig,
} from './elasticsearch_client_config';
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
afterEach(() => jest.clearAllMocks());
test('parses minimally specified config', () => {
@ -360,7 +360,7 @@ describe('#log', () => {
expect(typeof esLogger.close).toBe('function');
expect(loggingServiceMock.collect(logger)).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger)).toMatchInlineSnapshot(`
Object {
"debug": Array [],
"error": Array [
@ -406,7 +406,7 @@ Object {
expect(typeof esLogger.close).toBe('function');
expect(loggingServiceMock.collect(logger)).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger)).toMatchInlineSnapshot(`
Object {
"debug": Array [
Array [

View file

@ -26,7 +26,7 @@ import { Env } from '../config';
import { getEnvOptions } from '../config/__mocks__/env';
import { CoreContext } from '../core_context';
import { configServiceMock } from '../config/config_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { httpServiceMock } from '../http/http_service.mock';
import { ElasticsearchConfig } from './elasticsearch_config';
import { ElasticsearchService } from './elasticsearch_service';
@ -55,7 +55,7 @@ configService.atPath.mockReturnValue(
let env: Env;
let coreContext: CoreContext;
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
beforeEach(() => {
env = Env.createDefault(getEnvOptions());

View file

@ -19,7 +19,7 @@
import * as legacyElasticsearch from 'elasticsearch';
import { retryCallCluster, migrationsRetryCallCluster } from './retry_call_cluster';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
describe('retryCallCluster', () => {
it('retries ES API calls that rejects with NoConnections', () => {
@ -69,10 +69,10 @@ describe('migrationsRetryCallCluster', () => {
'Gone',
];
const mockLogger = loggingServiceMock.create();
const mockLogger = loggingSystemMock.create();
beforeEach(() => {
loggingServiceMock.clear(mockLogger);
loggingSystemMock.clear(mockLogger);
});
errors.forEach((errorName) => {
@ -133,7 +133,7 @@ describe('migrationsRetryCallCluster', () => {
callEsApi.mockResolvedValueOnce('done');
const retried = migrationsRetryCallCluster(callEsApi, mockLogger.get('mock log'), 1);
await retried('endpoint');
expect(loggingServiceMock.collect(mockLogger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(mockLogger).warn).toMatchInlineSnapshot(`
Array [
Array [
"Unable to connect to Elasticsearch. Error: No Living connections",

View file

@ -17,12 +17,12 @@
* under the License.
*/
import { mapNodesVersionCompatibility, pollEsNodesVersion, NodesInfo } from './ensure_es_version';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { take, delay } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';
import { of } from 'rxjs';
const mockLoggerFactory = loggingServiceMock.create();
const mockLoggerFactory = loggingSystemMock.create();
const mockLogger = mockLoggerFactory.get('mock logger');
const KIBANA_VERSION = '5.1.0';

View file

@ -29,14 +29,14 @@ import { Env } from '../config';
import { getEnvOptions } from '../config/__mocks__/env';
import { configServiceMock } from '../config/config_service.mock';
import { contextServiceMock } from '../context/context_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { httpServerMock } from './http_server.mocks';
import { createCookieSessionStorageFactory } from './cookie_session_storage';
let server: HttpService;
let logger: ReturnType<typeof loggingServiceMock.create>;
let logger: ReturnType<typeof loggingSystemMock.create>;
let env: Env;
let coreContext: CoreContext;
const configService = configServiceMock.create();
@ -67,7 +67,7 @@ configService.atPath.mockReturnValue(
);
beforeEach(() => {
logger = loggingServiceMock.create();
logger = loggingSystemMock.create();
env = Env.createDefault(getEnvOptions());
coreContext = { coreId: Symbol(), env, logger, configService: configService as any };
@ -324,7 +324,7 @@ describe('Cookie based SessionStorage', () => {
expect(mockServer.auth.test).toBeCalledTimes(1);
expect(mockServer.auth.test).toHaveBeenCalledWith('security-cookie', mockRequest);
expect(loggingServiceMock.collect(logger).warn).toEqual([
expect(loggingSystemMock.collect(logger).warn).toEqual([
['Found 2 auth sessions when we were only expecting 1.'],
]);
});
@ -381,7 +381,7 @@ describe('Cookie based SessionStorage', () => {
const session = await factory.asScoped(KibanaRequest.from(mockRequest)).get();
expect(session).toBe(null);
expect(loggingServiceMock.collect(logger).debug).toEqual([['Error: Invalid cookie.']]);
expect(loggingSystemMock.collect(logger).debug).toEqual([['Error: Invalid cookie.']]);
});
});

View file

@ -31,7 +31,7 @@ import {
RouteValidationResultFactory,
RouteValidationFunction,
} from './router';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { HttpServer } from './http_server';
import { Readable } from 'stream';
import { RequestHandlerContext } from 'kibana/server';
@ -48,7 +48,7 @@ let server: HttpServer;
let config: HttpConfig;
let configWithSSL: HttpConfig;
const loggingService = loggingServiceMock.create();
const loggingService = loggingSystemMock.create();
const logger = loggingService.get();
const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {});
@ -97,7 +97,7 @@ test('log listening address after started', async () => {
await server.start();
expect(server.isListening()).toBe(true);
expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(loggingService).info).toMatchInlineSnapshot(`
Array [
Array [
"http server running at http://127.0.0.1:10002",
@ -113,7 +113,7 @@ test('log listening address after started when configured with BasePath and rewr
await server.start();
expect(server.isListening()).toBe(true);
expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(loggingService).info).toMatchInlineSnapshot(`
Array [
Array [
"http server running at http://127.0.0.1:10002",
@ -129,7 +129,7 @@ test('log listening address after started when configured with BasePath and rewr
await server.start();
expect(server.isListening()).toBe(true);
expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(loggingService).info).toMatchInlineSnapshot(`
Array [
Array [
"http server running at http://127.0.0.1:10002/bar",

View file

@ -25,12 +25,12 @@ import { HttpService } from '.';
import { HttpConfigType, config } from './http_config';
import { httpServerMock } from './http_server.mocks';
import { ConfigService, Env } from '../config';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { contextServiceMock } from '../context/context_service.mock';
import { getEnvOptions } from '../config/__mocks__/env';
import { config as cspConfig } from '../csp';
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
const env = Env.createDefault(getEnvOptions());
const coreId = Symbol();
@ -159,7 +159,7 @@ test('logs error if already set up', async () => {
await service.setup(setupDeps);
expect(loggingServiceMock.collect(logger).warn).toMatchSnapshot();
expect(loggingSystemMock.collect(logger).warn).toMatchSnapshot();
});
test('stops http server', async () => {

View file

@ -34,7 +34,7 @@ import { defaultValidationErrorHandler, HapiValidationError, getServerOptions }
import { HttpServer } from './http_server';
import { HttpConfig, config } from './http_config';
import { Router } from './router';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { ByteSizeValue } from '@kbn/config-schema';
const emptyOutput = {
@ -77,7 +77,7 @@ describe('defaultValidationErrorHandler', () => {
});
describe('timeouts', () => {
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
const server = new HttpServer(logger, 'foo');
const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {});

View file

@ -27,7 +27,7 @@ import supertest from 'supertest';
import { ByteSizeValue } from '@kbn/config-schema';
import { HttpConfig } from '.';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { HttpsRedirectServer } from './https_redirect_server';
const chance = new Chance();
@ -50,7 +50,7 @@ beforeEach(() => {
},
} as HttpConfig;
server = new HttpsRedirectServer(loggingServiceMock.create().get());
server = new HttpsRedirectServer(loggingSystemMock.create().get());
});
afterEach(async () => {

View file

@ -24,12 +24,12 @@ import { ensureRawRequest } from '../router';
import { HttpService } from '../http_service';
import { contextServiceMock } from '../../context/context_service.mock';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { createHttpServer } from '../test_utils';
let server: HttpService;
let logger: ReturnType<typeof loggingServiceMock.create>;
let logger: ReturnType<typeof loggingSystemMock.create>;
const contextSetup = contextServiceMock.createSetupContract();
@ -38,7 +38,7 @@ const setupDeps = {
};
beforeEach(() => {
logger = loggingServiceMock.create();
logger = loggingSystemMock.create();
server = createHttpServer({ logger });
});
@ -167,7 +167,7 @@ describe('OnPreAuth', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: reason],
@ -188,7 +188,7 @@ describe('OnPreAuth', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Unexpected result from OnPreAuth. Expected OnPreAuthResult or KibanaResponse, but given: [object Object].],
@ -301,7 +301,7 @@ describe('OnPostAuth', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: reason],
@ -321,7 +321,7 @@ describe('OnPostAuth', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Unexpected result from OnPostAuth. Expected OnPostAuthResult or KibanaResponse, but given: [object Object].],
@ -506,7 +506,7 @@ describe('Auth', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: reason],
@ -703,7 +703,7 @@ describe('Auth', () => {
const response = await supertest(innerServer.listener).get('/').expect(200);
expect(response.header['www-authenticate']).toBe('from auth interceptor');
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"onPreResponseHandler rewrote a response header [www-authenticate].",
@ -736,7 +736,7 @@ describe('Auth', () => {
const response = await supertest(innerServer.listener).get('/').expect(400);
expect(response.header['www-authenticate']).toBe('from auth interceptor');
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"onPreResponseHandler rewrote a response header [www-authenticate].",
@ -798,7 +798,7 @@ describe('Auth', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: reason],
@ -818,7 +818,7 @@ describe('Auth', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Unexpected result from OnPostAuth. Expected OnPostAuthResult or KibanaResponse, but given: [object Object].],
@ -929,7 +929,7 @@ describe('OnPreResponse', () => {
await supertest(innerServer.listener).get('/').expect(200);
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"onPreResponseHandler rewrote a response header [x-kibana-header].",
@ -953,7 +953,7 @@ describe('OnPreResponse', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: reason],
@ -975,7 +975,7 @@ describe('OnPreResponse', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Unexpected result from OnPreResponse. Expected OnPreResponseResult, but given: [object Object].],

View file

@ -21,12 +21,12 @@ import supertest from 'supertest';
import { HttpService } from '../http_service';
import { contextServiceMock } from '../../context/context_service.mock';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { createHttpServer } from '../test_utils';
let server: HttpService;
let logger: ReturnType<typeof loggingServiceMock.create>;
let logger: ReturnType<typeof loggingSystemMock.create>;
const contextSetup = contextServiceMock.createSetupContract();
const setupDeps = {
@ -34,7 +34,7 @@ const setupDeps = {
};
beforeEach(() => {
logger = loggingServiceMock.create();
logger = loggingSystemMock.create();
server = createHttpServer({ logger });
});

View file

@ -24,12 +24,12 @@ import { schema } from '@kbn/config-schema';
import { HttpService } from '../http_service';
import { contextServiceMock } from '../../context/context_service.mock';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { createHttpServer } from '../test_utils';
let server: HttpService;
let logger: ReturnType<typeof loggingServiceMock.create>;
let logger: ReturnType<typeof loggingSystemMock.create>;
const contextSetup = contextServiceMock.createSetupContract();
const setupDeps = {
@ -37,7 +37,7 @@ const setupDeps = {
};
beforeEach(() => {
logger = loggingServiceMock.create();
logger = loggingSystemMock.create();
server = createHttpServer({ logger });
});
@ -347,7 +347,7 @@ describe('Handler', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: unexpected error],
@ -368,7 +368,7 @@ describe('Handler', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Unauthorized],
@ -387,7 +387,7 @@ describe('Handler', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Unexpected result from Route Handler. Expected KibanaResponse, but given: string.],
@ -763,7 +763,7 @@ describe('Response factory', () => {
await supertest(innerServer.listener).get('/').expect(500);
// error happens within hapi when route handler already finished execution.
expect(loggingServiceMock.collect(logger).error).toHaveLength(0);
expect(loggingSystemMock.collect(logger).error).toHaveLength(0);
});
it('200 OK with body', async () => {
@ -855,7 +855,7 @@ describe('Response factory', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: expected 'location' header to be set],
@ -1261,7 +1261,7 @@ describe('Response factory', () => {
message: 'An internal server error occurred.',
statusCode: 500,
});
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Unexpected Http status code. Expected from 400 to 599, but given: 200],
@ -1330,7 +1330,7 @@ describe('Response factory', () => {
await supertest(innerServer.listener).get('/').expect(500);
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: expected 'location' header to be set],
@ -1445,7 +1445,7 @@ describe('Response factory', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('reason');
expect(loggingServiceMock.collect(logger).error).toHaveLength(0);
expect(loggingSystemMock.collect(logger).error).toHaveLength(0);
});
it('throws an error if not valid error is provided', async () => {
@ -1464,7 +1464,7 @@ describe('Response factory', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: expected error message to be provided],
@ -1488,7 +1488,7 @@ describe('Response factory', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: expected error message to be provided],
@ -1511,7 +1511,7 @@ describe('Response factory', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: options.statusCode is expected to be set. given options: undefined],
@ -1534,7 +1534,7 @@ describe('Response factory', () => {
const result = await supertest(innerServer.listener).get('/').expect(500);
expect(result.body.message).toBe('An internal server error occurred.');
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Unexpected Http status code. Expected from 100 to 599, but given: 20.],

View file

@ -18,10 +18,10 @@
*/
import { Router } from './router';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { schema } from '@kbn/config-schema';
const logger = loggingServiceMock.create().get();
const logger = loggingSystemMock.create().get();
const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {});
describe('Router', () => {

View file

@ -24,12 +24,12 @@ import { getEnvOptions } from '../config/__mocks__/env';
import { HttpService } from './http_service';
import { CoreContext } from '../core_context';
import { configServiceMock } from '../config/config_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
const coreId = Symbol('core');
const env = Env.createDefault(getEnvOptions());
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
const configService = configServiceMock.create();
configService.atPath.mockReturnValue(

View file

@ -62,6 +62,12 @@ import { CapabilitiesSetup, CapabilitiesStart } from './capabilities';
import { UuidServiceSetup } from './uuid';
import { MetricsServiceSetup } from './metrics';
import { StatusServiceSetup } from './status';
import {
LoggingServiceSetup,
appendersSchema,
loggerContextConfigSchema,
loggerSchema,
} from './logging';
export { bootstrap } from './bootstrap';
export { Capabilities, CapabilitiesProvider, CapabilitiesSwitcher } from './capabilities';
@ -187,7 +193,17 @@ export {
} from './http_resources';
export { IRenderOptions } from './rendering';
export { Logger, LoggerFactory, LogMeta, LogRecord, LogLevel } from './logging';
export {
Logger,
LoggerFactory,
LogMeta,
LogRecord,
LogLevel,
LoggingServiceSetup,
LoggerContextConfigInput,
LoggerConfigType,
AppenderConfigType,
} from './logging';
export {
DiscoveredPlugin,
@ -385,6 +401,8 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
/** {@link HttpResources} */
resources: HttpResources;
};
/** {@link LoggingServiceSetup} */
logging: LoggingServiceSetup;
/** {@link MetricsServiceSetup} */
metrics: MetricsServiceSetup;
/** {@link SavedObjectsServiceSetup} */
@ -450,4 +468,9 @@ export const config = {
elasticsearch: {
schema: elasticsearchConfigSchema,
},
logging: {
appenders: appendersSchema,
loggers: loggerSchema,
loggerContext: loggerContextConfigSchema,
},
};

View file

@ -34,6 +34,7 @@ import { InternalMetricsServiceSetup } from './metrics';
import { InternalRenderingServiceSetup } from './rendering';
import { InternalHttpResourcesSetup } from './http_resources';
import { InternalStatusServiceSetup } from './status';
import { InternalLoggingServiceSetup } from './logging';
/** @internal */
export interface InternalCoreSetup {
@ -48,6 +49,7 @@ export interface InternalCoreSetup {
uuid: UuidServiceSetup;
rendering: InternalRenderingServiceSetup;
httpResources: InternalHttpResourcesSetup;
logging: InternalLoggingServiceSetup;
}
/**

View file

@ -38,7 +38,7 @@ import { BasePathProxyServer } from '../http';
import { DiscoveredPlugin } from '../plugins';
import { configServiceMock } from '../config/config_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { contextServiceMock } from '../context/context_service.mock';
import { httpServiceMock } from '../http/http_service.mock';
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
@ -53,6 +53,7 @@ import { LegacyVars, LegacyServiceSetupDeps, LegacyServiceStartDeps } from './ty
import { LegacyService } from './legacy_service';
import { coreMock } from '../mocks';
import { statusServiceMock } from '../status/status_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
const MockKbnServer: jest.Mock<KbnServer> = KbnServer as any;
@ -64,7 +65,7 @@ let setupDeps: LegacyServiceSetupDeps;
let startDeps: LegacyServiceStartDeps;
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
let configService: ReturnType<typeof configServiceMock.create>;
let uuidSetup: ReturnType<typeof uuidServiceMock.createSetupContract>;
@ -100,6 +101,7 @@ beforeEach(() => {
metrics: metricsServiceMock.createInternalSetupContract(),
uuid: uuidSetup,
status: statusServiceMock.createInternalSetupContract(),
logging: loggingServiceMock.createInternalSetupContract(),
},
plugins: { 'plugin-id': 'plugin-value' },
uiPlugins: {
@ -281,7 +283,7 @@ describe('once LegacyService is set up with connection info', () => {
const [mockKbnServer] = MockKbnServer.mock.instances as Array<jest.Mocked<KbnServer>>;
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
expect(loggingServiceMock.collect(logger).error).toEqual([]);
expect(loggingSystemMock.collect(logger).error).toEqual([]);
const configError = new Error('something went wrong');
mockKbnServer.applyLoggingConfiguration.mockImplementation(() => {
@ -290,7 +292,7 @@ describe('once LegacyService is set up with connection info', () => {
config$.next(new ObjectToConfigAdapter({ logging: { verbose: true } }));
expect(loggingServiceMock.collect(logger).error).toEqual([[configError]]);
expect(loggingSystemMock.collect(logger).error).toEqual([[configError]]);
});
test('logs error if config service fails.', async () => {
@ -306,13 +308,13 @@ describe('once LegacyService is set up with connection info', () => {
const [mockKbnServer] = MockKbnServer.mock.instances;
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
expect(loggingServiceMock.collect(logger).error).toEqual([]);
expect(loggingSystemMock.collect(logger).error).toEqual([]);
const configError = new Error('something went wrong');
config$.error(configError);
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
expect(loggingServiceMock.collect(logger).error).toEqual([[configError]]);
expect(loggingSystemMock.collect(logger).error).toEqual([[configError]]);
});
});

View file

@ -309,6 +309,9 @@ export class LegacyService implements CoreService {
csp: setupDeps.core.http.csp,
getServerInfo: setupDeps.core.http.getServerInfo,
},
logging: {
configure: (config$) => setupDeps.core.logging.configure([], config$),
},
metrics: {
getOpsMetrics$: setupDeps.core.metrics.getOpsMetrics$,
},

View file

@ -26,13 +26,19 @@ import { LogRecord } from '../log_record';
import { ConsoleAppender } from './console/console_appender';
import { FileAppender } from './file/file_appender';
const appendersSchema = schema.oneOf([
/**
* Config schema for validting the shape of the `appenders` key in in {@link LoggerContextConfigType} or
* {@link LoggingConfigType}.
*
* @public
*/
export const appendersSchema = schema.oneOf([
ConsoleAppender.configSchema,
FileAppender.configSchema,
LegacyAppender.configSchema,
]);
/** @internal */
/** @public */
export type AppenderConfigType = TypeOf<typeof appendersSchema>;
/**

View file

@ -21,7 +21,18 @@ export { Logger, LogMeta } from './logger';
export { LoggerFactory } from './logger_factory';
export { LogRecord } from './log_record';
export { LogLevel } from './log_level';
/** @internal */
export { config, LoggingConfigType } from './logging_config';
/** @internal */
export { LoggingService, ILoggingService } from './logging_service';
export {
config,
LoggingConfigType,
LoggerContextConfigInput,
LoggerConfigType,
loggerContextConfigSchema,
loggerSchema,
} from './logging_config';
export { LoggingSystem, ILoggingSystem } from './logging_system';
export {
InternalLoggingServiceSetup,
LoggingServiceSetup,
LoggingService,
} from './logging_service';
export { appendersSchema, AppenderConfigType } from './appenders/appenders';

View file

@ -171,3 +171,127 @@ test('fails if loggers use unknown appenders.', () => {
expect(() => new LoggingConfig(validateConfig)).toThrowErrorMatchingSnapshot();
});
describe('extend', () => {
it('adds new appenders', () => {
const configValue = new LoggingConfig(
config.schema.validate({
appenders: {
file1: {
kind: 'file',
layout: { kind: 'pattern' },
path: 'path',
},
},
})
);
const mergedConfigValue = configValue.extend(
config.schema.validate({
appenders: {
file2: {
kind: 'file',
layout: { kind: 'pattern' },
path: 'path',
},
},
})
);
expect([...mergedConfigValue.appenders.keys()]).toEqual([
'default',
'console',
'file1',
'file2',
]);
});
it('overrides appenders', () => {
const configValue = new LoggingConfig(
config.schema.validate({
appenders: {
file1: {
kind: 'file',
layout: { kind: 'pattern' },
path: 'path',
},
},
})
);
const mergedConfigValue = configValue.extend(
config.schema.validate({
appenders: {
file1: {
kind: 'file',
layout: { kind: 'json' },
path: 'updatedPath',
},
},
})
);
expect(mergedConfigValue.appenders.get('file1')).toEqual({
kind: 'file',
layout: { kind: 'json' },
path: 'updatedPath',
});
});
it('adds new loggers', () => {
const configValue = new LoggingConfig(
config.schema.validate({
loggers: [
{
context: 'plugins',
level: 'warn',
},
],
})
);
const mergedConfigValue = configValue.extend(
config.schema.validate({
loggers: [
{
context: 'plugins.pid',
level: 'trace',
},
],
})
);
expect([...mergedConfigValue.loggers.keys()]).toEqual(['root', 'plugins', 'plugins.pid']);
});
it('overrides loggers', () => {
const configValue = new LoggingConfig(
config.schema.validate({
loggers: [
{
context: 'plugins',
level: 'warn',
},
],
})
);
const mergedConfigValue = configValue.extend(
config.schema.validate({
loggers: [
{
appenders: ['console'],
context: 'plugins',
level: 'trace',
},
],
})
);
expect(mergedConfigValue.loggers.get('plugins')).toEqual({
appenders: ['console'],
context: 'plugins',
level: 'trace',
});
});
});

View file

@ -39,7 +39,7 @@ const ROOT_CONTEXT_NAME = 'root';
*/
const DEFAULT_APPENDER_NAME = 'default';
const createLevelSchema = schema.oneOf(
const levelSchema = schema.oneOf(
[
schema.literal('all'),
schema.literal('fatal'),
@ -55,21 +55,26 @@ const createLevelSchema = schema.oneOf(
}
);
const createLoggerSchema = schema.object({
/**
* Config schema for validating the `loggers` key in {@link LoggerContextConfigType} or {@link LoggingConfigType}.
*
* @public
*/
export const loggerSchema = schema.object({
appenders: schema.arrayOf(schema.string(), { defaultValue: [] }),
context: schema.string(),
level: createLevelSchema,
level: levelSchema,
});
/** @internal */
export type LoggerConfigType = TypeOf<typeof createLoggerSchema>;
/** @public */
export type LoggerConfigType = TypeOf<typeof loggerSchema>;
export const config = {
path: 'logging',
schema: schema.object({
appenders: schema.mapOf(schema.string(), Appenders.configSchema, {
defaultValue: new Map<string, AppenderConfigType>(),
}),
loggers: schema.arrayOf(createLoggerSchema, {
loggers: schema.arrayOf(loggerSchema, {
defaultValue: [],
}),
root: schema.object(
@ -78,7 +83,7 @@ export const config = {
defaultValue: [DEFAULT_APPENDER_NAME],
minSize: 1,
}),
level: createLevelSchema,
level: levelSchema,
},
{
validate(rawConfig) {
@ -93,6 +98,29 @@ export const config = {
export type LoggingConfigType = TypeOf<typeof config.schema>;
/**
* Config schema for validating the inputs to the {@link LoggingServiceStart.configure} API.
* See {@link LoggerContextConfigType}.
*
* @public
*/
export const loggerContextConfigSchema = schema.object({
appenders: schema.mapOf(schema.string(), Appenders.configSchema, {
defaultValue: new Map<string, AppenderConfigType>(),
}),
loggers: schema.arrayOf(loggerSchema, { defaultValue: [] }),
});
/** @public */
export type LoggerContextConfigType = TypeOf<typeof loggerContextConfigSchema>;
/** @public */
export interface LoggerContextConfigInput {
// config-schema knows how to handle either Maps or Records
appenders?: Record<string, AppenderConfigType> | Map<string, AppenderConfigType>;
loggers?: LoggerConfigType[];
}
/**
* Describes the config used to fully setup logging subsystem.
* @internal
@ -147,11 +175,35 @@ export class LoggingConfig {
*/
public readonly loggers: Map<string, LoggerConfigType> = new Map();
constructor(configType: LoggingConfigType) {
constructor(private readonly configType: LoggingConfigType) {
this.fillAppendersConfig(configType);
this.fillLoggersConfig(configType);
}
/**
* Returns a new LoggingConfig that merges the existing config with the specified config.
*
* @remarks
* Does not support merging the `root` config property.
*
* @param contextConfig
*/
public extend(contextConfig: LoggerContextConfigType) {
// Use a Map to de-dupe any loggers for the same context. contextConfig overrides existing config.
const mergedLoggers = new Map<string, LoggerConfigType>([
...this.configType.loggers.map((l) => [l.context, l] as [string, LoggerConfigType]),
...contextConfig.loggers.map((l) => [l.context, l] as [string, LoggerConfigType]),
]);
const mergedConfig: LoggingConfigType = {
appenders: new Map([...this.configType.appenders, ...contextConfig.appenders]),
loggers: [...mergedLoggers.values()],
root: this.configType.root,
};
return new LoggingConfig(mergedConfig);
}
private fillAppendersConfig(loggingConfig: LoggingConfigType) {
for (const [appenderKey, appenderSchema] of loggingConfig.appenders) {
this.appenders.set(appenderKey, appenderSchema);

View file

@ -17,67 +17,35 @@
* under the License.
*/
// Test helpers to simplify mocking logs and collecting all their outputs
import { ILoggingService } from './logging_service';
import { LoggerFactory } from './logger_factory';
import { loggerMock, MockedLogger } from './logger.mock';
import {
LoggingService,
LoggingServiceSetup,
InternalLoggingServiceSetup,
} from './logging_service';
const createLoggingServiceMock = () => {
const mockLog = loggerMock.create();
const createInternalSetupMock = (): jest.Mocked<InternalLoggingServiceSetup> => ({
configure: jest.fn(),
});
mockLog.get.mockImplementation((...context) => ({
...mockLog,
context,
}));
const createSetupMock = (): jest.Mocked<LoggingServiceSetup> => ({
configure: jest.fn(),
});
const mocked: jest.Mocked<ILoggingService> = {
get: jest.fn(),
asLoggerFactory: jest.fn(),
upgrade: jest.fn(),
type LoggingServiceContract = PublicMethodsOf<LoggingService>;
const createMock = (): jest.Mocked<LoggingServiceContract> => {
const service: jest.Mocked<LoggingServiceContract> = {
setup: jest.fn(),
start: jest.fn(),
stop: jest.fn(),
};
mocked.get.mockImplementation((...context) => ({
...mockLog,
context,
}));
mocked.asLoggerFactory.mockImplementation(() => mocked);
mocked.stop.mockResolvedValue();
return mocked;
};
const collectLoggingServiceMock = (loggerFactory: LoggerFactory) => {
const mockLog = loggerFactory.get() as MockedLogger;
return {
debug: mockLog.debug.mock.calls,
error: mockLog.error.mock.calls,
fatal: mockLog.fatal.mock.calls,
info: mockLog.info.mock.calls,
log: mockLog.log.mock.calls,
trace: mockLog.trace.mock.calls,
warn: mockLog.warn.mock.calls,
};
};
service.setup.mockReturnValue(createInternalSetupMock());
const clearLoggingServiceMock = (loggerFactory: LoggerFactory) => {
const mockedLoggerFactory = (loggerFactory as unknown) as jest.Mocked<ILoggingService>;
mockedLoggerFactory.get.mockClear();
mockedLoggerFactory.asLoggerFactory.mockClear();
mockedLoggerFactory.upgrade.mockClear();
mockedLoggerFactory.stop.mockClear();
const mockLog = loggerFactory.get() as MockedLogger;
mockLog.debug.mockClear();
mockLog.info.mockClear();
mockLog.warn.mockClear();
mockLog.error.mockClear();
mockLog.trace.mockClear();
mockLog.fatal.mockClear();
mockLog.log.mockClear();
return service;
};
export const loggingServiceMock = {
create: createLoggingServiceMock,
collect: collectLoggingServiceMock,
clear: clearLoggingServiceMock,
createLogger: loggerMock.create,
create: createMock,
createSetupContract: createSetupMock,
createInternalSetupContract: createInternalSetupMock,
};

View file

@ -16,167 +16,85 @@
* specific language governing permissions and limitations
* under the License.
*/
import { of, Subject } from 'rxjs';
const mockStreamWrite = jest.fn();
jest.mock('fs', () => ({
constants: {},
createWriteStream: jest.fn(() => ({ write: mockStreamWrite })),
}));
import { LoggingService, InternalLoggingServiceSetup } from './logging_service';
import { loggingSystemMock } from './logging_system.mock';
import { LoggerContextConfigType } from './logging_config';
const dynamicProps = { pid: expect.any(Number) };
describe('LoggingService', () => {
let loggingSystem: ReturnType<typeof loggingSystemMock.create>;
let service: LoggingService;
let setup: InternalLoggingServiceSetup;
jest.mock('../../../legacy/server/logging/rotate', () => ({
setupLoggingRotate: jest.fn().mockImplementation(() => Promise.resolve({})),
}));
beforeEach(() => {
loggingSystem = loggingSystemMock.create();
service = new LoggingService({ logger: loggingSystem.asLoggerFactory() } as any);
setup = service.setup({ loggingSystem });
});
afterEach(() => {
service.stop();
});
const timestamp = new Date(Date.UTC(2012, 1, 1, 14, 33, 22, 11));
let mockConsoleLog: jest.SpyInstance;
describe('setup', () => {
it('forwards configuration changes to logging system', () => {
const config1: LoggerContextConfigType = {
appenders: new Map(),
loggers: [{ context: 'subcontext', appenders: ['console'], level: 'warn' }],
};
const config2: LoggerContextConfigType = {
appenders: new Map(),
loggers: [{ context: 'subcontext', appenders: ['default'], level: 'all' }],
};
import { createWriteStream } from 'fs';
const mockCreateWriteStream = (createWriteStream as unknown) as jest.Mock<typeof createWriteStream>;
setup.configure(['test', 'context'], of(config1, config2));
expect(loggingSystem.setContextConfig).toHaveBeenNthCalledWith(
1,
['test', 'context'],
config1
);
expect(loggingSystem.setContextConfig).toHaveBeenNthCalledWith(
2,
['test', 'context'],
config2
);
});
import { LoggingService, config } from '.';
it('stops forwarding first observable when called a second time', () => {
const updates$ = new Subject<LoggerContextConfigType>();
const config1: LoggerContextConfigType = {
appenders: new Map(),
loggers: [{ context: 'subcontext', appenders: ['console'], level: 'warn' }],
};
const config2: LoggerContextConfigType = {
appenders: new Map(),
loggers: [{ context: 'subcontext', appenders: ['default'], level: 'all' }],
};
let service: LoggingService;
beforeEach(() => {
mockConsoleLog = jest.spyOn(global.console, 'log').mockReturnValue(undefined);
jest.spyOn<any, any>(global, 'Date').mockImplementation(() => timestamp);
service = new LoggingService();
});
afterEach(() => {
jest.restoreAllMocks();
mockCreateWriteStream.mockClear();
mockStreamWrite.mockClear();
});
test('uses default memory buffer logger until config is provided', () => {
const bufferAppendSpy = jest.spyOn((service as any).bufferAppender, 'append');
const logger = service.get('test', 'context');
logger.trace('trace message');
// We shouldn't create new buffer appender for another context.
const anotherLogger = service.get('test', 'context2');
anotherLogger.fatal('fatal message', { some: 'value' });
expect(bufferAppendSpy).toHaveBeenCalledTimes(2);
expect(bufferAppendSpy.mock.calls[0][0]).toMatchSnapshot(dynamicProps);
expect(bufferAppendSpy.mock.calls[1][0]).toMatchSnapshot(dynamicProps);
});
test('flushes memory buffer logger and switches to real logger once config is provided', () => {
const logger = service.get('test', 'context');
logger.trace('buffered trace message');
logger.info('buffered info message', { some: 'value' });
logger.fatal('buffered fatal message');
const bufferAppendSpy = jest.spyOn((service as any).bufferAppender, 'append');
// Switch to console appender with `info` level, so that `trace` message won't go through.
service.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'info' },
})
);
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchSnapshot(
dynamicProps,
'buffered messages'
);
mockConsoleLog.mockClear();
// Now message should go straight to thew newly configured appender, not buffered one.
logger.info('some new info message');
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchSnapshot(dynamicProps, 'new messages');
expect(bufferAppendSpy).not.toHaveBeenCalled();
});
test('appends records via multiple appenders.', () => {
const loggerWithoutConfig = service.get('some-context');
const testsLogger = service.get('tests');
const testsChildLogger = service.get('tests', 'child');
loggerWithoutConfig.info('You know, just for your info.');
testsLogger.warn('Config is not ready!');
testsChildLogger.error('Too bad that config is not ready :/');
testsChildLogger.info('Just some info that should not be logged.');
expect(mockConsoleLog).not.toHaveBeenCalled();
expect(mockCreateWriteStream).not.toHaveBeenCalled();
service.upgrade(
config.schema.validate({
appenders: {
default: { kind: 'console', layout: { kind: 'pattern' } },
file: { kind: 'file', layout: { kind: 'pattern' }, path: 'path' },
},
loggers: [
{ appenders: ['file'], context: 'tests', level: 'warn' },
{ context: 'tests.child', level: 'error' },
],
})
);
// Now all logs should added to configured appenders.
expect(mockConsoleLog).toHaveBeenCalledTimes(1);
expect(mockConsoleLog.mock.calls[0][0]).toMatchSnapshot('console logs');
expect(mockStreamWrite).toHaveBeenCalledTimes(2);
expect(mockStreamWrite.mock.calls[0][0]).toMatchSnapshot('file logs');
expect(mockStreamWrite.mock.calls[1][0]).toMatchSnapshot('file logs');
});
test('uses `root` logger if context is not specified.', () => {
service.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'pattern' } } },
})
);
const rootLogger = service.get();
rootLogger.info('This message goes to a root context.');
expect(mockConsoleLog.mock.calls).toMatchSnapshot();
});
test('`stop()` disposes all appenders.', async () => {
service.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'info' },
})
);
const bufferDisposeSpy = jest.spyOn((service as any).bufferAppender, 'dispose');
const consoleDisposeSpy = jest.spyOn((service as any).appenders.get('default'), 'dispose');
await service.stop();
expect(bufferDisposeSpy).toHaveBeenCalledTimes(1);
expect(consoleDisposeSpy).toHaveBeenCalledTimes(1);
});
test('asLoggerFactory() only allows to create new loggers.', () => {
const logger = service.asLoggerFactory().get('test', 'context');
service.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'all' },
})
);
logger.trace('buffered trace message');
logger.info('buffered info message', { some: 'value' });
logger.fatal('buffered fatal message');
expect(Object.keys(service.asLoggerFactory())).toEqual(['get']);
expect(mockConsoleLog).toHaveBeenCalledTimes(3);
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchSnapshot(dynamicProps);
expect(JSON.parse(mockConsoleLog.mock.calls[1][0])).toMatchSnapshot(dynamicProps);
expect(JSON.parse(mockConsoleLog.mock.calls[2][0])).toMatchSnapshot(dynamicProps);
setup.configure(['test', 'context'], updates$);
setup.configure(['test', 'context'], of(config1));
updates$.next(config2);
expect(loggingSystem.setContextConfig).toHaveBeenNthCalledWith(
1,
['test', 'context'],
config1
);
expect(loggingSystem.setContextConfig).not.toHaveBeenCalledWith(['test', 'context'], config2);
});
});
describe('stop', () => {
it('stops forwarding updates to logging system', () => {
const updates$ = new Subject<LoggerContextConfigType>();
const config1: LoggerContextConfigType = {
appenders: new Map(),
loggers: [{ context: 'subcontext', appenders: ['console'], level: 'warn' }],
};
setup.configure(['test', 'context'], updates$);
service.stop();
updates$.next(config1);
expect(loggingSystem.setContextConfig).not.toHaveBeenCalledWith(['test', 'context'], config1);
});
});
});

View file

@ -16,112 +16,88 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Appenders, DisposableAppender } from './appenders/appenders';
import { BufferAppender } from './appenders/buffer/buffer_appender';
import { LogLevel } from './log_level';
import { BaseLogger, Logger } from './logger';
import { LoggerAdapter } from './logger_adapter';
import { LoggerFactory } from './logger_factory';
import { LoggingConfigType, LoggerConfigType, LoggingConfig } from './logging_config';
export type ILoggingService = PublicMethodsOf<LoggingService>;
import { Observable, Subscription } from 'rxjs';
import { CoreService } from '../../types';
import { LoggingConfig, LoggerContextConfigInput } from './logging_config';
import { ILoggingSystem } from './logging_system';
import { Logger } from './logger';
import { CoreContext } from '../core_context';
/**
* Service that is responsible for maintaining loggers and logger appenders.
* @internal
* Provides APIs to plugins for customizing the plugin's logger.
* @public
*/
export class LoggingService implements LoggerFactory {
private config?: LoggingConfig;
private readonly appenders: Map<string, DisposableAppender> = new Map();
private readonly bufferAppender = new BufferAppender();
private readonly loggers: Map<string, LoggerAdapter> = new Map();
public get(...contextParts: string[]): Logger {
const context = LoggingConfig.getLoggerContext(contextParts);
if (!this.loggers.has(context)) {
this.loggers.set(context, new LoggerAdapter(this.createLogger(context, this.config)));
}
return this.loggers.get(context)!;
}
export interface LoggingServiceSetup {
/**
* Safe wrapper that allows passing logging service as immutable LoggerFactory.
* Customizes the logging config for the plugin's context.
*
* @remarks
* Assumes that that the `context` property of the individual `logger` items emitted by `config$`
* are relative to the plugin's logging context (defaults to `plugins.<plugin_id>`).
*
* @example
* Customize the configuration for the plugins.data.search context.
* ```ts
* core.logging.configure(
* of({
* appenders: new Map(),
* loggers: [{ context: 'search', appenders: ['default'] }]
* })
* )
* ```
*
* @param config$
*/
public asLoggerFactory(): LoggerFactory {
return { get: (...contextParts: string[]) => this.get(...contextParts) };
configure(config$: Observable<LoggerContextConfigInput>): void;
}
/** @internal */
export interface InternalLoggingServiceSetup {
configure(contextParts: string[], config$: Observable<LoggerContextConfigInput>): void;
}
interface SetupDeps {
loggingSystem: ILoggingSystem;
}
/** @internal */
export class LoggingService implements CoreService<InternalLoggingServiceSetup> {
private readonly subscriptions = new Map<string, Subscription>();
private readonly log: Logger;
constructor(coreContext: CoreContext) {
this.log = coreContext.logger.get('logging');
}
/**
* Updates all current active loggers with the new config values.
* @param rawConfig New config instance.
*/
public upgrade(rawConfig: LoggingConfigType) {
const config = new LoggingConfig(rawConfig);
// Config update is asynchronous and may require some time to complete, so we should invalidate
// config so that new loggers will be using BufferAppender until newly configured appenders are ready.
this.config = undefined;
public setup({ loggingSystem }: SetupDeps) {
return {
configure: (contextParts: string[], config$: Observable<LoggerContextConfigInput>) => {
const contextName = LoggingConfig.getLoggerContext(contextParts);
this.log.debug(`Setting custom config for context [${contextName}]`);
// Appenders must be reset, so we first dispose of the current ones, then
// build up a new set of appenders.
for (const appender of this.appenders.values()) {
appender.dispose();
}
this.appenders.clear();
const existingSubscription = this.subscriptions.get(contextName);
if (existingSubscription) {
existingSubscription.unsubscribe();
}
for (const [appenderKey, appenderConfig] of config.appenders) {
this.appenders.set(appenderKey, Appenders.create(appenderConfig));
}
for (const [loggerKey, loggerAdapter] of this.loggers) {
loggerAdapter.updateLogger(this.createLogger(loggerKey, config));
}
this.config = config;
// Re-log all buffered log records with newly configured appenders.
for (const logRecord of this.bufferAppender.flush()) {
this.get(logRecord.context).log(logRecord);
}
// Might be fancier way to do this with rxjs, but this works and is simple to understand
this.subscriptions.set(
contextName,
config$.subscribe((config) => {
this.log.debug(`Updating logging config for context [${contextName}]`);
loggingSystem.setContextConfig(contextParts, config);
})
);
},
};
}
/**
* Disposes all loggers (closes log files, clears buffers etc.). Service is not usable after
* calling of this method until new config is provided via `upgrade` method.
* @returns Promise that is resolved once all loggers are successfully disposed.
*/
public async stop() {
for (const appender of this.appenders.values()) {
await appender.dispose();
public start() {}
public stop() {
for (const [, subscription] of this.subscriptions) {
subscription.unsubscribe();
}
await this.bufferAppender.dispose();
this.appenders.clear();
this.loggers.clear();
}
private createLogger(context: string, config: LoggingConfig | undefined) {
if (config === undefined) {
// If we don't have config yet, use `buffered` appender that will store all logged messages in the memory
// until the config is ready.
return new BaseLogger(context, LogLevel.All, [this.bufferAppender], this.asLoggerFactory());
}
const { level, appenders } = this.getLoggerConfigByContext(config, context);
const loggerLevel = LogLevel.fromId(level);
const loggerAppenders = appenders.map((appenderKey) => this.appenders.get(appenderKey)!);
return new BaseLogger(context, loggerLevel, loggerAppenders, this.asLoggerFactory());
}
private getLoggerConfigByContext(config: LoggingConfig, context: string): LoggerConfigType {
const loggerConfig = config.loggers.get(context);
if (loggerConfig !== undefined) {
return loggerConfig;
}
// If we don't have configuration for the specified context and it's the "nested" one (eg. `foo.bar.baz`),
// let's move up to the parent context (eg. `foo.bar`) and check if it has config we can rely on. Otherwise
// we fallback to the `root` context that should always be defined (enforced by configuration schema).
return this.getLoggerConfigByContext(config, LoggingConfig.getParentLoggerContext(context));
}
}

View file

@ -0,0 +1,84 @@
/*
* 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.
*/
// Test helpers to simplify mocking logs and collecting all their outputs
import { ILoggingSystem } from './logging_system';
import { LoggerFactory } from './logger_factory';
import { loggerMock, MockedLogger } from './logger.mock';
const createLoggingSystemMock = () => {
const mockLog = loggerMock.create();
mockLog.get.mockImplementation((...context) => ({
...mockLog,
context,
}));
const mocked: jest.Mocked<ILoggingSystem> = {
get: jest.fn(),
asLoggerFactory: jest.fn(),
setContextConfig: jest.fn(),
upgrade: jest.fn(),
stop: jest.fn(),
};
mocked.get.mockImplementation((...context) => ({
...mockLog,
context,
}));
mocked.asLoggerFactory.mockImplementation(() => mocked);
mocked.stop.mockResolvedValue();
return mocked;
};
const collectLoggingSystemMock = (loggerFactory: LoggerFactory) => {
const mockLog = loggerFactory.get() as MockedLogger;
return {
debug: mockLog.debug.mock.calls,
error: mockLog.error.mock.calls,
fatal: mockLog.fatal.mock.calls,
info: mockLog.info.mock.calls,
log: mockLog.log.mock.calls,
trace: mockLog.trace.mock.calls,
warn: mockLog.warn.mock.calls,
};
};
const clearLoggingSystemMock = (loggerFactory: LoggerFactory) => {
const mockedLoggerFactory = (loggerFactory as unknown) as jest.Mocked<ILoggingSystem>;
mockedLoggerFactory.get.mockClear();
mockedLoggerFactory.asLoggerFactory.mockClear();
mockedLoggerFactory.upgrade.mockClear();
mockedLoggerFactory.stop.mockClear();
const mockLog = loggerFactory.get() as MockedLogger;
mockLog.debug.mockClear();
mockLog.info.mockClear();
mockLog.warn.mockClear();
mockLog.error.mockClear();
mockLog.trace.mockClear();
mockLog.fatal.mockClear();
mockLog.log.mockClear();
};
export const loggingSystemMock = {
create: createLoggingSystemMock,
collect: collectLoggingSystemMock,
clear: clearLoggingSystemMock,
createLogger: loggerMock.create,
};

View file

@ -0,0 +1,348 @@
/*
* 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.
*/
const mockStreamWrite = jest.fn();
jest.mock('fs', () => ({
constants: {},
createWriteStream: jest.fn(() => ({ write: mockStreamWrite })),
}));
const dynamicProps = { pid: expect.any(Number) };
jest.mock('../../../legacy/server/logging/rotate', () => ({
setupLoggingRotate: jest.fn().mockImplementation(() => Promise.resolve({})),
}));
const timestamp = new Date(Date.UTC(2012, 1, 1, 14, 33, 22, 11));
let mockConsoleLog: jest.SpyInstance;
import { createWriteStream } from 'fs';
const mockCreateWriteStream = (createWriteStream as unknown) as jest.Mock<typeof createWriteStream>;
import { LoggingSystem, config } from '.';
let system: LoggingSystem;
beforeEach(() => {
mockConsoleLog = jest.spyOn(global.console, 'log').mockReturnValue(undefined);
jest.spyOn<any, any>(global, 'Date').mockImplementation(() => timestamp);
system = new LoggingSystem();
});
afterEach(() => {
jest.restoreAllMocks();
mockCreateWriteStream.mockClear();
mockStreamWrite.mockClear();
});
test('uses default memory buffer logger until config is provided', () => {
const bufferAppendSpy = jest.spyOn((system as any).bufferAppender, 'append');
const logger = system.get('test', 'context');
logger.trace('trace message');
// We shouldn't create new buffer appender for another context.
const anotherLogger = system.get('test', 'context2');
anotherLogger.fatal('fatal message', { some: 'value' });
expect(bufferAppendSpy).toHaveBeenCalledTimes(2);
expect(bufferAppendSpy.mock.calls[0][0]).toMatchSnapshot(dynamicProps);
expect(bufferAppendSpy.mock.calls[1][0]).toMatchSnapshot(dynamicProps);
});
test('flushes memory buffer logger and switches to real logger once config is provided', () => {
const logger = system.get('test', 'context');
logger.trace('buffered trace message');
logger.info('buffered info message', { some: 'value' });
logger.fatal('buffered fatal message');
const bufferAppendSpy = jest.spyOn((system as any).bufferAppender, 'append');
// Switch to console appender with `info` level, so that `trace` message won't go through.
system.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'info' },
})
);
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchSnapshot(
dynamicProps,
'buffered messages'
);
mockConsoleLog.mockClear();
// Now message should go straight to thew newly configured appender, not buffered one.
logger.info('some new info message');
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchSnapshot(dynamicProps, 'new messages');
expect(bufferAppendSpy).not.toHaveBeenCalled();
});
test('appends records via multiple appenders.', () => {
const loggerWithoutConfig = system.get('some-context');
const testsLogger = system.get('tests');
const testsChildLogger = system.get('tests', 'child');
loggerWithoutConfig.info('You know, just for your info.');
testsLogger.warn('Config is not ready!');
testsChildLogger.error('Too bad that config is not ready :/');
testsChildLogger.info('Just some info that should not be logged.');
expect(mockConsoleLog).not.toHaveBeenCalled();
expect(mockCreateWriteStream).not.toHaveBeenCalled();
system.upgrade(
config.schema.validate({
appenders: {
default: { kind: 'console', layout: { kind: 'pattern' } },
file: { kind: 'file', layout: { kind: 'pattern' }, path: 'path' },
},
loggers: [
{ appenders: ['file'], context: 'tests', level: 'warn' },
{ context: 'tests.child', level: 'error' },
],
})
);
// Now all logs should added to configured appenders.
expect(mockConsoleLog).toHaveBeenCalledTimes(1);
expect(mockConsoleLog.mock.calls[0][0]).toMatchSnapshot('console logs');
expect(mockStreamWrite).toHaveBeenCalledTimes(2);
expect(mockStreamWrite.mock.calls[0][0]).toMatchSnapshot('file logs');
expect(mockStreamWrite.mock.calls[1][0]).toMatchSnapshot('file logs');
});
test('uses `root` logger if context is not specified.', () => {
system.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'pattern' } } },
})
);
const rootLogger = system.get();
rootLogger.info('This message goes to a root context.');
expect(mockConsoleLog.mock.calls).toMatchSnapshot();
});
test('`stop()` disposes all appenders.', async () => {
system.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'info' },
})
);
const bufferDisposeSpy = jest.spyOn((system as any).bufferAppender, 'dispose');
const consoleDisposeSpy = jest.spyOn((system as any).appenders.get('default'), 'dispose');
await system.stop();
expect(bufferDisposeSpy).toHaveBeenCalledTimes(1);
expect(consoleDisposeSpy).toHaveBeenCalledTimes(1);
});
test('asLoggerFactory() only allows to create new loggers.', () => {
const logger = system.asLoggerFactory().get('test', 'context');
system.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'all' },
})
);
logger.trace('buffered trace message');
logger.info('buffered info message', { some: 'value' });
logger.fatal('buffered fatal message');
expect(Object.keys(system.asLoggerFactory())).toEqual(['get']);
expect(mockConsoleLog).toHaveBeenCalledTimes(3);
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchSnapshot(dynamicProps);
expect(JSON.parse(mockConsoleLog.mock.calls[1][0])).toMatchSnapshot(dynamicProps);
expect(JSON.parse(mockConsoleLog.mock.calls[2][0])).toMatchSnapshot(dynamicProps);
});
test('setContextConfig() updates config with relative contexts', () => {
const testsLogger = system.get('tests');
const testsChildLogger = system.get('tests', 'child');
const testsGrandchildLogger = system.get('tests', 'child', 'grandchild');
system.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'info' },
})
);
system.setContextConfig(['tests', 'child'], {
appenders: new Map([
[
'custom',
{ kind: 'console', layout: { kind: 'pattern', pattern: '[%level][%logger] %message' } },
],
]),
loggers: [{ context: 'grandchild', appenders: ['default', 'custom'], level: 'debug' }],
});
testsLogger.warn('tests log to default!');
testsChildLogger.error('tests.child log to default!');
testsGrandchildLogger.debug('tests.child.grandchild log to default and custom!');
expect(mockConsoleLog).toHaveBeenCalledTimes(4);
// Parent contexts are unaffected
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchObject({
context: 'tests',
message: 'tests log to default!',
level: 'WARN',
});
expect(JSON.parse(mockConsoleLog.mock.calls[1][0])).toMatchObject({
context: 'tests.child',
message: 'tests.child log to default!',
level: 'ERROR',
});
// Customized context is logged in both appender formats
expect(JSON.parse(mockConsoleLog.mock.calls[2][0])).toMatchObject({
context: 'tests.child.grandchild',
message: 'tests.child.grandchild log to default and custom!',
level: 'DEBUG',
});
expect(mockConsoleLog.mock.calls[3][0]).toMatchInlineSnapshot(
`"[DEBUG][tests.child.grandchild] tests.child.grandchild log to default and custom!"`
);
});
test('custom context configs are applied on subsequent calls to update()', () => {
system.setContextConfig(['tests', 'child'], {
appenders: new Map([
[
'custom',
{ kind: 'console', layout: { kind: 'pattern', pattern: '[%level][%logger] %message' } },
],
]),
loggers: [{ context: 'grandchild', appenders: ['default', 'custom'], level: 'debug' }],
});
// Calling upgrade after setContextConfig should not throw away the context-specific config
system.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'info' },
})
);
system
.get('tests', 'child', 'grandchild')
.debug('tests.child.grandchild log to default and custom!');
// Customized context is logged in both appender formats still
expect(mockConsoleLog).toHaveBeenCalledTimes(2);
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchObject({
context: 'tests.child.grandchild',
message: 'tests.child.grandchild log to default and custom!',
level: 'DEBUG',
});
expect(mockConsoleLog.mock.calls[1][0]).toMatchInlineSnapshot(
`"[DEBUG][tests.child.grandchild] tests.child.grandchild log to default and custom!"`
);
});
test('subsequent calls to setContextConfig() for the same context override the previous config', () => {
system.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'info' },
})
);
system.setContextConfig(['tests', 'child'], {
appenders: new Map([
[
'custom',
{ kind: 'console', layout: { kind: 'pattern', pattern: '[%level][%logger] %message' } },
],
]),
loggers: [{ context: 'grandchild', appenders: ['default', 'custom'], level: 'debug' }],
});
// Call again, this time with level: 'warn' and a different pattern
system.setContextConfig(['tests', 'child'], {
appenders: new Map([
[
'custom',
{
kind: 'console',
layout: { kind: 'pattern', pattern: '[%level][%logger] second pattern! %message' },
},
],
]),
loggers: [{ context: 'grandchild', appenders: ['default', 'custom'], level: 'warn' }],
});
const logger = system.get('tests', 'child', 'grandchild');
logger.debug('this should not show anywhere!');
logger.warn('tests.child.grandchild log to default and custom!');
// Only the warn log should have been logged
expect(mockConsoleLog).toHaveBeenCalledTimes(2);
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchObject({
context: 'tests.child.grandchild',
message: 'tests.child.grandchild log to default and custom!',
level: 'WARN',
});
expect(mockConsoleLog.mock.calls[1][0]).toMatchInlineSnapshot(
`"[WARN ][tests.child.grandchild] second pattern! tests.child.grandchild log to default and custom!"`
);
});
test('subsequent calls to setContextConfig() for the same context can disable the previous config', () => {
system.upgrade(
config.schema.validate({
appenders: { default: { kind: 'console', layout: { kind: 'json' } } },
root: { level: 'info' },
})
);
system.setContextConfig(['tests', 'child'], {
appenders: new Map([
[
'custom',
{ kind: 'console', layout: { kind: 'pattern', pattern: '[%level][%logger] %message' } },
],
]),
loggers: [{ context: 'grandchild', appenders: ['default', 'custom'], level: 'debug' }],
});
// Call again, this time no customizations (effectively disabling)
system.setContextConfig(['tests', 'child'], {});
const logger = system.get('tests', 'child', 'grandchild');
logger.debug('this should not show anywhere!');
logger.warn('tests.child.grandchild log to default!');
// Only the warn log should have been logged once on the default appender
expect(mockConsoleLog).toHaveBeenCalledTimes(1);
expect(JSON.parse(mockConsoleLog.mock.calls[0][0])).toMatchObject({
context: 'tests.child.grandchild',
message: 'tests.child.grandchild log to default!',
level: 'WARN',
});
});

View file

@ -0,0 +1,185 @@
/*
* 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 { Appenders, DisposableAppender } from './appenders/appenders';
import { BufferAppender } from './appenders/buffer/buffer_appender';
import { LogLevel } from './log_level';
import { BaseLogger, Logger } from './logger';
import { LoggerAdapter } from './logger_adapter';
import { LoggerFactory } from './logger_factory';
import {
LoggingConfigType,
LoggerConfigType,
LoggingConfig,
LoggerContextConfigType,
LoggerContextConfigInput,
loggerContextConfigSchema,
} from './logging_config';
export type ILoggingSystem = PublicMethodsOf<LoggingSystem>;
/**
* System that is responsible for maintaining loggers and logger appenders.
* @internal
*/
export class LoggingSystem implements LoggerFactory {
/** The configuration set by the user. */
private baseConfig?: LoggingConfig;
/** The fully computed configuration extended by context-specific configurations set programmatically */
private computedConfig?: LoggingConfig;
private readonly appenders: Map<string, DisposableAppender> = new Map();
private readonly bufferAppender = new BufferAppender();
private readonly loggers: Map<string, LoggerAdapter> = new Map();
private readonly contextConfigs = new Map<string, LoggerContextConfigType>();
public get(...contextParts: string[]): Logger {
const context = LoggingConfig.getLoggerContext(contextParts);
if (!this.loggers.has(context)) {
this.loggers.set(context, new LoggerAdapter(this.createLogger(context, this.computedConfig)));
}
return this.loggers.get(context)!;
}
/**
* Safe wrapper that allows passing logging service as immutable LoggerFactory.
*/
public asLoggerFactory(): LoggerFactory {
return { get: (...contextParts: string[]) => this.get(...contextParts) };
}
/**
* Updates all current active loggers with the new config values.
* @param rawConfig New config instance.
*/
public upgrade(rawConfig: LoggingConfigType) {
const config = new LoggingConfig(rawConfig)!;
this.applyBaseConfig(config);
}
/**
* Customizes the logging config for a specific context.
*
* @remarks
* Assumes that that the `context` property of the individual items in `rawConfig.loggers`
* are relative to the `baseContextParts`.
*
* @example
* Customize the configuration for the plugins.data.search context.
* ```ts
* loggingSystem.setContextConfig(
* ['plugins', 'data'],
* {
* loggers: [{ context: 'search', appenders: ['default'] }]
* }
* )
* ```
*
* @param baseContextParts
* @param rawConfig
*/
public setContextConfig(baseContextParts: string[], rawConfig: LoggerContextConfigInput) {
const context = LoggingConfig.getLoggerContext(baseContextParts);
const contextConfig = loggerContextConfigSchema.validate(rawConfig);
this.contextConfigs.set(context, {
...contextConfig,
// Automatically prepend the base context to the logger sub-contexts
loggers: contextConfig.loggers.map((l) => ({
...l,
context: LoggingConfig.getLoggerContext([context, l.context]),
})),
});
// If we already have a base config, apply the config. If not, custom context configs
// will be picked up on next call to `upgrade`.
if (this.baseConfig) {
this.applyBaseConfig(this.baseConfig);
}
}
/**
* Disposes all loggers (closes log files, clears buffers etc.). Service is not usable after
* calling of this method until new config is provided via `upgrade` method.
* @returns Promise that is resolved once all loggers are successfully disposed.
*/
public async stop() {
await Promise.all([...this.appenders.values()].map((a) => a.dispose()));
await this.bufferAppender.dispose();
this.appenders.clear();
this.loggers.clear();
}
private createLogger(context: string, config: LoggingConfig | undefined) {
if (config === undefined) {
// If we don't have config yet, use `buffered` appender that will store all logged messages in the memory
// until the config is ready.
return new BaseLogger(context, LogLevel.All, [this.bufferAppender], this.asLoggerFactory());
}
const { level, appenders } = this.getLoggerConfigByContext(config, context);
const loggerLevel = LogLevel.fromId(level);
const loggerAppenders = appenders.map((appenderKey) => this.appenders.get(appenderKey)!);
return new BaseLogger(context, loggerLevel, loggerAppenders, this.asLoggerFactory());
}
private getLoggerConfigByContext(config: LoggingConfig, context: string): LoggerConfigType {
const loggerConfig = config.loggers.get(context);
if (loggerConfig !== undefined) {
return loggerConfig;
}
// If we don't have configuration for the specified context and it's the "nested" one (eg. `foo.bar.baz`),
// let's move up to the parent context (eg. `foo.bar`) and check if it has config we can rely on. Otherwise
// we fallback to the `root` context that should always be defined (enforced by configuration schema).
return this.getLoggerConfigByContext(config, LoggingConfig.getParentLoggerContext(context));
}
private applyBaseConfig(newBaseConfig: LoggingConfig) {
const computedConfig = [...this.contextConfigs.values()].reduce(
(baseConfig, contextConfig) => baseConfig.extend(contextConfig),
newBaseConfig
);
// Appenders must be reset, so we first dispose of the current ones, then
// build up a new set of appenders.
for (const appender of this.appenders.values()) {
appender.dispose();
}
this.appenders.clear();
for (const [appenderKey, appenderConfig] of computedConfig.appenders) {
this.appenders.set(appenderKey, Appenders.create(appenderConfig));
}
for (const [loggerKey, loggerAdapter] of this.loggers) {
loggerAdapter.updateLogger(this.createLogger(loggerKey, computedConfig));
}
// We keep a reference to the base config so we can properly extend it
// on each config change.
this.baseConfig = newBaseConfig;
this.computedConfig = computedConfig;
// Re-log all buffered log records with newly configured appenders.
for (const logRecord of this.bufferAppender.flush()) {
this.get(logRecord.context).log(logRecord);
}
}
}

View file

@ -19,6 +19,7 @@
import { of } from 'rxjs';
import { duration } from 'moment';
import { PluginInitializerContext, CoreSetup, CoreStart, StartServicesAccessor } from '.';
import { loggingSystemMock } from './logging/logging_system.mock';
import { loggingServiceMock } from './logging/logging_service.mock';
import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
import { httpServiceMock } from './http/http_service.mock';
@ -42,7 +43,7 @@ export { sessionStorageMock } from './http/cookie_session_storage.mocks';
export { configServiceMock } from './config/config_service.mock';
export { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
export { httpServiceMock } from './http/http_service.mock';
export { loggingServiceMock } from './logging/logging_service.mock';
export { loggingSystemMock } from './logging/logging_system.mock';
export { savedObjectsRepositoryMock } from './saved_objects/service/lib/repository.mock';
export { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock';
export { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_objects/saved_objects_type_registry.mock';
@ -78,7 +79,7 @@ export function pluginInitializerContextConfigMock<T>(config: T) {
function pluginInitializerContextMock<T>(config: T = {} as T) {
const mock: PluginInitializerContext<T> = {
opaqueId: Symbol(),
logger: loggingServiceMock.create(),
logger: loggingSystemMock.create(),
env: {
mode: {
dev: true,
@ -130,6 +131,7 @@ function createCoreSetupMock({
metrics: metricsServiceMock.createSetupContract(),
uiSettings: uiSettingsMock,
uuid: uuidServiceMock.createSetupContract(),
logging: loggingServiceMock.createSetupContract(),
getStartServices: jest
.fn<Promise<[ReturnType<typeof createCoreStartMock>, object, any]>, []>()
.mockResolvedValue([createCoreStartMock(), pluginStartDeps, pluginStartContract]),
@ -163,6 +165,7 @@ function createInternalCoreSetupMock() {
httpResources: httpResourcesMock.createSetupContract(),
rendering: renderingMock.createSetupContract(),
uiSettings: uiSettingsServiceMock.createSetupContract(),
logging: loggingServiceMock.createInternalSetupContract(),
};
return setupDeps;
}

View file

@ -20,12 +20,12 @@
import { PluginDiscoveryErrorType } from './plugin_discovery_error';
import { mockReadFile } from './plugin_manifest_parser.test.mocks';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { resolve } from 'path';
import { parseManifest } from './plugin_manifest_parser';
const logger = loggingServiceMock.createLogger();
const logger = loggingSystemMock.createLogger();
const pluginPath = resolve('path', 'existent-dir');
const pluginManifestPath = resolve(pluginPath, 'kibana.json');
const packageInfo = {
@ -105,9 +105,9 @@ test('logs warning if pluginId is not in camelCase format', async () => {
cb(null, Buffer.from(JSON.stringify({ id: 'some_name', version: 'kibana', server: true })));
});
expect(loggingServiceMock.collect(logger).warn).toHaveLength(0);
expect(loggingSystemMock.collect(logger).warn).toHaveLength(0);
await parseManifest(pluginPath, packageInfo, logger);
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"Expect plugin \\"id\\" in camelCase, but found: some_name",

View file

@ -19,7 +19,7 @@
import { mockPackage, mockReaddir, mockReadFile, mockStat } from './plugins_discovery.test.mocks';
import { rawConfigServiceMock } from '../../config/raw_config_service.mock';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { resolve } from 'path';
import { first, map, toArray } from 'rxjs/operators';
@ -37,7 +37,7 @@ const TEST_PLUGIN_SEARCH_PATHS = {
};
const TEST_EXTRA_PLUGIN_PATH = resolve(process.cwd(), 'my-extra-plugin');
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
beforeEach(() => {
mockReaddir.mockImplementation((path, cb) => {
@ -221,7 +221,7 @@ test('logs a warning about --plugin-path when used in development', async () =>
logger,
});
expect(loggingServiceMock.collect(logger).warn).toEqual([
expect(loggingSystemMock.collect(logger).warn).toEqual([
[
`Explicit plugin paths [${TEST_EXTRA_PLUGIN_PATH}] should only be used in development. Relative imports may not work properly in production.`,
],
@ -263,5 +263,5 @@ test('does not log a warning about --plugin-path when used in production', async
logger,
});
expect(loggingServiceMock.collect(logger).warn).toEqual([]);
expect(loggingSystemMock.collect(logger).warn).toEqual([]);
});

View file

@ -27,13 +27,13 @@ import { getEnvOptions } from '../../config/__mocks__/env';
import { BehaviorSubject, from } from 'rxjs';
import { rawConfigServiceMock } from '../../config/raw_config_service.mock';
import { config } from '../plugins_config';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { coreMock } from '../../mocks';
import { Plugin } from '../types';
import { PluginWrapper } from '../plugin';
describe('PluginsService', () => {
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
let pluginsService: PluginsService;
const createPlugin = (

View file

@ -26,14 +26,14 @@ import { getEnvOptions } from '../config/__mocks__/env';
import { CoreContext } from '../core_context';
import { coreMock } from '../mocks';
import { configServiceMock } from '../config/config_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { PluginWrapper } from './plugin';
import { PluginManifest } from './types';
import { createPluginInitializerContext, createPluginSetupContext } from './plugin_context';
const mockPluginInitializer = jest.fn();
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
jest.doMock(
join('plugin-with-initializer-path', 'server'),
() => ({ plugin: mockPluginInitializer }),

View file

@ -95,8 +95,6 @@ export class PluginWrapper<
public async setup(setupContext: CoreSetup<TPluginsStart>, plugins: TPluginsSetup) {
this.instance = this.createPluginInstance();
this.log.debug('Setting up plugin');
return this.instance.setup(setupContext, plugins);
}
@ -112,8 +110,6 @@ export class PluginWrapper<
throw new Error(`Plugin "${this.name}" can't be started since it isn't set up.`);
}
this.log.debug('Starting plugin');
const startContract = await this.instance.start(startContext, plugins);
this.startDependencies$.next([startContext, plugins, startContract]);
return startContract;
@ -127,8 +123,6 @@ export class PluginWrapper<
throw new Error(`Plugin "${this.name}" can't be stopped since it isn't set up.`);
}
this.log.info('Stopping plugin');
if (typeof this.instance.stop === 'function') {
await this.instance.stop();
}

View file

@ -22,14 +22,14 @@ import { first } from 'rxjs/operators';
import { createPluginInitializerContext } from './plugin_context';
import { CoreContext } from '../core_context';
import { Env } from '../config';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { rawConfigServiceMock } from '../config/raw_config_service.mock';
import { getEnvOptions } from '../config/__mocks__/env';
import { PluginManifest } from './types';
import { Server } from '../server';
import { fromRoot } from '../utils';
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
let coreId: symbol;
let env: Env;

View file

@ -166,6 +166,9 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
csp: deps.http.csp,
getServerInfo: deps.http.getServerInfo,
},
logging: {
configure: (config$) => deps.logging.configure(['plugins', plugin.name], config$),
},
metrics: {
getOpsMetrics$: deps.metrics.getOpsMetrics$,
},

View file

@ -28,7 +28,7 @@ import { ConfigPath, ConfigService, Env } from '../config';
import { rawConfigServiceMock } from '../config/raw_config_service.mock';
import { getEnvOptions } from '../config/__mocks__/env';
import { coreMock } from '../mocks';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { PluginDiscoveryError } from './discovery';
import { PluginWrapper } from './plugin';
import { PluginsService } from './plugins_service';
@ -47,7 +47,7 @@ let env: Env;
let mockPluginSystem: jest.Mocked<PluginsSystem>;
const setupDeps = coreMock.createInternalSetup();
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
expect.addSnapshotSerializer(createAbsolutePathSerializer());
@ -138,7 +138,7 @@ describe('PluginsService', () => {
[Error: Failed to initialize plugins:
Invalid JSON (invalid-manifest, path-1)]
`);
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Invalid JSON (invalid-manifest, path-1)],
@ -159,7 +159,7 @@ describe('PluginsService', () => {
[Error: Failed to initialize plugins:
Incompatible version (incompatible-version, path-3)]
`);
expect(loggingServiceMock.collect(logger).error).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
[Error: Incompatible version (incompatible-version, path-3)],
@ -238,7 +238,7 @@ describe('PluginsService', () => {
expect(mockPluginSystem.setupPlugins).toHaveBeenCalledTimes(1);
expect(mockPluginSystem.setupPlugins).toHaveBeenCalledWith(setupDeps);
expect(loggingServiceMock.collect(logger).info).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).info).toMatchInlineSnapshot(`
Array [
Array [
"Plugin \\"explicitly-disabled-plugin\\" is disabled.",
@ -360,7 +360,7 @@ describe('PluginsService', () => {
{ coreId, env, logger, configService }
);
const logs = loggingServiceMock.collect(logger);
const logs = loggingSystemMock.collect(logger);
expect(logs.info).toHaveLength(0);
expect(logs.error).toHaveLength(0);
});

View file

@ -28,7 +28,7 @@ import { Env } from '../config';
import { getEnvOptions } from '../config/__mocks__/env';
import { CoreContext } from '../core_context';
import { configServiceMock } from '../config/config_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { PluginWrapper } from './plugin';
import { PluginName } from './types';
@ -36,7 +36,7 @@ import { PluginsSystem } from './plugins_system';
import { coreMock } from '../mocks';
import { Logger } from '../logging';
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
function createPlugin(
id: string,
{

View file

@ -17,10 +17,10 @@
* under the License.
*/
import { loggingServiceMock } from '../logging/logging_service.mock';
export const logger = loggingServiceMock.create();
jest.doMock('../logging/logging_service', () => ({
LoggingService: jest.fn(() => logger),
import { loggingSystemMock } from '../logging/logging_system.mock';
export const logger = loggingSystemMock.create();
jest.doMock('../logging/logging_system', () => ({
LoggingSystem: jest.fn(() => logger),
}));
import { configServiceMock } from '../config/config_service.mock';

View file

@ -21,7 +21,7 @@ import { ConnectableObservable, Subscription } from 'rxjs';
import { first, map, publishReplay, switchMap, tap } from 'rxjs/operators';
import { Env, RawConfigurationProvider } from '../config';
import { Logger, LoggerFactory, LoggingConfigType, LoggingService } from '../logging';
import { Logger, LoggerFactory, LoggingConfigType, LoggingSystem } from '../logging';
import { Server } from '../server';
/**
@ -30,7 +30,7 @@ import { Server } from '../server';
export class Root {
public readonly logger: LoggerFactory;
private readonly log: Logger;
private readonly loggingService: LoggingService;
private readonly loggingSystem: LoggingSystem;
private readonly server: Server;
private loggingConfigSubscription?: Subscription;
@ -39,10 +39,10 @@ export class Root {
env: Env,
private readonly onShutdown?: (reason?: Error | string) => void
) {
this.loggingService = new LoggingService();
this.logger = this.loggingService.asLoggerFactory();
this.loggingSystem = new LoggingSystem();
this.logger = this.loggingSystem.asLoggerFactory();
this.log = this.logger.get('root');
this.server = new Server(rawConfigProvider, env, this.logger);
this.server = new Server(rawConfigProvider, env, this.loggingSystem);
}
public async setup() {
@ -86,7 +86,7 @@ export class Root {
this.loggingConfigSubscription.unsubscribe();
this.loggingConfigSubscription = undefined;
}
await this.loggingService.stop();
await this.loggingSystem.stop();
if (this.onShutdown !== undefined) {
this.onShutdown(reason);
@ -99,7 +99,7 @@ export class Root {
const update$ = configService.getConfig$().pipe(
// always read the logging config when the underlying config object is re-read
switchMap(() => configService.atPath<LoggingConfigType>('logging')),
map((config) => this.loggingService.upgrade(config)),
map((config) => this.loggingSystem.upgrade(config)),
// This specifically console.logs because we were not able to configure the logger.
// eslint-disable-next-line no-console
tap({ error: (err) => console.error('Configuring logger failed:', err) }),

View file

@ -20,11 +20,11 @@
import _ from 'lodash';
import { SavedObjectUnsanitizedDoc } from '../../serialization';
import { DocumentMigrator } from './document_migrator';
import { loggingServiceMock } from '../../../logging/logging_service.mock';
import { loggingSystemMock } from '../../../logging/logging_system.mock';
import { SavedObjectsType } from '../../types';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
const mockLoggerFactory = loggingServiceMock.create();
const mockLoggerFactory = loggingSystemMock.create();
const mockLogger = mockLoggerFactory.get('mock logger');
const createRegistry = (...types: Array<Partial<SavedObjectsType>>) => {
@ -572,7 +572,7 @@ describe('DocumentMigrator', () => {
expect('Did not throw').toEqual('But it should have!');
} catch (error) {
expect(error.message).toMatch(/Dang diggity!/);
const warning = loggingServiceMock.collect(mockLoggerFactory).warn[0][0];
const warning = loggingSystemMock.collect(mockLoggerFactory).warn[0][0];
expect(warning).toContain(JSON.stringify(failedDoc));
expect(warning).toContain('dog:1.2.3');
}
@ -601,8 +601,8 @@ describe('DocumentMigrator', () => {
migrationVersion: {},
};
migrator.migrate(doc);
expect(loggingServiceMock.collect(mockLoggerFactory).info[0][0]).toEqual(logTestMsg);
expect(loggingServiceMock.collect(mockLoggerFactory).warn[1][0]).toEqual(logTestMsg);
expect(loggingSystemMock.collect(mockLoggerFactory).info[0][0]).toEqual(logTestMsg);
expect(loggingSystemMock.collect(mockLoggerFactory).warn[1][0]).toEqual(logTestMsg);
});
test('extracts the latest migration version info', () => {

View file

@ -21,7 +21,7 @@ import _ from 'lodash';
import { SavedObjectUnsanitizedDoc, SavedObjectsSerializer } from '../../serialization';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
import { IndexMigrator } from './index_migrator';
import { loggingServiceMock } from '../../../logging/logging_service.mock';
import { loggingSystemMock } from '../../../logging/logging_system.mock';
describe('IndexMigrator', () => {
let testOpts: any;
@ -31,7 +31,7 @@ describe('IndexMigrator', () => {
batchSize: 10,
callCluster: jest.fn(),
index: '.kibana',
log: loggingServiceMock.create().get(),
log: loggingSystemMock.create().get(),
mappingProperties: {},
pollInterval: 1,
scrollDuration: '1m',

View file

@ -19,7 +19,7 @@
import { take } from 'rxjs/operators';
import { KibanaMigratorOptions, KibanaMigrator } from './kibana_migrator';
import { loggingServiceMock } from '../../../logging/logging_service.mock';
import { loggingSystemMock } from '../../../logging/logging_system.mock';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
import { SavedObjectsType } from '../../types';
@ -110,7 +110,7 @@ describe('KibanaMigrator', () => {
function mockOptions(): KibanaMigratorOptions {
const callCluster = jest.fn();
return {
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
kibanaVersion: '8.2.3',
savedObjectValidations: {},
typeRegistry: createRegistry([

View file

@ -20,7 +20,7 @@
import supertest from 'supertest';
import { UnwrapPromise } from '@kbn/utility-types';
import { registerLogLegacyImportRoute } from '../log_legacy_import';
import { loggingServiceMock } from '../../../logging/logging_service.mock';
import { loggingSystemMock } from '../../../logging/logging_system.mock';
import { setupServer } from '../test_utils';
type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
@ -28,11 +28,11 @@ type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
describe('POST /api/saved_objects/_log_legacy_import', () => {
let server: setupServerReturn['server'];
let httpSetup: setupServerReturn['httpSetup'];
let logger: ReturnType<typeof loggingServiceMock.createLogger>;
let logger: ReturnType<typeof loggingSystemMock.createLogger>;
beforeEach(async () => {
({ server, httpSetup } = await setupServer());
logger = loggingServiceMock.createLogger();
logger = loggingSystemMock.createLogger();
const router = httpSetup.createRouter('/api/saved_objects/');
registerLogLegacyImportRoute(router, logger);
@ -50,7 +50,7 @@ describe('POST /api/saved_objects/_log_legacy_import', () => {
.expect(200);
expect(result.body).toEqual({ success: true });
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"Importing saved objects from a .json file has been deprecated",

View file

@ -388,6 +388,11 @@ export interface APICaller {
<T = any>(endpoint: string, clientParams?: Record<string, any>, options?: CallAPIOptions): Promise<T>;
}
// Warning: (ae-forgotten-export) The symbol "appendersSchema" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type AppenderConfigType = TypeOf<typeof appendersSchema>;
// @public
export function assertNever(x: never): never;
@ -574,6 +579,72 @@ export const config: {
ignoreVersionMismatch: import("@kbn/config-schema/target/types/types").ConditionalType<false, boolean, boolean>;
}>;
};
logging: {
appenders: import("@kbn/config-schema").Type<Readonly<{} & {
layout: Readonly<{} & {
kind: "json";
}> | Readonly<{
pattern?: string | undefined;
highlight?: boolean | undefined;
} & {
kind: "pattern";
}>;
kind: "console";
}> | Readonly<{} & {
path: string;
layout: Readonly<{} & {
kind: "json";
}> | Readonly<{
pattern?: string | undefined;
highlight?: boolean | undefined;
} & {
kind: "pattern";
}>;
kind: "file";
}> | Readonly<{
legacyLoggingConfig?: any;
} & {
kind: "legacy-appender";
}>>;
loggers: import("@kbn/config-schema").ObjectType<{
appenders: import("@kbn/config-schema").Type<string[]>;
context: import("@kbn/config-schema").Type<string>;
level: import("@kbn/config-schema").Type<import("./logging/log_level").LogLevelId>;
}>;
loggerContext: import("@kbn/config-schema").ObjectType<{
appenders: import("@kbn/config-schema").Type<Map<string, Readonly<{} & {
layout: Readonly<{} & {
kind: "json";
}> | Readonly<{
pattern?: string | undefined;
highlight?: boolean | undefined;
} & {
kind: "pattern";
}>;
kind: "console";
}> | Readonly<{} & {
path: string;
layout: Readonly<{} & {
kind: "json";
}> | Readonly<{
pattern?: string | undefined;
highlight?: boolean | undefined;
} & {
kind: "pattern";
}>;
kind: "file";
}> | Readonly<{
legacyLoggingConfig?: any;
} & {
kind: "legacy-appender";
}>>>;
loggers: import("@kbn/config-schema").Type<Readonly<{} & {
context: string;
appenders: string[];
level: import("./logging/log_level").LogLevelId;
}>[]>;
}>;
};
};
// @public
@ -639,6 +710,8 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
resources: HttpResources;
};
// (undocumented)
logging: LoggingServiceSetup;
// (undocumented)
metrics: MetricsServiceSetup;
// (undocumented)
savedObjects: SavedObjectsServiceSetup;
@ -1270,11 +1343,29 @@ export interface Logger {
warn(errorOrMessage: string | Error, meta?: LogMeta): void;
}
// Warning: (ae-forgotten-export) The symbol "loggerSchema" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type LoggerConfigType = TypeOf<typeof loggerSchema>;
// @public (undocumented)
export interface LoggerContextConfigInput {
// (undocumented)
appenders?: Record<string, AppenderConfigType> | Map<string, AppenderConfigType>;
// (undocumented)
loggers?: LoggerConfigType[];
}
// @public
export interface LoggerFactory {
get(...contextParts: string[]): Logger;
}
// @public
export interface LoggingServiceSetup {
configure(config$: Observable<LoggerContextConfigInput>): void;
}
// @internal
export class LogLevel {
// (undocumented)

View file

@ -91,3 +91,9 @@ export const mockStatusService = statusServiceMock.create();
jest.doMock('./status/status_service', () => ({
StatusService: jest.fn(() => mockStatusService),
}));
import { loggingServiceMock } from './logging/logging_service.mock';
export const mockLoggingService = loggingServiceMock.create();
jest.doMock('./logging/logging_service', () => ({
LoggingService: jest.fn(() => mockLoggingService),
}));

View file

@ -30,6 +30,7 @@ import {
mockRenderingService,
mockMetricsService,
mockStatusService,
mockLoggingService,
} from './server.test.mocks';
import { BehaviorSubject } from 'rxjs';
@ -37,11 +38,11 @@ import { Env } from './config';
import { Server } from './server';
import { getEnvOptions } from './config/__mocks__/env';
import { loggingServiceMock } from './logging/logging_service.mock';
import { loggingSystemMock } from './logging/logging_system.mock';
import { rawConfigServiceMock } from './config/raw_config_service.mock';
const env = new Env('.', getEnvOptions());
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
const rawConfigService = rawConfigServiceMock.create({});
beforeEach(() => {
@ -68,6 +69,7 @@ test('sets up services on "setup"', async () => {
expect(mockRenderingService.setup).not.toHaveBeenCalled();
expect(mockMetricsService.setup).not.toHaveBeenCalled();
expect(mockStatusService.setup).not.toHaveBeenCalled();
expect(mockLoggingService.setup).not.toHaveBeenCalled();
await server.setup();
@ -80,6 +82,7 @@ test('sets up services on "setup"', async () => {
expect(mockRenderingService.setup).toHaveBeenCalledTimes(1);
expect(mockMetricsService.setup).toHaveBeenCalledTimes(1);
expect(mockStatusService.setup).toHaveBeenCalledTimes(1);
expect(mockLoggingService.setup).toHaveBeenCalledTimes(1);
});
test('injects legacy dependency to context#setup()', async () => {
@ -151,6 +154,7 @@ test('stops services on "stop"', async () => {
expect(mockUiSettingsService.stop).not.toHaveBeenCalled();
expect(mockMetricsService.stop).not.toHaveBeenCalled();
expect(mockStatusService.stop).not.toHaveBeenCalled();
expect(mockLoggingService.stop).not.toHaveBeenCalled();
await server.stop();
@ -162,6 +166,7 @@ test('stops services on "stop"', async () => {
expect(mockUiSettingsService.stop).toHaveBeenCalledTimes(1);
expect(mockMetricsService.stop).toHaveBeenCalledTimes(1);
expect(mockStatusService.stop).toHaveBeenCalledTimes(1);
expect(mockLoggingService.stop).toHaveBeenCalledTimes(1);
});
test(`doesn't setup core services if config validation fails`, async () => {
@ -179,6 +184,7 @@ test(`doesn't setup core services if config validation fails`, async () => {
expect(mockRenderingService.setup).not.toHaveBeenCalled();
expect(mockMetricsService.setup).not.toHaveBeenCalled();
expect(mockStatusService.setup).not.toHaveBeenCalled();
expect(mockLoggingService.setup).not.toHaveBeenCalled();
});
test(`doesn't setup core services if legacy config validation fails`, async () => {
@ -200,4 +206,5 @@ test(`doesn't setup core services if legacy config validation fails`, async () =
expect(mockUiSettingsService.setup).not.toHaveBeenCalled();
expect(mockMetricsService.setup).not.toHaveBeenCalled();
expect(mockStatusService.setup).not.toHaveBeenCalled();
expect(mockLoggingService.setup).not.toHaveBeenCalled();
});

View file

@ -32,7 +32,7 @@ import { HttpService } from './http';
import { HttpResourcesService } from './http_resources';
import { RenderingService } from './rendering';
import { LegacyService, ensureValidConfiguration } from './legacy';
import { Logger, LoggerFactory } from './logging';
import { Logger, LoggerFactory, LoggingService, ILoggingSystem } from './logging';
import { UiSettingsService } from './ui_settings';
import { PluginsService, config as pluginsConfig } from './plugins';
import { SavedObjectsService } from '../server/saved_objects';
@ -74,20 +74,23 @@ export class Server {
private readonly metrics: MetricsService;
private readonly httpResources: HttpResourcesService;
private readonly status: StatusService;
private readonly logging: LoggingService;
private readonly coreApp: CoreApp;
#pluginsInitialized?: boolean;
private coreStart?: InternalCoreStart;
private readonly logger: LoggerFactory;
constructor(
rawConfigProvider: RawConfigurationProvider,
public readonly env: Env,
private readonly logger: LoggerFactory
private readonly loggingSystem: ILoggingSystem
) {
this.logger = this.loggingSystem.asLoggerFactory();
this.log = this.logger.get('server');
this.configService = new ConfigService(rawConfigProvider, env, logger);
this.configService = new ConfigService(rawConfigProvider, env, this.logger);
const core = { coreId, configService: this.configService, env, logger };
const core = { coreId, configService: this.configService, env, logger: this.logger };
this.context = new ContextService(core);
this.http = new HttpService(core);
this.rendering = new RenderingService(core);
@ -102,6 +105,7 @@ export class Server {
this.status = new StatusService(core);
this.coreApp = new CoreApp(core);
this.httpResources = new HttpResourcesService(core);
this.logging = new LoggingService(core);
}
public async setup() {
@ -164,6 +168,10 @@ export class Server {
savedObjects: savedObjectsSetup,
});
const loggingSetup = this.logging.setup({
loggingSystem: this.loggingSystem,
});
const coreSetup: InternalCoreSetup = {
capabilities: capabilitiesSetup,
context: contextServiceSetup,
@ -176,6 +184,7 @@ export class Server {
metrics: metricsSetup,
rendering: renderingSetup,
httpResources: httpResourcesSetup,
logging: loggingSetup,
};
const pluginsSetup = await this.plugins.setup(coreSetup);
@ -244,6 +253,7 @@ export class Server {
await this.rendering.stop();
await this.metrics.stop();
await this.status.stop();
await this.logging.stop();
}
private registerCoreContext(coreSetup: InternalCoreSetup) {

View file

@ -21,7 +21,7 @@ import Chance from 'chance';
import { SavedObjectsErrorHelpers } from '../../saved_objects';
import { savedObjectsClientMock } from '../../saved_objects/service/saved_objects_client.mock';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { getUpgradeableConfigMock } from './get_upgradeable_config.test.mock';
import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config';
@ -35,7 +35,7 @@ describe('uiSettings/createOrUpgradeSavedConfig', function () {
const buildNum = chance.integer({ min: 1000, max: 5000 });
function setup() {
const logger = loggingServiceMock.create();
const logger = loggingSystemMock.create();
const getUpgradeableConfig = getUpgradeableConfigMock;
const savedObjectsClient = savedObjectsClientMock.create();
savedObjectsClient.create.mockImplementation(
@ -137,7 +137,7 @@ describe('uiSettings/createOrUpgradeSavedConfig', function () {
});
await run();
expect(loggingServiceMock.collect(logger).debug).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(`
Array [
Array [
"Upgrade config from 4.0.0 to 4.0.1",
@ -169,7 +169,7 @@ describe('uiSettings/createOrUpgradeSavedConfig', function () {
expect(error.message).toBe('foo');
}
expect(loggingServiceMock.collect(logger).debug).toHaveLength(0);
expect(loggingSystemMock.collect(logger).debug).toHaveLength(0);
});
});

View file

@ -26,10 +26,10 @@ import {
TestUtils,
} from '../../../../../test_utils/kbn_server';
import { createOrUpgradeSavedConfig } from '../create_or_upgrade_saved_config';
import { loggingServiceMock } from '../../../logging/logging_service.mock';
import { loggingSystemMock } from '../../../logging/logging_system.mock';
import { httpServerMock } from '../../../http/http_server.mocks';
const logger = loggingServiceMock.create().get();
const logger = loggingSystemMock.create().get();
describe('createOrUpgradeSavedConfig()', () => {
let savedObjectsClient: SavedObjectsClientContract;
let servers: TestUtils;

View file

@ -20,7 +20,7 @@
import Chance from 'chance';
import { schema } from '@kbn/config-schema';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { createOrUpgradeSavedConfigMock } from './create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.mock';
import { SavedObjectsClient } from '../saved_objects';
@ -28,7 +28,7 @@ import { savedObjectsClientMock } from '../saved_objects/service/saved_objects_c
import { UiSettingsClient } from './ui_settings_client';
import { CannotOverrideError } from './ui_settings_errors';
const logger = loggingServiceMock.create().get();
const logger = loggingSystemMock.create().get();
const TYPE = 'config';
const ID = 'kibana-version';
@ -375,7 +375,7 @@ describe('ui settings', () => {
},
});
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].",
@ -517,7 +517,7 @@ describe('ui settings', () => {
user: 'foo',
});
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].",
@ -645,7 +645,7 @@ describe('ui settings', () => {
expect(await uiSettings.get('id')).toBe(42);
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].",

View file

@ -21,7 +21,7 @@ import { join } from 'path';
import { readFile, writeFile } from './fs';
import { resolveInstanceUuid, UUID_7_6_0_BUG } from './resolve_uuid';
import { configServiceMock } from '../config/config_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { BehaviorSubject } from 'rxjs';
import { Logger } from '../logging';
@ -93,7 +93,7 @@ describe('resolveInstanceUuid', () => {
mockReadFile({ uuid: DEFAULT_FILE_UUID });
mockWriteFile();
configService = getConfigService(DEFAULT_CONFIG_UUID);
logger = loggingServiceMock.create().get() as any;
logger = loggingSystemMock.create().get() as any;
});
describe('when file is present and config property is set', () => {

View file

@ -21,7 +21,7 @@ import { UuidService } from './uuid_service';
import { resolveInstanceUuid } from './resolve_uuid';
import { CoreContext } from '../core_context';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { loggingSystemMock } from '../logging/logging_system.mock';
import { mockCoreContext } from '../core_context.mock';
import { Env } from '../config';
import { getEnvOptions } from '../config/__mocks__/env';
@ -31,12 +31,12 @@ jest.mock('./resolve_uuid', () => ({
}));
describe('UuidService', () => {
let logger: ReturnType<typeof loggingServiceMock.create>;
let logger: ReturnType<typeof loggingSystemMock.create>;
let coreContext: CoreContext;
beforeEach(() => {
jest.clearAllMocks();
logger = loggingServiceMock.create();
logger = loggingSystemMock.create();
coreContext = mockCoreContext.create({ logger });
});

View file

@ -20,7 +20,7 @@
import { shortUrlLookupProvider, ShortUrlLookupService, UrlAttributes } from './short_url_lookup';
import { SavedObjectsClientContract, SavedObject } from 'kibana/server';
import { savedObjectsClientMock, loggingServiceMock } from '../../../../../core/server/mocks';
import { savedObjectsClientMock, loggingSystemMock } from '../../../../../core/server/mocks';
describe('shortUrlLookupProvider', () => {
const ID = 'bf00ad16941fc51420f91a93428b27a0';
@ -35,7 +35,7 @@ describe('shortUrlLookupProvider', () => {
savedObjects = savedObjectsClientMock.create();
savedObjects.create.mockResolvedValue({ id: ID } as SavedObject<UrlAttributes>);
deps = { savedObjects };
shortUrl = shortUrlLookupProvider({ logger: loggingServiceMock.create().get() });
shortUrl = shortUrlLookupProvider({ logger: loggingSystemMock.create().get() });
});
describe('generateUrlId', () => {

View file

@ -21,9 +21,9 @@ import { noop } from 'lodash';
import { Collector } from './collector';
import { CollectorSet } from './collector_set';
import { UsageCollector } from './usage_collector';
import { loggingServiceMock } from '../../../../core/server/mocks';
import { loggingSystemMock } from '../../../../core/server/mocks';
const logger = loggingServiceMock.createLogger();
const logger = loggingSystemMock.createLogger();
const loggerSpies = {
debug: jest.spyOn(logger, 'debug'),

View file

@ -17,14 +17,14 @@
* under the License.
*/
import { loggingServiceMock } from '../../../core/server/mocks';
import { loggingSystemMock } from '../../../core/server/mocks';
import { UsageCollectionSetup } from './plugin';
import { CollectorSet } from './collector';
const createSetupContract = () => {
return {
...new CollectorSet({
logger: loggingServiceMock.createLogger(),
logger: loggingSystemMock.createLogger(),
maximumWaitTimeForAllCollectorsInS: 1,
}),
} as UsageCollectionSetup;

View file

@ -0,0 +1,7 @@
{
"id": "core_logging",
"version": "0.0.1",
"kibanaVersion": "kibana",
"configPath": ["core_logging"],
"server": true
}

View file

@ -0,0 +1 @@
/*debug.log

View file

@ -0,0 +1,23 @@
/*
* 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);

View file

@ -0,0 +1,118 @@
/*
* 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() {}
}

View file

@ -0,0 +1,13 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"outDir": "./target",
"skipLibCheck": true
},
"include": [
"index.ts",
"server/**/*.ts",
"../../../../typings/**/*",
],
"exclude": []
}

View file

@ -30,5 +30,6 @@ export default function ({ loadTestFile }: PluginFunctionalProviderContext) {
loadTestFile(require.resolve('./application_leave_confirm'));
loadTestFile(require.resolve('./application_status'));
loadTestFile(require.resolve('./rendering'));
loadTestFile(require.resolve('./logging'));
});
}

View file

@ -0,0 +1,146 @@
/*
* 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',
]);
});
});
}

View file

@ -9,7 +9,7 @@ import { ActionTypeRegistry } from '../action_type_registry';
import { taskManagerMock } from '../../../task_manager/server/task_manager.mock';
import { registerBuiltInActionTypes } from './index';
import { Logger } from '../../../../../src/core/server';
import { loggingServiceMock } from '../../../../../src/core/server/mocks';
import { loggingSystemMock } from '../../../../../src/core/server/mocks';
import { actionsConfigMock } from '../actions_config.mock';
import { licenseStateMock } from '../lib/license_state.mock';
@ -19,7 +19,7 @@ export function createActionTypeRegistry(): {
logger: jest.Mocked<Logger>;
actionTypeRegistry: ActionTypeRegistry;
} {
const logger = loggingServiceMock.create().get() as jest.Mocked<Logger>;
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
const actionTypeRegistry = new ActionTypeRegistry({
taskManager: taskManagerMock.setup(),
taskRunnerFactory: new TaskRunnerFactory(

View file

@ -10,14 +10,14 @@ jest.mock('nodemailer', () => ({
import { Logger } from '../../../../../../src/core/server';
import { sendEmail } from './send_email';
import { loggingServiceMock } from '../../../../../../src/core/server/mocks';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import nodemailer from 'nodemailer';
const createTransportMock = nodemailer.createTransport as jest.Mock;
const sendMailMockResult = { result: 'does not matter' };
const sendMailMock = jest.fn();
const mockLogger = loggingServiceMock.create().get() as jest.Mocked<Logger>;
const mockLogger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
describe('send_email module', () => {
beforeEach(() => {

View file

@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema';
import { ActionExecutor } from './action_executor';
import { actionTypeRegistryMock } from '../action_type_registry.mock';
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
import { loggingServiceMock, savedObjectsClientMock } from '../../../../../src/core/server/mocks';
import { loggingSystemMock, savedObjectsClientMock } from '../../../../../src/core/server/mocks';
import { eventLoggerMock } from '../../../event_log/server/mocks';
import { spacesServiceMock } from '../../../spaces/server/spaces_service/spaces_service.mock';
import { ActionType } from '../types';
@ -31,7 +31,7 @@ const executeParams = {
const spacesMock = spacesServiceMock.createSetupContract();
actionExecutor.initialize({
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
spaces: spacesMock,
getServices: () => services,
getScopedSavedObjectsClient: () => savedObjectsClientWithHidden,
@ -266,7 +266,7 @@ test('should not throws an error if actionType is preconfigured', async () => {
test('throws an error when passing isESOUsingEphemeralEncryptionKey with value of true', async () => {
const customActionExecutor = new ActionExecutor({ isESOUsingEphemeralEncryptionKey: true });
customActionExecutor.initialize({
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
spaces: spacesMock,
getScopedSavedObjectsClient: () => savedObjectsClientWithHidden,
getServices: () => services,

View file

@ -12,7 +12,7 @@ import { TaskRunnerFactory } from './task_runner_factory';
import { actionTypeRegistryMock } from '../action_type_registry.mock';
import { actionExecutorMock } from './action_executor.mock';
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
import { savedObjectsClientMock, loggingServiceMock } from 'src/core/server/mocks';
import { savedObjectsClientMock, loggingSystemMock } from 'src/core/server/mocks';
import { eventLoggerMock } from '../../../event_log/server/mocks';
import { ActionTypeDisabledError } from './errors';
@ -56,7 +56,7 @@ const services = {
savedObjectsClient: savedObjectsClientMock.create(),
};
const actionExecutorInitializerParams = {
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
getServices: jest.fn().mockReturnValue(services),
actionTypeRegistry,
getScopedSavedObjectsClient: () => savedObjectsClientMock.create(),
@ -67,7 +67,7 @@ const actionExecutorInitializerParams = {
const taskRunnerFactoryInitializerParams = {
spaceIdToNamespace,
actionTypeRegistry,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
encryptedSavedObjectsClient: mockedEncryptedSavedObjectsClient,
getBasePath: jest.fn().mockReturnValue(undefined),
getScopedSavedObjectsClient: jest.fn().mockReturnValue(services.savedObjectsClient),

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { loggingServiceMock } from '../../../../../../src/core/server/mocks';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import { getAlertType } from './alert_type';
import { Params } from './alert_type_params';
@ -13,7 +13,7 @@ describe('alertType', () => {
indexThreshold: {
timeSeriesQuery: jest.fn(),
},
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
};
const alertType = getAlertType(service);

View file

@ -6,7 +6,7 @@
// test error conditions of calling timeSeriesQuery - postive results tested in FT
import { loggingServiceMock } from '../../../../../../../src/core/server/mocks';
import { loggingSystemMock } from '../../../../../../../src/core/server/mocks';
import { coreMock } from '../../../../../../../src/core/server/mocks';
import { AlertingBuiltinsPlugin } from '../../../plugin';
import { TimeSeriesQueryParameters, TimeSeriesResult, TimeSeriesQuery } from './time_series_query';
@ -44,7 +44,7 @@ describe('timeSeriesQuery', () => {
mockCallCluster.mockReset();
params = {
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
callCluster: mockCallCluster,
query: DefaultQueryParams,
};

View file

@ -6,7 +6,7 @@
import uuid from 'uuid';
import { schema } from '@kbn/config-schema';
import { AlertsClient, CreateOptions } from './alerts_client';
import { savedObjectsClientMock, loggingServiceMock } from '../../../../src/core/server/mocks';
import { savedObjectsClientMock, loggingSystemMock } from '../../../../src/core/server/mocks';
import { taskManagerMock } from '../../task_manager/server/task_manager.mock';
import { alertTypeRegistryMock } from './alert_type_registry.mock';
import { TaskStatus } from '../../task_manager/server';
@ -29,7 +29,7 @@ const alertsClientParams = {
getUserName: jest.fn(),
createAPIKey: jest.fn(),
invalidateAPIKey: jest.fn(),
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
encryptedSavedObjectsClient: encryptedSavedObjects,
getActionsClient: jest.fn(),
};

View file

@ -9,7 +9,7 @@ import { AlertsClientFactory, AlertsClientFactoryOpts } from './alerts_client_fa
import { alertTypeRegistryMock } from './alert_type_registry.mock';
import { taskManagerMock } from '../../task_manager/server/task_manager.mock';
import { KibanaRequest } from '../../../../src/core/server';
import { loggingServiceMock, savedObjectsClientMock } from '../../../../src/core/server/mocks';
import { loggingSystemMock, savedObjectsClientMock } from '../../../../src/core/server/mocks';
import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks';
import { AuthenticatedUser } from '../../../plugins/security/common/model';
import { securityMock } from '../../security/server/mocks';
@ -20,7 +20,7 @@ jest.mock('./alerts_client');
const savedObjectsClient = savedObjectsClientMock.create();
const securityPluginSetup = securityMock.createSetup();
const alertsClientFactoryParams: jest.Mocked<AlertsClientFactoryOpts> = {
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
taskManager: taskManagerMock.start(),
alertTypeRegistry: alertTypeRegistryMock.create(),
getSpaceId: jest.fn(),

View file

@ -6,7 +6,7 @@
import { AlertType } from '../types';
import { createExecutionHandler } from './create_execution_handler';
import { loggingServiceMock } from '../../../../../src/core/server/mocks';
import { loggingSystemMock } from '../../../../../src/core/server/mocks';
import { actionsMock, actionsClientMock } from '../../../actions/server/mocks';
import { eventLoggerMock } from '../../../event_log/server/event_logger.mock';
import { KibanaRequest } from 'kibana/server';
@ -34,7 +34,7 @@ const createExecutionHandlerParams = {
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
getBasePath: jest.fn().mockReturnValue(undefined),
alertType,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
eventLogger: eventLoggerMock.create(),
actions: [
{

View file

@ -11,7 +11,7 @@ import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server';
import { TaskRunnerContext } from './task_runner_factory';
import { TaskRunner } from './task_runner';
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
import { loggingServiceMock } from '../../../../../src/core/server/mocks';
import { loggingSystemMock } from '../../../../../src/core/server/mocks';
import { PluginStartContract as ActionsPluginStart } from '../../../actions/server';
import { actionsMock, actionsClientMock } from '../../../actions/server/mocks';
import { alertsMock } from '../mocks';
@ -66,7 +66,7 @@ describe('Task Runner', () => {
getServices: jest.fn().mockReturnValue(services),
actionsPlugin: actionsMock.createStart(),
encryptedSavedObjectsClient,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
getBasePath: jest.fn().mockReturnValue(undefined),
eventLogger: eventLoggerMock.create(),

View file

@ -8,7 +8,7 @@ import sinon from 'sinon';
import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server';
import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory';
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
import { loggingServiceMock } from '../../../../../src/core/server/mocks';
import { loggingSystemMock } from '../../../../../src/core/server/mocks';
import { actionsMock } from '../../../actions/server/mocks';
import { alertsMock } from '../mocks';
import { eventLoggerMock } from '../../../event_log/server/event_logger.mock';
@ -57,7 +57,7 @@ describe('Task Runner Factory', () => {
getServices: jest.fn().mockReturnValue(services),
actionsPlugin: actionsMock.createStart(),
encryptedSavedObjectsClient: encryptedSavedObjectsPlugin.getClient(),
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
getBasePath: jest.fn().mockReturnValue(undefined),
eventLogger: eventLoggerMock.create(),

View file

@ -9,7 +9,7 @@ import {
savedObjectsClientMock,
httpServiceMock,
httpServerMock,
loggingServiceMock,
loggingSystemMock,
} from 'src/core/server/mocks';
import { CUSTOM_ELEMENT_TYPE } from '../../../common/lib/constants';
import { initializeCreateCustomElementRoute } from './create';
@ -41,7 +41,7 @@ describe('POST custom element', () => {
const router = httpService.createRouter();
initializeCreateCustomElementRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.post.mock.calls[0][1];

View file

@ -11,7 +11,7 @@ import {
savedObjectsClientMock,
httpServiceMock,
httpServerMock,
loggingServiceMock,
loggingSystemMock,
} from 'src/core/server/mocks';
const mockRouteContext = ({
@ -30,7 +30,7 @@ describe('DELETE custom element', () => {
const router = httpService.createRouter();
initializeDeleteCustomElementRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.delete.mock.calls[0][1];

View file

@ -10,7 +10,7 @@ import {
savedObjectsClientMock,
httpServiceMock,
httpServerMock,
loggingServiceMock,
loggingSystemMock,
} from 'src/core/server/mocks';
const mockRouteContext = ({
@ -29,7 +29,7 @@ describe('Find custom element', () => {
const router = httpService.createRouter();
initializeFindCustomElementsRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.get.mock.calls[0][1];

View file

@ -11,7 +11,7 @@ import {
savedObjectsClientMock,
httpServiceMock,
httpServerMock,
loggingServiceMock,
loggingSystemMock,
} from 'src/core/server/mocks';
const mockRouteContext = ({
@ -30,7 +30,7 @@ describe('GET custom element', () => {
const router = httpService.createRouter();
initializeGetCustomElementRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.get.mock.calls[0][1];

View file

@ -13,7 +13,7 @@ import {
savedObjectsClientMock,
httpServiceMock,
httpServerMock,
loggingServiceMock,
loggingSystemMock,
} from 'src/core/server/mocks';
import { okResponse } from '../ok_response';
@ -55,7 +55,7 @@ describe('PUT custom element', () => {
const router = httpService.createRouter();
initializeUpdateCustomElementRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.put.mock.calls[0][1];

View file

@ -9,7 +9,7 @@ import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from 'sr
import {
httpServiceMock,
httpServerMock,
loggingServiceMock,
loggingSystemMock,
elasticsearchServiceMock,
} from 'src/core/server/mocks';
@ -29,7 +29,7 @@ describe('Retrieve ES Fields', () => {
const router = httpService.createRouter();
initializeESFieldsRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.get.mock.calls[0][1];

View file

@ -8,7 +8,7 @@ jest.mock('fs');
import fs from 'fs';
import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from 'src/core/server';
import { httpServiceMock, httpServerMock, loggingServiceMock } from 'src/core/server/mocks';
import { httpServiceMock, httpServerMock, loggingSystemMock } from 'src/core/server/mocks';
import { initializeDownloadShareableWorkpadRoute } from './download';
const mockRouteContext = {} as RequestHandlerContext;
@ -23,7 +23,7 @@ describe('Download Canvas shareables runtime', () => {
const router = httpService.createRouter();
initializeDownloadShareableWorkpadRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.get.mock.calls[0][1];

View file

@ -8,7 +8,7 @@ jest.mock('archiver');
const archiver = require('archiver') as jest.Mock;
import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from 'src/core/server';
import { httpServiceMock, httpServerMock, loggingServiceMock } from 'src/core/server/mocks';
import { httpServiceMock, httpServerMock, loggingSystemMock } from 'src/core/server/mocks';
import { initializeZipShareableWorkpadRoute } from './zip';
import { API_ROUTE_SHAREABLE_ZIP } from '../../../common/lib';
import {
@ -29,7 +29,7 @@ describe('Zips Canvas shareables runtime together with workpad', () => {
const router = httpService.createRouter();
initializeZipShareableWorkpadRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.post.mock.calls[0][1];

View file

@ -9,7 +9,7 @@ import {
savedObjectsClientMock,
httpServiceMock,
httpServerMock,
loggingServiceMock,
loggingSystemMock,
} from 'src/core/server/mocks';
import { CANVAS_TYPE } from '../../../common/lib/constants';
import { initializeCreateWorkpadRoute } from './create';
@ -41,7 +41,7 @@ describe('POST workpad', () => {
const router = httpService.createRouter();
initializeCreateWorkpadRoute({
router,
logger: loggingServiceMock.create().get(),
logger: loggingSystemMock.create().get(),
});
routeHandler = router.post.mock.calls[0][1];

Some files were not shown because too many files have changed in this diff Show more