Configure Logging#
Utilize Bokeh and Panel Environment Variables#
Bokeh and Panel offer several environment variables for logging control:
BOKEH_LOG_LEVEL
: Sets the log level of thebokeh
JavaScript logger, with the default being âinfoâ.BOKEH_PY_LOG_LEVEL
: Sets the log level of thebokeh
Python logger, by default itâs ânoneâ.PANEL_LOG_LEVEL
: Sets the log level of thepanel
Python logger, with the default being âWARNINGâ.
For more detailed information, refer to bokeh.util.logconfig
or panel.io.logging
.
Reconfigure the Root Logger#
Bokeh executes logging.basicConfig
to configure the root logger. Fortunately we can redo this using force=True
.
import logging
import panel as pn
FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
@pn.cache
def reconfig_basic_config(format_=FORMAT, level=logging.INFO):
"""(Re-)configure logging"""
logging.basicConfig(format=format_, level=level, force=True)
logging.info("Logging.basicConfig completed successfully")
reconfig_basic_config()
logger = logging.getLogger(name="app")
logger.info("Hello World")
pn.panel("Hello World").servable()
Upon use, it should resemble:
$ panel serve app.py
2024-03-16 08:20:35,993 Starting Bokeh server version 3.4.0 (running on Tornado 6.4)
2024-03-16 08:20:35,995 User authentication hooks NOT provided (default user enabled)
2024-03-16 08:20:36,002 Bokeh app running at: http://localhost:5006/app
2024-03-16 08:20:36,002 Starting Bokeh server with process id: 25540
2024-03-16 08:20:36,930 | INFO | root | Logging.basicConfig completed successfully
2024-03-16 08:20:36,930 | INFO | app | Hello World
2024-03-16 08:20:37,080 | INFO | tornado.access | 200 GET /app (::1) 168.75ms
2024-03-16 08:20:37,184 | INFO | tornado.access | 101 GET /app/ws (::1) 0.00ms
2024-03-16 08:20:37,185 | INFO | bokeh.server.views.ws | WebSocket connection opened
2024-03-16 08:20:37,186 | INFO | bokeh.server.views.ws | ServerConnection created
2024-03-16 08:20:37,189 | INFO | tornado.access | 200 GET /static/extensions/panel/images/favicon.ico (::1) 2.00ms
Note
Please be aware that loggers like tornado
, which were not displayed previously, will now be shown because weâve modified the root log level to INFO.
Note
We utilize caching (pn.cache
) because reconfig_basic_config
runs each time a new Panel session starts. If you move the logging reconfiguration to a separate module or a --setup
script, caching wonât be necessary.
Configure a Single Logger#
If you prefer not to reconfigure all logging, you can configure a single logger as demonstrated below.
import logging
import sys
import panel as pn
FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
@pn.cache
def get_logger(name, format_=FORMAT, level=logging.INFO):
logger = logging.getLogger(name)
logger.handlers.clear()
handler = logging.StreamHandler()
handler.setStream(sys.stdout)
formatter = logging.Formatter(format_)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.propagate = False
logger.setLevel(level)
logger.info("Logger successfully configured")
return logger
logger = get_logger(name="app")
logger.info("Hello World")
pn.panel("Hello World").servable()
It should yield:
$ panel serve app.py
2024-03-16 08:24:02,469 Starting Bokeh server version 3.4.0 (running on Tornado 6.4)
2024-03-16 08:24:02,469 User authentication hooks NOT provided (default user enabled)
2024-03-16 08:24:02,485 Bokeh app running at: http://localhost:5006/app
2024-03-16 08:24:02,485 Starting Bokeh server with process id: 33536
2024-03-16 08:24:05,272 | INFO | app | Logger successfully configured
2024-03-16 08:24:05,272 | INFO | app | Hello World
2024-03-16 08:24:05,565 WebSocket connection opened
2024-03-16 08:24:05,566 ServerConnection created
Configure the Panel and Bokeh Loggers#
If you want to configure the Panel and Bokeh loggers as well, itâs straightforward.
import logging
import sys
import panel as pn
FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
@pn.cache
def get_logger(name, format_=FORMAT, level=logging.INFO):
logger = logging.getLogger(name)
logger.handlers.clear()
handler = logging.StreamHandler()
handler.setStream(sys.stdout)
formatter = logging.Formatter(format_)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.propagate = False
logger.setLevel(level)
logger.info("Logger successfully configured")
return logger
get_logger(name="bokeh")
get_logger(name="panel")
logger = get_logger(name="app")
logger.info("Hello World")
pn.panel("Hello World").servable()
It will appear as follows:
$ panel serve app.py
2024-03-16 08:26:59,183 Starting Bokeh server version 3.4.0 (running on Tornado 6.4)
2024-03-16 08:26:59,183 User authentication hooks NOT provided (default user enabled)
2024-03-16 08:26:59,199 Bokeh app running at: http://localhost:5006/app
2024-03-16 08:26:59,199 Starting Bokeh server with process id: 11200
2024-03-16 08:27:00,434 | INFO | app | logger successfully configured
2024-03-16 08:27:00,435 | INFO | bokeh | logger successfully configured
2024-03-16 08:27:00,437 | INFO | panel | logger successfully configured
2024-03-16 08:27:00,437 | INFO | app | Hello World
2024-03-16 08:27:00,585 | INFO | panel.io.server | Session 2863386286800 created