diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index c942bddc9fd57..fa0edd8faadd7 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -56,6 +56,7 @@ - [On the server side](#on-the-server-side) - [On the client side](#on-the-client-side) - [Updates an application navlink at runtime](#updates-an-app-navlink-at-runtime) + - [Logging config migration](#logging-config-migration) Make no mistake, it is going to take a lot of work to move certain plugins to the new platform. Our target is to migrate the entire repo over to the new platform throughout 7.x and to remove the legacy plugin system no later than 8.0, and this is only possible if teams start on the effort now. @@ -1655,4 +1656,7 @@ export class MyPlugin implements Plugin { tooltip: 'Application disabled', }) } -``` \ No newline at end of file +``` + +### Logging config migration +[Read](./server/logging/README.md#logging-config-migration) \ No newline at end of file diff --git a/src/core/server/logging/README.md b/src/core/server/logging/README.md index 65fe64b045801..3fbec7a45148d 100644 --- a/src/core/server/logging/README.md +++ b/src/core/server/logging/README.md @@ -1,4 +1,12 @@ # Logging +- [Loggers, Appenders and Layouts](#loggers-appenders-and-layouts) +- [Logger hierarchy](#logger-hierarchy) +- [Log level](#log-level) +- [Layouts](#layouts) + - [Pattern layout](#pattern-layout) + - [JSON layout](#json-layout) +- [Configuration](#configuration) +- [Usage](#usage) The way logging works in Kibana is inspired by `log4j 2` logging framework used by [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html#logging). The main idea is to have consistent logging behaviour (configuration, log format etc.) across the entire Elastic Stack @@ -52,12 +60,68 @@ custom appenders, so one should always make the choice explicitly. There are two types of layout supported at the moment: `pattern` and `json`. -With `pattern` layout it's possible to define a string pattern with special placeholders wrapped into curly braces that +### Pattern layout +With `pattern` layout it's possible to define a string pattern with special placeholders `%conversion_pattern` (see the table below) that will be replaced with data from the actual log message. By default the following pattern is used: -`[{timestamp}][{level}][{context}] {message}`. Also `highlight` option can be enabled for `pattern` layout so that +`[%date][%level][%logger]%meta %message`. Also `highlight` option can be enabled for `pattern` layout so that some parts of the log message are highlighted with different colors that may be quite handy if log messages are forwarded to the terminal with color support. +`pattern` layout uses a sub-set of [log4j2 pattern syntax](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout) +and **doesn't implement** all `log4j2` capabilities. The conversions that are provided out of the box are: +#### level +Outputs the [level](#log-level) of the logging event. +Example of `%level` output: +```bash +TRACE +DEBUG +INFO +``` + +##### logger +Outputs the name of the logger that published the logging event. +Example of `%logger` output: +```bash +server +server.http +server.http.Kibana +``` + +#### message +Outputs the application supplied message associated with the logging event. + +#### meta +Outputs the entries of `meta` object data in **json** format, if one is present in the event. +Example of `%meta` output: +```bash +// Meta{from: 'v7', to: 'v8'} +'{"from":"v7","to":"v8"}' +// Meta empty object +'{}' +// no Meta provided +'' +``` + +##### date +Outputs the date of the logging event. The date conversion specifier may be followed by a set of braces containing a name of predefined date format and canonical timezone name. +Timezone name is expected to be one from [TZ database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) +Example of `%date` output: + +| Conversion pattern | Example | +| ---------------------------------------- | ---------------------------------------------------------------- | +| `%date` | `2012-02-01T14:30:22.011Z` uses `ISO8601` format by default | +| `%date{ISO8601}` | `2012-02-01T14:30:22.011Z` | +| `%date{ISO8601_TZ}` | `2012-02-01T09:30:22.011-05:00` `ISO8601` with timezone | +| `%date{ISO8601_TZ}{America/Los_Angeles}` | `2012-02-01T06:30:22.011-08:00` | +| `%date{ABSOLUTE}` | `09:30:22.011` | +| `%date{ABSOLUTE}{America/Los_Angeles}` | `06:30:22.011` | +| `%date{UNIX}` | `1328106622` | +| `%date{UNIX_MILLIS}` | `1328106622011` | + +#### pid +Outputs the process ID. + +### JSON layout With `json` layout log messages will be formatted as JSON strings that include timestamp, log level, context, message text and any other metadata that may be associated with the log message itself. @@ -88,7 +152,7 @@ logging: kind: console layout: kind: pattern - pattern: [{timestamp}][{level}] {message} + pattern: "[%date][%level] %message" json-file-appender: kind: file path: /var/log/kibana-json.log @@ -179,3 +243,81 @@ The log will be less verbose with `warn` level for the `server` context: [2017-07-25T18:54:41.639Z][ERROR][server] Message with `error` log level. [2017-07-25T18:54:41.639Z][FATAL][server] Message with `fatal` log level. ``` + +### Logging config migration +Compatibility with the legacy logging system is assured until the end of the `v7` version. +All log messages handled by `root` context are forwarded to the legacy logging service. If you re-write +root appenders, make sure that it contains `default` appender to provide backward compatibility. +**Note**: If you define an appender for a context, the log messages aren't handled by the +`root` context anymore and not forwarded to the legacy logging service. + +#### logging.dest +By default logs in *stdout*. With new Kibana logging you can use pre-existing `console` appender or +define a custom one. +```yaml +logging: + loggers: + - context: your-plugin + appenders: [console] +``` +Logs in a *file* if given file path. You should define a custom appender with `kind: file` +```yaml + +logging: + appenders: + file: + kind: file + path: /var/log/kibana.log + layout: + kind: pattern + loggers: + - context: your-plugin + appenders: [file] +``` +#### logging.json +Defines the format of log output. Logs in JSON if `true`. With new logging config you can adjust +the output format with [layouts](#layouts). + +#### logging.quiet +Suppresses all logging output other than error messages. With new logging, config can be achieved +with adjusting minimum required [logging level](#log-level) +```yaml + loggers: + - context: my-plugin + appenders: [console] + level: error +# or for all output +logging.root.level: error +``` + +#### logging.silent: +Suppresses all logging output. +```yaml +logging.root.level: off +``` + +#### logging.verbose: +Logs all events +```yaml +logging.root.level: all +``` + +#### logging.timezone +Set to the canonical timezone id to log events using that timezone. New logging config allows +to [specify timezone](#date) for `layout: pattern`. +```yaml +logging: + appenders: + custom-console: + kind: console + layout: + kind: pattern + highlight: true + pattern: "[%level] [%date{ISO8601_TZ}{America/Los_Angeles}][%logger] %message" +``` + +#### logging.events +Define a custom logger for a specific context. + +#### logging.filter +TBD diff --git a/src/core/server/logging/integration_tests/logging.test.ts b/src/core/server/logging/integration_tests/logging.test.ts index 7142f91300f12..b88f5ba2c2b60 100644 --- a/src/core/server/logging/integration_tests/logging.test.ts +++ b/src/core/server/logging/integration_tests/logging.test.ts @@ -29,7 +29,7 @@ function createRoot() { layout: { highlight: false, kind: 'pattern', - pattern: '{level}|{context}|{message}', + pattern: '%level|%logger|%message', }, }, }, diff --git a/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap b/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap index da57023c94286..14c071b40ad7a 100644 --- a/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap +++ b/src/core/server/logging/layouts/__snapshots__/json_layout.test.ts.snap @@ -1,9 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`\`format()\` correctly formats error record with meta-data 1`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"context\\":\\"context-with-meta\\",\\"level\\":\\"DEBUG\\",\\"message\\":\\"message-with-meta\\",\\"meta\\":{\\"from\\":\\"v7\\",\\"to\\":\\"v8\\"},\\"pid\\":5355}"`; - -exports[`\`format()\` correctly formats record with meta-data 1`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"context\\":\\"context-with-meta\\",\\"level\\":\\"DEBUG\\",\\"message\\":\\"message-with-meta\\",\\"meta\\":{\\"from\\":\\"v7\\",\\"to\\":\\"v8\\"},\\"pid\\":5355}"`; - exports[`\`format()\` correctly formats record. 1`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"context\\":\\"context-1\\",\\"error\\":{\\"message\\":\\"Some error message\\",\\"name\\":\\"Some error name\\",\\"stack\\":\\"Some error stack\\"},\\"level\\":\\"FATAL\\",\\"message\\":\\"message-1\\",\\"pid\\":5355}"`; exports[`\`format()\` correctly formats record. 2`] = `"{\\"@timestamp\\":\\"2012-02-01T09:30:22.011-05:00\\",\\"context\\":\\"context-2\\",\\"level\\":\\"ERROR\\",\\"message\\":\\"message-2\\",\\"pid\\":5355}"`; diff --git a/src/core/server/logging/layouts/conversions/timestamp.ts b/src/core/server/logging/layouts/conversions/date.ts similarity index 83% rename from src/core/server/logging/layouts/conversions/timestamp.ts rename to src/core/server/logging/layouts/conversions/date.ts index 6db6fc6eeb6bf..d3ed54fb98240 100644 --- a/src/core/server/logging/layouts/conversions/timestamp.ts +++ b/src/core/server/logging/layouts/conversions/date.ts @@ -22,7 +22,7 @@ import { last } from 'lodash'; import { Conversion } from './type'; import { LogRecord } from '../../log_record'; -const timestampRegExp = /{timestamp({(?[^}]+)})?({(?[^}]+)})?}/gi; +const dateRegExp = /%date({(?[^}]+)})?({(?[^}]+)})?/g; const formats = { ISO8601: 'ISO8601', @@ -54,10 +54,11 @@ function formatDate(date: Date, dateFormat: string = formats.ISO8601, timezone?: } function validateDateFormat(input: string) { - if (Reflect.has(formats, input)) return; - throw new Error( - `Date format expected one of ${Reflect.ownKeys(formats).join(', ')}, but given: ${input}` - ); + if (!Reflect.has(formats, input)) { + throw new Error( + `Date format expected one of ${Reflect.ownKeys(formats).join(', ')}, but given: ${input}` + ); + } } function validateTimezone(timezone: string) { @@ -66,7 +67,7 @@ function validateTimezone(timezone: string) { } function validate(rawString: string) { - for (const matched of rawString.matchAll(timestampRegExp)) { + for (const matched of rawString.matchAll(dateRegExp)) { const { format, timezone } = matched.groups!; if (format) { @@ -78,9 +79,9 @@ function validate(rawString: string) { } } -export const TimestampConversion: Conversion = { - pattern: timestampRegExp, - formatter(record: LogRecord, highlight: boolean, ...matched: any[]) { +export const DateConversion: Conversion = { + pattern: dateRegExp, + convert(record: LogRecord, highlight: boolean, ...matched: any[]) { const groups: Record = last(matched); const { format, timezone } = groups; diff --git a/src/core/server/logging/layouts/conversions/index.ts b/src/core/server/logging/layouts/conversions/index.ts new file mode 100644 index 0000000000000..23e6aded6c6f7 --- /dev/null +++ b/src/core/server/logging/layouts/conversions/index.ts @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +export { Conversion } from './type'; + +export { LoggerConversion } from './logger'; +export { LevelConversion } from './level'; +export { MessageConversion } from './message'; +export { MetaConversion } from './meta'; +export { PidConversion } from './pid'; +export { DateConversion } from './date'; diff --git a/src/core/server/logging/layouts/conversions/level.ts b/src/core/server/logging/layouts/conversions/level.ts index 02ed86dd2c24f..58b271140eff5 100644 --- a/src/core/server/logging/layouts/conversions/level.ts +++ b/src/core/server/logging/layouts/conversions/level.ts @@ -32,8 +32,8 @@ const LEVEL_COLORS = new Map([ ]); export const LevelConversion: Conversion = { - pattern: /{level}/gi, - formatter(record: LogRecord, highlight: boolean) { + pattern: /%level/g, + convert(record: LogRecord, highlight: boolean) { let message = record.level.id.toUpperCase().padEnd(5); if (highlight && LEVEL_COLORS.has(record.level)) { const color = LEVEL_COLORS.get(record.level)!; diff --git a/src/core/server/logging/layouts/conversions/context.ts b/src/core/server/logging/layouts/conversions/logger.ts similarity index 89% rename from src/core/server/logging/layouts/conversions/context.ts rename to src/core/server/logging/layouts/conversions/logger.ts index d1fa9ca84f555..debb1737ab95a 100644 --- a/src/core/server/logging/layouts/conversions/context.ts +++ b/src/core/server/logging/layouts/conversions/logger.ts @@ -22,9 +22,9 @@ import chalk from 'chalk'; import { Conversion } from './type'; import { LogRecord } from '../../log_record'; -export const ContextConversion: Conversion = { - pattern: /{context}/gi, - formatter(record: LogRecord, highlight: boolean) { +export const LoggerConversion: Conversion = { + pattern: /%logger/g, + convert(record: LogRecord, highlight: boolean) { let message = record.context; if (highlight) { message = chalk.magenta(message); diff --git a/src/core/server/logging/layouts/conversions/message.ts b/src/core/server/logging/layouts/conversions/message.ts index b95a89b12b780..f8c5e68ada4fb 100644 --- a/src/core/server/logging/layouts/conversions/message.ts +++ b/src/core/server/logging/layouts/conversions/message.ts @@ -21,8 +21,8 @@ import { Conversion } from './type'; import { LogRecord } from '../../log_record'; export const MessageConversion: Conversion = { - pattern: /{message}/gi, - formatter(record: LogRecord) { + pattern: /%message/g, + convert(record: LogRecord) { // Error stack is much more useful than just the message. return (record.error && record.error.stack) || record.message; }, diff --git a/src/core/server/logging/layouts/conversions/meta.ts b/src/core/server/logging/layouts/conversions/meta.ts index f6d4557e0db53..ee8c207389fbe 100644 --- a/src/core/server/logging/layouts/conversions/meta.ts +++ b/src/core/server/logging/layouts/conversions/meta.ts @@ -20,8 +20,8 @@ import { Conversion } from './type'; import { LogRecord } from '../../log_record'; export const MetaConversion: Conversion = { - pattern: /{meta}/gi, - formatter(record: LogRecord) { - return record.meta ? `[${JSON.stringify(record.meta)}]` : ''; + pattern: /%meta/g, + convert(record: LogRecord) { + return record.meta ? `${JSON.stringify(record.meta)}` : ''; }, }; diff --git a/src/core/server/logging/layouts/conversions/pid.ts b/src/core/server/logging/layouts/conversions/pid.ts index 0fcdd93fcda0c..37d34a4f1cf8b 100644 --- a/src/core/server/logging/layouts/conversions/pid.ts +++ b/src/core/server/logging/layouts/conversions/pid.ts @@ -21,8 +21,8 @@ import { Conversion } from './type'; import { LogRecord } from '../../log_record'; export const PidConversion: Conversion = { - pattern: /{pid}/gi, - formatter(record: LogRecord) { + pattern: /%pid/g, + convert(record: LogRecord) { return String(record.pid); }, }; diff --git a/src/core/server/logging/layouts/conversions/type.ts b/src/core/server/logging/layouts/conversions/type.ts index 34a6475138814..a57a1f954e53a 100644 --- a/src/core/server/logging/layouts/conversions/type.ts +++ b/src/core/server/logging/layouts/conversions/type.ts @@ -20,6 +20,6 @@ import { LogRecord } from 'kibana/server'; export interface Conversion { pattern: RegExp; - formatter: (record: LogRecord, highlight: boolean) => string; + convert: (record: LogRecord, highlight: boolean) => string; validate?: (input: string) => void; } diff --git a/src/core/server/logging/layouts/json_layout.test.ts b/src/core/server/logging/layouts/json_layout.test.ts index ec8c44ec62a22..77e2876c143da 100644 --- a/src/core/server/logging/layouts/json_layout.test.ts +++ b/src/core/server/logging/layouts/json_layout.test.ts @@ -90,34 +90,68 @@ test('`format()` correctly formats record with meta-data', () => { const layout = new JsonLayout(); expect( - layout.format({ - context: 'context-with-meta', - level: LogLevel.Debug, - message: 'message-with-meta', - timestamp, - pid: 5355, - meta: { - from: 'v7', - to: 'v8', - }, - }) - ).toMatchSnapshot(); + JSON.parse( + layout.format({ + context: 'context-with-meta', + level: LogLevel.Debug, + message: 'message-with-meta', + timestamp, + pid: 5355, + meta: { + from: 'v7', + to: 'v8', + }, + }) + ) + ).toStrictEqual({ + '@timestamp': '2012-02-01T09:30:22.011-05:00', + context: 'context-with-meta', + level: 'DEBUG', + message: 'message-with-meta', + meta: { + from: 'v7', + to: 'v8', + }, + pid: 5355, + }); }); test('`format()` correctly formats error record with meta-data', () => { const layout = new JsonLayout(); expect( - layout.format({ - context: 'context-with-meta', - level: LogLevel.Debug, - message: 'message-with-meta', - timestamp, - pid: 5355, - meta: { - from: 'v7', - to: 'v8', - }, - }) - ).toMatchSnapshot(); + JSON.parse( + layout.format({ + context: 'error-with-meta', + level: LogLevel.Debug, + error: { + message: 'Some error message', + name: 'Some error name', + stack: 'Some error stack', + }, + message: 'Some error message', + timestamp, + pid: 5355, + meta: { + from: 'v7', + to: 'v8', + }, + }) + ) + ).toStrictEqual({ + '@timestamp': '2012-02-01T09:30:22.011-05:00', + context: 'error-with-meta', + level: 'DEBUG', + error: { + message: 'Some error message', + name: 'Some error name', + stack: 'Some error stack', + }, + message: 'Some error message', + meta: { + from: 'v7', + to: 'v8', + }, + pid: 5355, + }); }); diff --git a/src/core/server/logging/layouts/layouts.test.ts b/src/core/server/logging/layouts/layouts.test.ts index aa1c54c846bc6..b1fb836f40d5d 100644 --- a/src/core/server/logging/layouts/layouts.test.ts +++ b/src/core/server/logging/layouts/layouts.test.ts @@ -33,12 +33,12 @@ test('`configSchema` creates correct schema for `pattern` layout.', () => { const validConfig = { highlight: true, kind: 'pattern', - pattern: '{message}', + pattern: '%message', }; expect(layoutsSchema.validate(validConfig)).toEqual({ highlight: true, kind: 'pattern', - pattern: '{message}', + pattern: '%message', }); const wrongConfig2 = { kind: 'pattern', pattern: 1 }; @@ -56,7 +56,7 @@ test('`create()` creates correct layout.', () => { const patternLayout = Layouts.create({ highlight: false, kind: 'pattern', - pattern: '[{timestamp}][{level}][{context}] {message}', + pattern: '[%date][%level][%logger] %message', }); expect(patternLayout).toBeInstanceOf(PatternLayout); diff --git a/src/core/server/logging/layouts/pattern_layout.test.ts b/src/core/server/logging/layouts/pattern_layout.test.ts index 2d948ea59c6d1..cce55b147e0ed 100644 --- a/src/core/server/logging/layouts/pattern_layout.test.ts +++ b/src/core/server/logging/layouts/pattern_layout.test.ts @@ -88,12 +88,12 @@ test('`createConfigSchema()` creates correct schema.', () => { const validConfig = { highlight: true, kind: 'pattern', - pattern: '{message}', + pattern: '%message', }; expect(layoutSchema.validate(validConfig)).toEqual({ highlight: true, kind: 'pattern', - pattern: '{message}', + pattern: '%message', }); const wrongConfig1 = { kind: 'json' }; @@ -112,7 +112,7 @@ test('`format()` correctly formats record with full pattern.', () => { }); test('`format()` correctly formats record with custom pattern.', () => { - const layout = new PatternLayout('mock-{message}-{context}-{message}'); + const layout = new PatternLayout('mock-%message-%logger-%message'); for (const record of records) { expect(layout.format(record)).toMatchSnapshot(); @@ -134,7 +134,7 @@ test('`format()` correctly formats record with meta data.', () => { to: 'v8', }, }) - ).toBe('[2012-02-01T14:30:22.011Z][DEBUG][context-meta][{"from":"v7","to":"v8"}] message-meta'); + ).toBe('[2012-02-01T14:30:22.011Z][DEBUG][context-meta]{"from":"v7","to":"v8"} message-meta'); expect( layout.format({ @@ -145,7 +145,7 @@ test('`format()` correctly formats record with meta data.', () => { pid: 5355, meta: {}, }) - ).toBe('[2012-02-01T14:30:22.011Z][DEBUG][context-meta][{}] message-meta'); + ).toBe('[2012-02-01T14:30:22.011Z][DEBUG][context-meta]{} message-meta'); expect( layout.format({ @@ -167,7 +167,7 @@ test('`format()` correctly formats record with highlighting.', () => { }); test('allows specifying the PID in custom pattern', () => { - const layout = new PatternLayout('{pid}-{context}-{message}'); + const layout = new PatternLayout('%pid-%logger-%message'); for (const record of records) { expect(layout.format(record)).toMatchSnapshot(); @@ -175,7 +175,7 @@ test('allows specifying the PID in custom pattern', () => { }); test('`format()` allows specifying pattern with meta.', () => { - const layout = new PatternLayout('{context}-{meta}-{message}'); + const layout = new PatternLayout('%logger-%meta-%message'); const record = { context: 'context', level: LogLevel.Debug, @@ -187,7 +187,7 @@ test('`format()` allows specifying pattern with meta.', () => { to: 'v8', }, }; - expect(layout.format(record)).toBe('context-[{"from":"v7","to":"v8"}]-message'); + expect(layout.format(record)).toBe('context-{"from":"v7","to":"v8"}-message'); }); describe('format', () => { @@ -207,31 +207,31 @@ describe('format', () => { describe('supports specifying a predefined format', () => { it('ISO8601', () => { - const layout = new PatternLayout('[{timestamp{ISO8601}}][{context}]'); + const layout = new PatternLayout('[%date{ISO8601}][%logger]'); expect(layout.format(record)).toBe('[2012-02-01T14:30:22.011Z][context]'); }); it('ISO8601_TZ', () => { - const layout = new PatternLayout('[{timestamp{ISO8601_TZ}}][{context}]'); + const layout = new PatternLayout('[%date{ISO8601_TZ}][%logger]'); expect(layout.format(record)).toBe('[2012-02-01T09:30:22.011-05:00][context]'); }); it('ABSOLUTE', () => { - const layout = new PatternLayout('[{timestamp{ABSOLUTE}}][{context}]'); + const layout = new PatternLayout('[%date{ABSOLUTE}][%logger]'); expect(layout.format(record)).toBe('[09:30:22.011][context]'); }); it('UNIX', () => { - const layout = new PatternLayout('[{timestamp{UNIX}}][{context}]'); + const layout = new PatternLayout('[%date{UNIX}][%logger]'); expect(layout.format(record)).toBe('[1328106622][context]'); }); it('UNIX_MILLIS', () => { - const layout = new PatternLayout('[{timestamp{UNIX_MILLIS}}][{context}]'); + const layout = new PatternLayout('[%date{UNIX_MILLIS}][%logger]'); expect(layout.format(record)).toBe('[1328106622011][context]'); }); @@ -239,42 +239,38 @@ describe('format', () => { describe('supports specifying a predefined format and timezone', () => { it('ISO8601', () => { - const layout = new PatternLayout('[{timestamp{ISO8601}{America/Los_Angeles}}][{context}]'); + const layout = new PatternLayout('[%date{ISO8601}{America/Los_Angeles}][%logger]'); expect(layout.format(record)).toBe('[2012-02-01T14:30:22.011Z][context]'); }); it('ISO8601_TZ', () => { - const layout = new PatternLayout( - '[{timestamp{ISO8601_TZ}{America/Los_Angeles}}][{context}]' - ); + const layout = new PatternLayout('[%date{ISO8601_TZ}{America/Los_Angeles}][%logger]'); expect(layout.format(record)).toBe('[2012-02-01T06:30:22.011-08:00][context]'); }); it('ABSOLUTE', () => { - const layout = new PatternLayout('[{timestamp{ABSOLUTE}{America/Los_Angeles}}][{context}]'); + const layout = new PatternLayout('[%date{ABSOLUTE}{America/Los_Angeles}][%logger]'); expect(layout.format(record)).toBe('[06:30:22.011][context]'); }); it('UNIX', () => { - const layout = new PatternLayout('[{timestamp{UNIX}{America/Los_Angeles}}][{context}]'); + const layout = new PatternLayout('[%date{UNIX}{America/Los_Angeles}][%logger]'); expect(layout.format(record)).toBe('[1328106622][context]'); }); it('UNIX_MILLIS', () => { - const layout = new PatternLayout( - '[{timestamp{UNIX_MILLIS}{America/Los_Angeles}}][{context}]' - ); + const layout = new PatternLayout('[%date{UNIX_MILLIS}{America/Los_Angeles}][%logger]'); expect(layout.format(record)).toBe('[1328106622011][context]'); }); }); it('formats several conversions patterns correctly', () => { const layout = new PatternLayout( - '[{timestamp{ABSOLUTE}{America/Los_Angeles}}][{context}][{timestamp{UNIX}}]' + '[%date{ABSOLUTE}{America/Los_Angeles}][%logger][%date{UNIX}]' ); expect(layout.format(record)).toBe('[06:30:22.011][context][1328106622]'); @@ -284,45 +280,44 @@ describe('format', () => { describe('schema', () => { describe('pattern', () => { - describe('{timestamp}', () => { - it('does not fail when {timestamp} not present', () => { + describe('%date', () => { + it('does not fail when %date not present', () => { expect(patternSchema.validate('')).toBe(''); expect(patternSchema.validate('{pid}')).toBe('{pid}'); }); - it('does not fail on {timestamp} without params', () => { - expect(patternSchema.validate('{timestamp}')).toBe('{timestamp}'); - expect(patternSchema.validate('{timestamp}}')).toBe('{timestamp}}'); - expect(patternSchema.validate('{{timestamp}}')).toBe('{{timestamp}}'); + it('does not fail on %date without params', () => { + expect(patternSchema.validate('%date')).toBe('%date'); + expect(patternSchema.validate('%date')).toBe('%date'); + expect(patternSchema.validate('{%date}')).toBe('{%date}'); + expect(patternSchema.validate('%date%date')).toBe('%date%date'); }); - it('does not fail on {timestamp} with predefined date format', () => { - expect(patternSchema.validate('{timestamp{ISO8601}}')).toBe('{timestamp{ISO8601}}'); + it('does not fail on %date with predefined date format', () => { + expect(patternSchema.validate('%date{ISO8601}')).toBe('%date{ISO8601}'); }); - it('does not fail on {timestamp} with predefined date format and valid timezone', () => { - expect(patternSchema.validate('{timestamp{ISO8601_TZ}{Europe/Berlin}}')).toBe( - '{timestamp{ISO8601_TZ}{Europe/Berlin}}' + it('does not fail on %date with predefined date format and valid timezone', () => { + expect(patternSchema.validate('%date{ISO8601_TZ}{Europe/Berlin}')).toBe( + '%date{ISO8601_TZ}{Europe/Berlin}' ); }); - it('fails on {timestamp} with unknown date format', () => { - expect(() => - patternSchema.validate('{timestamp{HH:MM:SS}}') - ).toThrowErrorMatchingInlineSnapshot( + it('fails on %date with unknown date format', () => { + expect(() => patternSchema.validate('%date{HH:MM:SS}')).toThrowErrorMatchingInlineSnapshot( `"Date format expected one of ISO8601, ISO8601_TZ, ABSOLUTE, UNIX, UNIX_MILLIS, but given: HH:MM:SS"` ); }); - it('fails on {timestamp} with predefined date format and invalid timezone', () => { + it('fails on %date with predefined date format and invalid timezone', () => { expect(() => - patternSchema.validate('{timestamp{ISO8601_TZ}{Europe/Kibana}}') + patternSchema.validate('%date{ISO8601_TZ}{Europe/Kibana}') ).toThrowErrorMatchingInlineSnapshot(`"Unknown timezone: Europe/Kibana"`); }); - it('validates several {timestamp} in pattern', () => { + it('validates several %date in pattern', () => { expect(() => - patternSchema.validate('{timestamp{ISO8601_TZ}{Europe/Berlin}}{message}{timestamp{HH}}') + patternSchema.validate('%date{ISO8601_TZ}{Europe/Berlin}%message%date{HH}') ).toThrowErrorMatchingInlineSnapshot( `"Date format expected one of ISO8601, ISO8601_TZ, ABSOLUTE, UNIX, UNIX_MILLIS, but given: HH"` ); diff --git a/src/core/server/logging/layouts/pattern_layout.ts b/src/core/server/logging/layouts/pattern_layout.ts index 0a2a25a135069..9490db149cc0f 100644 --- a/src/core/server/logging/layouts/pattern_layout.ts +++ b/src/core/server/logging/layouts/pattern_layout.ts @@ -21,23 +21,24 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { LogRecord } from '../log_record'; import { Layout } from './layouts'; - -import { Conversion } from './conversions/type'; -import { ContextConversion } from './conversions/context'; -import { LevelConversion } from './conversions/level'; -import { MetaConversion } from './conversions/meta'; -import { MessageConversion } from './conversions/message'; -import { PidConversion } from './conversions/pid'; -import { TimestampConversion } from './conversions/timestamp'; +import { + Conversion, + LoggerConversion, + LevelConversion, + MetaConversion, + MessageConversion, + PidConversion, + DateConversion, +} from './conversions'; /** * Default pattern used by PatternLayout if it's not overridden in the configuration. */ -const DEFAULT_PATTERN = `[{timestamp}][{level}][{context}]{meta} {message}`; +const DEFAULT_PATTERN = `[%date][%level][%logger]%meta %message`; export const patternSchema = schema.string({ validate: string => { - TimestampConversion.validate!(string); + DateConversion.validate!(string); }, }); @@ -48,12 +49,12 @@ const patternLayoutSchema = schema.object({ }); const conversions: Conversion[] = [ - ContextConversion, + LoggerConversion, MessageConversion, LevelConversion, MetaConversion, PidConversion, - TimestampConversion, + DateConversion, ]; /** @internal */ @@ -77,7 +78,7 @@ export class PatternLayout implements Layout { for (const conversion of conversions) { recordString = recordString.replace( conversion.pattern, - conversion.formatter.bind(null, record, this.highlight) + conversion.convert.bind(null, record, this.highlight) ); } diff --git a/src/core/server/logging/logging_config.test.ts b/src/core/server/logging/logging_config.test.ts index b3631abb9ff00..75f571d34c25c 100644 --- a/src/core/server/logging/logging_config.test.ts +++ b/src/core/server/logging/logging_config.test.ts @@ -59,7 +59,7 @@ test('`getLoggerContext()` returns correct joined context name.', () => { test('correctly fills in default config.', () => { const configValue = new LoggingConfig(config.schema.validate({})); - expect(configValue.appenders.size).toBe(3); + expect(configValue.appenders.size).toBe(2); expect(configValue.appenders.get('default')).toEqual({ kind: 'console', @@ -69,10 +69,6 @@ test('correctly fills in default config.', () => { kind: 'console', layout: { kind: 'pattern', highlight: true }, }); - expect(configValue.appenders.get('file')).toEqual({ - kind: 'file', - layout: { kind: 'pattern', highlight: false }, - }); }); test('correctly fills in custom `appenders` config.', () => { @@ -83,16 +79,11 @@ test('correctly fills in custom `appenders` config.', () => { kind: 'console', layout: { kind: 'pattern' }, }, - file: { - kind: 'file', - layout: { kind: 'pattern' }, - path: 'path', - }, }, }) ); - expect(configValue.appenders.size).toBe(3); + expect(configValue.appenders.size).toBe(2); expect(configValue.appenders.get('default')).toEqual({ kind: 'console', @@ -103,12 +94,6 @@ test('correctly fills in custom `appenders` config.', () => { kind: 'console', layout: { kind: 'pattern' }, }); - - expect(configValue.appenders.get('file')).toEqual({ - kind: 'file', - layout: { kind: 'pattern' }, - path: 'path', - }); }); test('correctly fills in default `loggers` config.', () => { diff --git a/src/core/server/logging/logging_config.ts b/src/core/server/logging/logging_config.ts index f1fbf787737b4..8f80be7d79cb1 100644 --- a/src/core/server/logging/logging_config.ts +++ b/src/core/server/logging/logging_config.ts @@ -140,13 +140,6 @@ export class LoggingConfig { layout: { kind: 'pattern', highlight: true }, } as AppenderConfigType, ], - [ - 'file', - { - kind: 'file', - layout: { kind: 'pattern', highlight: false }, - } as AppenderConfigType, - ], ]); /**