Skip to content

Latest commit

 

History

History

custom-log-console

Custom log console

Create a custom log console.

This example shows how to create a log console to print log messages from a JupyterLab extension.

Custom log console example

The default log console extension in JupyterLab obtains log outputs from the kernel context of the current active notebook. So you can either:

  1. Obtain the current active notebook and send message to his Logger instance (see the log message example).
  2. Create your custom log console (covered in this example).

It is strongly recommended to read commands, command-palette, main-menu, widgets and react-widget examples before diving into this one.

To implement this log console you need to install the following packages:

  • @jupyterlab/logconsole: Where you can find the different UI components and message format.
  • @jupyterlab/rendermime: Used to create renderers for various mime-types.
  • @jupyterlab/nbformat: Only necessary if you want to use the notebook output format as the type of message.

This example has two files. In the first one index.ts, you will find all the logic of the extension. And the second one logLevelSwitcher.tsx declares the React component used in the toolbar to switch between different log levels.

First of all, you will start by looking into the declaration of the extension:

// src/index.ts#L25-L35

const extension: JupyterFrontEndPlugin<void> = {
  id: '@jupyterlab-examples/custom-log-console:plugin',
  description: 'A minimal JupyterLab example to develop a custom log console.',
  autoStart: true,
  requires: [ICommandPalette, IRenderMimeRegistry, ILayoutRestorer],
  activate: (
    app: JupyterFrontEnd,
    palette: ICommandPalette,
    rendermime: IRenderMimeRegistry,
    restorer: ILayoutRestorer
  ) => {

To create a new log console the IRenderMimeRegistry token is required, which is necessary as a default rendermime in the LoggerRegistry to render the outputs. Moreover you will need JupyterFrontEnd to have access to some JupyterLab features, ICommandPalette to register some commands. IMainMenu allows to add some commands to the main menu. And ILayoutRestorer can restore the extension layout after reloading the web page.

In the activate function, the first step is to declare logConsolePanel and logConsoleWidget. Therefore, you can pass their reference to the commands. The commands will be able to interact with the widget even when deleting and creating a new one after closing the tab, as well as tracking them before launching the widget.

// src/index.ts#L38-L39

let logConsolePanel: LogConsolePanel | null = null;
let logConsoleWidget: MainAreaWidget<LogConsolePanel> | null = null;

The next step is to create a new widget when clicking on the button to open the custom log console. The function createLogConsoleWidget has all the logic necessary to initialize a new LogConsoleWidget and add it to the main area:

// src/index.ts#L72-L72

const createLogConsoleWidget = (): void => {

To initialize a new LogConsoleWidget you have to create a LogConsolePanel to hold the log messages. LogConsoleWidget needs a LoggerRegistry to add new logs to the panel. LoggerRegistry requires two parameters: the default rendermime and the maximum length for the logs.

// src/index.ts#L73-L78

logConsolePanel = new LogConsolePanel(
  new LoggerRegistry({
    defaultRendermime: rendermime,
    maxLength: 1000
  })
);

The source property identifies where the message comes from and is necessary to initialize the logger object present on LogConsolePanel. This object will let you send log messages to the log console and change the log level. In the logconsole-extension available in core JupyterLab, source identifies the kernel output of the active notebook. So when changing the active notebook, the log console change his logs. You can use any string for the value, but it is recommended to use the name of your extension.

// src/index.ts#L80-L80

logConsolePanel.source = 'custom-log-console';

Now you are ready to initialize a new MainAreaWidget passing the logConsolePanel as the content.

// src/index.ts#L82-L84

logConsoleWidget = new MainAreaWidget<LogConsolePanel>({
  content: logConsolePanel
});

The last step of the function createLogConsoleWidget is to establish how to proceed after a dispose request. The dispose method is present in Lumino widgets to ensure that resources can be claimed by the garbage collector. In this case, this method is called when you close the tab for a MainAreaWidget. So you need to delete the logConsolePanel and logConsoleWidget instances to clean all logs and be ready to initialize a new widget when you decide to open the tab again.

// src/index.ts#L108-L112

logConsoleWidget.disposed.connect(() => {
  logConsoleWidget = null;
  logConsolePanel = null;
  commands.notifyCommandChanged();
});

To launch a new log console, you can add a new command. In this case, you can use the option isToggled to make the button checkable to open and close the log console with the same button. In the execute function there is an if statement to check when closing and deleting the MainAreaWidget, and when creating and opening a new one calling createLogConsoleWidget.

// src/index.ts#L121-L132

commands.addCommand('jlab-examples/custom-log-console:open', {
  label: 'Custom Log Console',
  caption: 'Custom log console example.',
  isToggled: () => logConsoleWidget !== null,
  execute: () => {
    if (logConsoleWidget) {
      logConsoleWidget.dispose();
    } else {
      createLogConsoleWidget();
    }
  }
});

Finally, you can send log messages calling log method present on the logger property of logConsolePanel. This method lets you send different types:

  • HTML message with IHtmlLog:
// src/index.ts#L143-L149

const msg: IHtmlLog = {
  type: 'html',
  level: 'debug',
  data: '<div>Hello world HTML!!</div>'
};

logConsolePanel?.logger?.log(msg);
  • Raw text message with ITextLog
// src/index.ts#L157-L163

const msg: ITextLog = {
  type: 'text',
  level: 'info',
  data: 'Hello world text!!'
};

logConsolePanel?.logger?.log(msg);
  • Cell output message with IOutputLog
// src/index.ts#L171-L184

const data: nbformat.IOutput = {
  output_type: 'display_data',
  data: {
    'text/plain': 'Hello world nbformat!!'
  }
};

const msg: IOutputLog = {
  type: 'output',
  level: 'warning',
  data
};

logConsolePanel?.logger?.log(msg);