1# Logging
2
3- [Loggers, Appenders and Layouts](#loggers-appenders-and-layouts)
4- [Logger hierarchy](#logger-hierarchy)
5- [Log level](#log-level)
6- [Layouts](#layouts)
7  - [Pattern layout](#pattern-layout)
8  - [JSON layout](#json-layout)
9- [Configuration](#configuration)
10- [Usage](#usage)
11- [Logging config migration](#logging-config-migration)
12- [Log record format changes](#log-record-format-changes)
13
14The way logging works in OpenSearch Dashboards is inspired by `log4j 2` logging framework used by [OpenSearch](https://www.opensearch.org/guide/en/elasticsearch/reference/current/settings.html#logging).
15The main idea is to have consistent logging behaviour (configuration, log format etc.) across the entire OpenSearch Stack
16where possible.
17
18## Loggers, Appenders and Layouts
19
20OpenSearch Dashboards logging system has three main components: _loggers_, _appenders_ and _layouts_. These components allow us to log
21messages according to message type and level, and to control how these messages are formatted and where the final logs
22will be displayed or stored.
23
24**Loggers** define what logging settings should be applied at the particular context.
25
26**Appenders** define where log messages are displayed (eg. stdout or console) and stored (eg. file on the disk).
27
28**Layouts** define how log messages are formatted and what type of information they include.
29
30## Logger hierarchy
31
32Every logger has its unique name or context that follows hierarchical naming rule. The logger is considered to be an
33ancestor of another logger if its name followed by a `.` is a prefix of the descendant logger name. For example logger
34with `a.b` context is an ancestor of logger with `a.b.c` context. All top-level loggers are descendants of special
35logger with `root` context that resides at the top of the logger hierarchy. This logger always exists and
36fully configured.
37
38Developer can configure _log level_ and _appenders_ that should be used within particular context. If logger configuration
39specifies only _log level_ then _appenders_ configuration will be inherited from the ancestor logger.
40
41**Note:** in the current implementation log messages are only forwarded to appenders configured for a particular logger
42context or to appenders of the closest ancestor if current logger doesn't have any appenders configured. That means that
43we **don't support** so called _appender additivity_ when log messages are forwarded to _every_ distinct appender within
44ancestor chain including `root`.
45
46## Log level
47
48Currently we support the following log levels: _all_, _fatal_, _error_, _warn_, _info_, _debug_, _trace_, _off_.
49Levels are ordered, so _all_ > _fatal_ > _error_ > _warn_ > _info_ > _debug_ > _trace_ > _off_.
50A log record is being logged by the logger if its level is higher than or equal to the level of its logger. Otherwise,
51the log record is ignored.
52
53The _all_ and _off_ levels can be used only in configuration and are just handy shortcuts that allow developer to log every
54log record or disable logging entirely for the specific context.
55
56## Layouts
57
58Every appender should know exactly how to format log messages before they are written to the console or file on the disk.
59This behaviour is controlled by the layouts and configured through `appender.layout` configuration property for every
60custom appender (see examples in [Configuration](#configuration)). Currently we don't define any default layout for the
61custom appenders, so one should always make the choice explicitly.
62
63There are two types of layout supported at the moment: `pattern` and `json`.
64
65### Pattern layout
66
67With `pattern` layout it's possible to define a string pattern with special placeholders `%conversion_pattern` (see the table below) that
68will be replaced with data from the actual log message. By default the following pattern is used:
69`[%date][%level][%logger]%meta %message`. Also `highlight` option can be enabled for `pattern` layout so that
70some parts of the log message are highlighted with different colors that may be quite handy if log messages are forwarded
71to the terminal with color support.
72`pattern` layout uses a sub-set of [log4j2 pattern syntax](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout)
73and **doesn't implement** all `log4j2` capabilities. The conversions that are provided out of the box are:
74
75#### level
76
77Outputs the [level](#log-level) of the logging event.
78Example of `%level` output:
79
80```bash
81TRACE
82DEBUG
83INFO
84```
85
86##### logger
87
88Outputs the name of the logger that published the logging event.
89Example of `%logger` output:
90
91```bash
92server
93server.http
94server.http.OpenSearchDashboards
95```
96
97#### message
98
99Outputs the application supplied message associated with the logging event.
100
101#### meta
102
103Outputs the entries of `meta` object data in **json** format, if one is present in the event.
104Example of `%meta` output:
105
106```bash
107// Meta{from: 'v7', to: 'v8'}
108'{"from":"v7","to":"v8"}'
109// Meta empty object
110'{}'
111// no Meta provided
112''
113```
114
115##### date
116
117Outputs 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.
118Timezone name is expected to be one from [TZ database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
119Example of `%date` output:
120
121| Conversion pattern                       | Example                                                     |
122| ---------------------------------------- | ----------------------------------------------------------- |
123| `%date`                                  | `2012-02-01T14:30:22.011Z` uses `ISO8601` format by default |
124| `%date{ISO8601}`                         | `2012-02-01T14:30:22.011Z`                                  |
125| `%date{ISO8601_TZ}`                      | `2012-02-01T09:30:22.011-05:00` `ISO8601` with timezone     |
126| `%date{ISO8601_TZ}{America/Los_Angeles}` | `2012-02-01T06:30:22.011-08:00`                             |
127| `%date{ABSOLUTE}`                        | `09:30:22.011`                                              |
128| `%date{ABSOLUTE}{America/Los_Angeles}`   | `06:30:22.011`                                              |
129| `%date{UNIX}`                            | `1328106622`                                                |
130| `%date{UNIX_MILLIS}`                     | `1328106622011`                                             |
131
132#### pid
133
134Outputs the process ID.
135
136### JSON layout
137
138With `json` layout log messages will be formatted as JSON strings that include timestamp, log level, context, message
139text and any other metadata that may be associated with the log message itself.
140
141## Configuration
142
143As any configuration in the platform, logging configuration is validated against the predefined schema and if there are
144any issues with it, OpenSearch Dashboards will fail to start with the detailed error message.
145
146Once the code acquired a logger instance it should not care about any runtime changes in the configuration that may
147happen: all changes will be applied to existing logger instances under the hood.
148
149Here is the configuration example that can be used to configure _loggers_, _appenders_ and _layouts_:
150
151```yaml
152logging:
153  appenders:
154    console:
155      kind: console
156      layout:
157        kind: pattern
158        highlight: true
159    file:
160      kind: file
161      path: /var/log/opensearch_dashboards.log
162      layout:
163        kind: pattern
164    custom:
165      kind: console
166      layout:
167        kind: pattern
168        pattern: '[%date][%level] %message'
169    json-file-appender:
170      kind: file
171      path: /var/log/opensearch-dashboards-json.log
172
173  root:
174    appenders: [console, file]
175    level: error
176
177  loggers:
178    - context: plugins
179      appenders: [custom]
180      level: warn
181    - context: plugins.myPlugin
182      level: info
183    - context: server
184      level: fatal
185    - context: optimize
186      appenders: [console]
187    - context: telemetry
188      level: all
189      appenders: [json-file-appender]
190```
191
192Here is what we get with the config above:
193
194| Context          |     Appenders      | Level |
195| ---------------- | :----------------: | ----: |
196| root             |   console, file    | error |
197| plugins          |       custom       |  warn |
198| plugins.myPlugin |       custom       |  info |
199| server           |   console, file    | fatal |
200| optimize         |      console       | error |
201| telemetry        | json-file-appender |   all |
202
203The `root` logger has a dedicated configuration node since this context is special and should always exist. By
204default `root` is configured with `info` level and `default` appender that is also always available. This is the
205configuration that all custom loggers will use unless they're re-configured explicitly.
206
207For example to see _all_ log messages that fall back on the `root` logger configuration, just add one line to the configuration:
208
209```yaml
210logging.root.level: all
211```
212
213Or disable logging entirely with `off`:
214
215```yaml
216logging.root.level: off
217```
218
219## Usage
220
221Usage is very straightforward, one should just get a logger for a specific context and use it to log messages with
222different log level.
223
224```typescript
225const logger = opensearchDashboards.logger.get('server');
226
227logger.trace('Message with `trace` log level.');
228logger.debug('Message with `debug` log level.');
229logger.info('Message with `info` log level.');
230logger.warn('Message with `warn` log level.');
231logger.error('Message with `error` log level.');
232logger.fatal('Message with `fatal` log level.');
233
234const loggerWithNestedContext = opensearchDashboards.logger.get('server', 'http');
235loggerWithNestedContext.trace('Message with `trace` log level.');
236loggerWithNestedContext.debug('Message with `debug` log level.');
237```
238
239And assuming logger for `server` context with `console` appender and `trace` level was used, console output will look like this:
240
241```bash
242[2017-07-25T18:54:41.639Z][TRACE][server] Message with `trace` log level.
243[2017-07-25T18:54:41.639Z][DEBUG][server] Message with `debug` log level.
244[2017-07-25T18:54:41.639Z][INFO ][server] Message with `info` log level.
245[2017-07-25T18:54:41.639Z][WARN ][server] Message with `warn` log level.
246[2017-07-25T18:54:41.639Z][ERROR][server] Message with `error` log level.
247[2017-07-25T18:54:41.639Z][FATAL][server] Message with `fatal` log level.
248
249[2017-07-25T18:54:41.639Z][TRACE][server.http] Message with `trace` log level.
250[2017-07-25T18:54:41.639Z][DEBUG][server.http] Message with `debug` log level.
251```
252
253The log will be less verbose with `warn` level for the `server` context:
254
255```bash
256[2017-07-25T18:54:41.639Z][WARN ][server] Message with `warn` log level.
257[2017-07-25T18:54:41.639Z][ERROR][server] Message with `error` log level.
258[2017-07-25T18:54:41.639Z][FATAL][server] Message with `fatal` log level.
259```
260
261### Logging config migration
262
263Compatibility with the legacy logging system is assured until the end of the `v7` version.
264All log messages handled by `root` context are forwarded to the legacy logging service. If you re-write
265root appenders, make sure that it contains `default` appender to provide backward compatibility.
266**Note**: If you define an appender for a context, the log messages aren't handled by the
267`root` context anymore and not forwarded to the legacy logging service.
268
269#### logging.dest
270
271By default logs in _stdout_. With new OpenSearch Dashboards logging you can use pre-existing `console` appender or
272define a custom one.
273
274```yaml
275logging:
276  loggers:
277    - context: plugins.myPlugin
278      appenders: [console]
279```
280
281Logs in a _file_ if given file path. You should define a custom appender with `kind: file`
282
283```yaml
284logging:
285  appenders:
286    file:
287      kind: file
288      path: /var/log/opensearch_dashboards.log
289      layout:
290        kind: pattern
291  loggers:
292    - context: plugins.myPlugin
293      appenders: [file]
294```
295
296#### logging.json
297
298Defines the format of log output. Logs in JSON if `true`. With new logging config you can adjust
299the output format with [layouts](#layouts).
300
301#### logging.quiet
302
303Suppresses all logging output other than error messages. With new logging, config can be achieved
304with adjusting minimum required [logging level](#log-level).
305
306```yaml
307  loggers:
308    - context: plugins.myPlugin
309      appenders: [console]
310      level: error
311# or for all output
312logging.root.level: error
313```
314
315#### logging.silent:
316
317Suppresses all logging output.
318
319```yaml
320logging.root.level: off
321```
322
323#### logging.verbose:
324
325Logs all events
326
327```yaml
328logging.root.level: all
329```
330
331#### logging.timezone
332
333Set to the canonical timezone id to log events using that timezone. New logging config allows
334to [specify timezone](#date) for `layout: pattern`.
335
336```yaml
337logging:
338  appenders:
339    custom-console:
340      kind: console
341      layout:
342        kind: pattern
343        highlight: true
344        pattern: '[%level] [%date{ISO8601_TZ}{America/Los_Angeles}][%logger] %message'
345```
346
347#### logging.events
348
349Define a custom logger for a specific context.
350
351#### logging.filter
352
353TBD
354
355### Log record format changes
356
357| Parameter  | Platform log record in **pattern** format | Legacy Platform log record **text** format |
358| ---------- | ----------------------------------------- | ------------------------------------------ |
359| @timestamp | ISO8601 `2012-01-31T23:33:22.011Z`        | Absolute `23:33:22.011`                    |
360| context    | `parent.child`                            | `['parent', 'child']`                      |
361| level      | `DEBUG`                                   | `['debug']`                                |
362| meta       | stringified JSON object `{"to": "v8"}`    | N/A                                        |
363| pid        | can be configured as `%pid`               | N/A                                        |
364
365| Parameter  | Platform log record in **json** format     | Legacy Platform log record **json** format |
366| ---------- | ------------------------------------------ | ------------------------------------------ |
367| @timestamp | ISO8601_TZ `2012-01-31T23:33:22.011-05:00` | ISO8601 `2012-01-31T23:33:22.011Z`         |
368| context    | `context: parent.child`                    | `tags: ['parent', 'child']`                |
369| level      | `level: DEBUG`                             | `tags: ['debug']`                          |
370| meta       | separate property `"meta": {"to": "v8"}`   | merged in log record `{... "to": "v8"}`    |
371| pid        | `pid: 12345`                               | `pid: 12345`                               |
372| type       | N/A                                        | `type: log`                                |
373| error      | `{ message, name, stack }`                 | `{ message, name, stack, code, signal }`   |
374