Logging with the JS7 Logger
JS7 - GraalVM Python Jobs are based on Oracle® GraalVM and can use the JS7 Logger like this:
FEATURE AVAILABILITY STARTING FROM RELEASE 2.8.2
class JS7Job(js7.Job):
def processOrder(self, js7Step):
logger = js7Step.getLogger()
logger.info("logging some information")
logger.warn(".. logging some warning")
logger.error(".. logging some error")
logger.debug(".. logging some debug output")
# do some stuff
Explanation:
- The
js7Step.getLogger()object is provided that offers related methods for logging. - By default output of
info(),warn()anddebug()methods is written to the stdout channel, output byerror()is written to stderr.- The argument
log_levelcan be used for a job to specify the log level:log_level = info: default, no debug output enabled.log_level = debug: includes debug output
- For details see JS7 - JITL Common Variables.
- The argument
- For details see JS7 - Job API.
Logging with the Python Console Logger
Basically the Python print(), logging.info(), logging.warning() etc. methods cannot be used as they directly address the stdout channel. As a result use of the methods writes output to the JS7 Agent's stdout channel and to its ./logs/watchdog.log file.
It is an option to override the above logging methods and to map them to js7Step.getLogger() methods like this:
import builtins, logging
def logging_override(js7Step):
def _convert(msg, *args, **kwargs) -> str:
# Ensures that 'msg' is always a string
msg = str(msg)
# Removes 'extra' from 'kwargs' and passes the value to the variable 'extra'
extra = kwargs.pop("extra", None)
# Checks if 'args' is not empty and '%' is in 'msg'
if args and ("%" in msg):
msg = msg % args # If true, apply the modulo operator to replace '%s, %d, ..'
if extra:
# If 'extra' is not empty, we prepend the values to 'msg'
extra_string = " ".join(str(v) for _, v in extra.items())
msg = f"{extra_string} {msg}"
return msg
def _info(msg, *args, **kwargs):
js7Step.getLogger().info(_convert(msg, *args, **kwargs))
def _warning(msg, *args, **kwargs):
js7Step.getLogger().warn(_convert(msg, *args, **kwargs))
def _error(msg, *args, **kwargs):
js7Step.getLogger().error(_convert(msg, *args, **kwargs))
def _debug(msg, *args, **kwargs):
js7Step.getLogger().debug(_convert(msg, *args, **kwargs))
def _log(level: int, msg, *args, **kwargs):
match level:
case 0:
_info(msg, *args, **kwargs)
case 10:
_debug(msg, *args, **kwargs)
case 20:
_info(msg, *args, **kwargs)
case 30:
_warning(msg, *args, **kwargs)
case 40:
_error(msg, *args, **kwargs)
case 50:
_error(msg, *args, **kwargs)
case _:
_error(f"unknown log level: {level}")
# Here we patch the methods for 'print' and 'logging'
# (i) Every Job starts in a new context, therefore this only overrides the methods within the current Job
builtins.print = js7Step.getLogger().info
logging.log = _log
logging.info = _info
logging.warning = _warning
logging.error = _error
logging.critical = _error
logging.exception = _error
logging.debug = _debug
class JS7Job(js7.Job):
def processOrder(self, js7Step):
logging_override(js7Step)
logging.warning("some %s", "warning") # Now you can use 'logging' as usual
To make the override globally available users can add it to a JS7 - Script Include.
- Download sample files (or copy from above examples):
- Download override of Console Logger from Script Include (upload .json): Python-Logging.includescript.json
- Download the workflow (upload .json): pdPythonLogging.workflow.json
- In the Configuration->Inventory view the override is configured like this:
The Script Include can be referenced from a job using the syntax: ##!include <name-of-script-include>
As a result jobs can use the Python Console Logger as usually:
class JS7Job(js7.Job):
def processOrder(self, js7Step):
##!include Python-Logging
print("printing some information")
logging.info("hello %s", "world")
logging.warning("processing delay: %d seconds", 5)
logging.error("logging some error: %s", "ERROR")
logging.debug("debug output: %s", {"key": "value"})
