Skip to content

Logging

The way logging works in OpenSearch Dashboards is inspired by log4j 2 logging framework used by OpenSearch. The main idea is to have consistent logging behaviour (configuration, log format etc.) across the entire OpenSearch Stack where possible.

Loggers, Appenders and Layouts

OpenSearch Dashboards logging system has three main components: loggers, appenders and layouts. These components allow us to log messages according to message type and level, and to control how these messages are formatted and where the final logs will be displayed or stored.

Loggers define what logging settings should be applied at the particular context.

Appenders define where log messages are displayed (eg. stdout or console) and stored (eg. file on the disk).

Layouts define how log messages are formatted and what type of information they include.

Logger hierarchy

Every logger has its unique name or context that follows hierarchical naming rule. The logger is considered to be an ancestor of another logger if its name followed by a . is a prefix of the descendant logger name. For example logger with a.b context is an ancestor of logger with a.b.c context. All top-level loggers are descendants of special logger with root context that resides at the top of the logger hierarchy. This logger always exists and fully configured.

Developer can configure log level and appenders that should be used within particular context. If logger configuration specifies only log level then appenders configuration will be inherited from the ancestor logger.

Note: in the current implementation log messages are only forwarded to appenders configured for a particular logger context or to appenders of the closest ancestor if current logger doesn't have any appenders configured. That means that we don't support so called appender additivity when log messages are forwarded to every distinct appender within ancestor chain including root.

Log level

Currently we support the following log levels: all, fatal, error, warn, info, debug, trace, off. Levels are ordered, so all > fatal > error > warn > info > debug > trace > off. A log record is being logged by the logger if its level is higher than or equal to the level of its logger. Otherwise, the log record is ignored.

The all and off levels can be used only in configuration and are just handy shortcuts that allow developer to log every log record or disable logging entirely for the specific context.

Layouts

Every appender should know exactly how to format log messages before they are written to the console or file on the disk. This behaviour is controlled by the layouts and configured through appender.layout configuration property for every custom appender (see examples in Configuration). Currently we don't define any default layout for the custom appenders, so one should always make the choice explicitly.

There are two types of layout supported at the moment: pattern and json.

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: [%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 and doesn't implement all log4j2 capabilities. The conversions that are provided out of the box are:

level

Outputs the level of the logging event. Example of %level output:

TRACE
DEBUG
INFO
logger

Outputs the name of the logger that published the logging event. Example of %logger output:

server
server.http
server.http.OpenSearchDashboards

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:

// 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 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.

Configuration

As any configuration in the platform, logging configuration is validated against the predefined schema and if there are any issues with it, OpenSearch Dashboards will fail to start with the detailed error message.

Once the code acquired a logger instance it should not care about any runtime changes in the configuration that may happen: all changes will be applied to existing logger instances under the hood.

Here is the configuration example that can be used to configure loggers, appenders and layouts:

logging:
  appenders:
    console:
      kind: console
      layout:
        kind: pattern
        highlight: true
    file:
      kind: file
      path: /var/log/opensearch_dashboards.log
      layout:
        kind: pattern
    custom:
      kind: console
      layout:
        kind: pattern
        pattern: '[%date][%level] %message'
    json-file-appender:
      kind: file
      path: /var/log/opensearch-dashboards-json.log

  root:
    appenders: [console, file]
    level: error

  loggers:
    - context: plugins
      appenders: [custom]
      level: warn
    - context: plugins.myPlugin
      level: info
    - context: server
      level: fatal
    - context: optimize
      appenders: [console]
    - context: telemetry
      level: all
      appenders: [json-file-appender]

Here is what we get with the config above:

Context Appenders Level
root console, file error
plugins custom warn
plugins.myPlugin custom info
server console, file fatal
optimize console error
telemetry json-file-appender all

The root logger has a dedicated configuration node since this context is special and should always exist. By default root is configured with info level and default appender that is also always available. This is the configuration that all custom loggers will use unless they're re-configured explicitly.

For example to see all log messages that fall back on the root logger configuration, just add one line to the configuration:

logging.root.level: all

Or disable logging entirely with off:

logging.root.level: off

Usage

Usage is very straightforward, one should just get a logger for a specific context and use it to log messages with different log level.

const logger = opensearchDashboards.logger.get('server');

logger.trace('Message with `trace` log level.');
logger.debug('Message with `debug` log level.');
logger.info('Message with `info` log level.');
logger.warn('Message with `warn` log level.');
logger.error('Message with `error` log level.');
logger.fatal('Message with `fatal` log level.');

const loggerWithNestedContext = opensearchDashboards.logger.get('server', 'http');
loggerWithNestedContext.trace('Message with `trace` log level.');
loggerWithNestedContext.debug('Message with `debug` log level.');

And assuming logger for server context with console appender and trace level was used, console output will look like this:

[2017-07-25T18:54:41.639Z][TRACE][server] Message with `trace` log level.
[2017-07-25T18:54:41.639Z][DEBUG][server] Message with `debug` log level.
[2017-07-25T18:54:41.639Z][INFO ][server] Message with `info` log level.
[2017-07-25T18:54:41.639Z][WARN ][server] Message with `warn` log level.
[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.

[2017-07-25T18:54:41.639Z][TRACE][server.http] Message with `trace` log level.
[2017-07-25T18:54:41.639Z][DEBUG][server.http] Message with `debug` log level.

The log will be less verbose with warn level for the server context:

[2017-07-25T18:54:41.639Z][WARN ][server] Message with `warn` log level.
[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 OpenSearch Dashboards logging you can use pre-existing console appender or define a custom one.

logging:
  loggers:
    - context: plugins.myPlugin
      appenders: [console]

Logs in a file if given file path. You should define a custom appender with kind: file

logging:
  appenders:
    file:
      kind: file
      path: /var/log/opensearch_dashboards.log
      layout:
        kind: pattern
  loggers:
    - context: plugins.myPlugin
      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.

logging.quiet

Suppresses all logging output other than error messages. With new logging, config can be achieved with adjusting minimum required logging level.

  loggers:
    - context: plugins.myPlugin
      appenders: [console]
      level: error
# or for all output
logging.root.level: error

logging.silent:

Suppresses all logging output.

logging.root.level: off

logging.verbose:

Logs all events

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 for layout: pattern.

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

Log record format changes

Parameter Platform log record in pattern format Legacy Platform log record text format
@timestamp ISO8601 2012-01-31T23:33:22.011Z Absolute 23:33:22.011
context parent.child ['parent', 'child']
level DEBUG ['debug']
meta stringified JSON object {"to": "v8"} N/A
pid can be configured as %pid N/A
Parameter Platform log record in json format Legacy Platform log record json format
@timestamp ISO8601_TZ 2012-01-31T23:33:22.011-05:00 ISO8601 2012-01-31T23:33:22.011Z
context context: parent.child tags: ['parent', 'child']
level level: DEBUG tags: ['debug']
meta separate property "meta": {"to": "v8"} merged in log record {... "to": "v8"}
pid pid: 12345 pid: 12345
type N/A type: log
error { message, name, stack } { message, name, stack, code, signal }