Skip to content

Log

With the log.py module, you are able to use logs in a more efficient and precise way by configuring headers, payload, tracebacks, and setting log types such as DEBUG, ERROR, or INFO.

from everysk.core.log import Logger

Below we have a quick implementation example of the Logger class followed by an explanation of the attributes involved:

logger = Logger('test-logger')
logger.info('test message', extra={'http_headers': {'x-cloud-trace-context': '123'}}, stacklevel=1)
2024-09-17 19:38:20,946 - INFO - {} - info message

The string passed to Logger is the name you choose to identify this logger instance (e.g. 'test-logger'). It appears in log output and helps distinguish log sources when multiple loggers are in use.


If we wish to display the log messages to the Google Log Explorer, we need to configure the LOGGING_JSON variable in the settings file to True.

Now when we instantiate the Logger class, the log messages will be displayed in the Google Log Explorer.

logger = Logger('google-explorer-logger')
logger.info('', extra={'http_headers': {'x-cloud-trace-context': '123'}}, stacklevel=1)


Example for Developers

The following example demonstrates a simple use case of the Logger class inside a function, using different log levels based on a condition. This example runs directly inside the Everysk platform — paste the code into the Source Code panel of a For Developers worker and click Test to see the output.

Note

When using the Logger class inside a For Developers worker, the name must be 'user_logger' — this is required by convention for logs to be captured and displayed correctly in the platform.

from everysk.core.log import Logger
from everysk.core.datetime import Date

log = Logger('user_logger')

def main(args):
    today = Date.today()

    log.info(f'Running checks for {today.month_name}, quarter {today.quarter}')

    if today.is_last_day_of_month():
        log.warning('Today is the last day of the month — close-of-month processes may be running')
    else:
        days_left = Date.days_diff(today, today.get_last_day_of_month())
        log.info(f'{days_left} days remaining until end of month')

    next_bizday = today.bizdays_delta(1, calendar='ANBIMA')
    if next_bizday.month != today.month:
        log.warning(f'Next business day falls in {next_bizday.month_name} — crosses into a new month')
    else:
        log.info(f'Next business day: {next_bizday.strftime("%d/%m/%Y")}')


    return args

After running the script via the Test button, the Execution Result panel in the platform will display:

{
  "status": "OK",
  "data": {
    "script_inputs": {},
    "workspace": "main"
  },
  "log": [
    "INFO - Running checks for April, quarter 2",
    "INFO - 29 days remaining until end of month",
    "INFO - Next business day: 03/04/2025"
  ]
}

When run on the last day of a month (e.g. March 31), the is_last_day_of_month() condition is met and the next business day falls in April, so both warnings are triggered instead:

from everysk.core.log import Logger
from everysk.core.datetime import Date

log = Logger('user_logger')

def main(args):
    today = Date(2026, 3, 31)

    log.info(f'Running checks for {today.month_name}, quarter {today.quarter}')

    if today.is_last_day_of_month():
        log.warning('Today is the last day of the month — close-of-month processes may be running')
    else:
        days_left = Date.days_diff(today, today.get_last_day_of_month())
        log.info(f'{days_left} days remaining until end of month')

    next_bizday = today.bizdays_delta(1, calendar='ANBIMA')
    if next_bizday.month != today.month:
        log.warning(f'Next business day falls in {next_bizday.month_name} — crosses into a new month')
    else:
        log.info(f'Next business day: {next_bizday.strftime("%d/%m/%Y")}')

    return args
{
  "status": "OK",
  "data": {
    "script_inputs": {},
    "workspace": "main"
  },
  "log": [
    "INFO - Running checks for March, quarter 1",
    "WARNING - Today is the last day of the month — close-of-month processes may be running",
    "WARNING - Next business day falls in April — crosses into a new month"
  ]
}


The following example forces an error inside a try/except block and logs it with log.error:

from everysk.core.log import Logger

log = Logger('user_logger')

def main(args):
    try:
        raise ValueError('Something went wrong during processing')
    except ValueError as e:
        log.error(f'Caught an error: {e}')

    return args

The output inside the platform will look something like this

{
  "status": "OK",
  "data": {
    "script_inputs": {},
    "workspace": "main"
  },
  "log": [
    "ERROR - Caught an error: Something went wrong during processing"
  ]
}


Attributes

The extra parameter is used to add additional context to the logs. It consists of a dictionary that can be used to supply custom attributes to the log record. This information will be displayed in the terminal or in Google Log Explorer.

The stacklevel parameter is used to specify the stack level of the log. It is used to determine the source of the log message. The default value is 1. When the stacklevel is greater than 1, the corresponding number of stack frames will be skipped. This is useful when we want to log the message from a different file or function.


Methods

The method critical() is used in order to log a message with a severity level of CRITICAL. The method takes a msg input which is the message that will be logged. We are also able to provide an extra dictionary which might contain any extra information that we want to log and a stacklevel to be used in the log.

logger.critical('test message')
2024-09-17 19:38:20,946 - CRITICAL - {} - critical message


The debug() method is used in order to log a message with a severity level of DEBUG. The method takes a msg input which is the message that will be logged. We are also able to provide an extra dictionary which might contain any extra information that we want to log and a stacklevel to be used in the log.

logger.debug('test message')
2024-09-17 19:38:20,946 - DEBUG - {} - debug message


The deprecated() method is used in order to log a message with a severity level of WARNING. The method takes a msg input which is the message that will be logged. We are also able to provide an extra dictionary which might contain any extra information that we want to log and a stacklevel to be used in the log.

logger.deprecated('test message')
2024-09-17 19:38:20,946 - WARNING - {} - DeprecationWarning: deprecated message

The deprecated() method can also accept an optional show_once boolean, which defaults to True. When set to True it will only log the message once, otherwise, it will log the message every time the method is called.

logger.deprecated('test message', show_once=False)
2024-09-17 19:38:20,946 - WARNING - {} - DeprecationWarning: deprecated message


The error() method is used in order to log a message with a severity level of ERROR. The method takes a msg input which is the message that will be logged. We are also able to provide an extra dictionary which might contain any extra information that we want to log and a stacklevel to be used in the log.

logger.error('test message')
2024-09-17 19:38:20,946 - ERROR - {} - error message


The exception() method is used in order to log a message with a severity level of ERROR and also log the traceback of the exception. The method takes a msg input which is the message that will be logged. We are also able to provide an extra dictionary which might contain any extra information that we want to log and a stacklevel to be used in the log.

logger.exception('test message')
2024-09-17 19:38:20,946 - ERROR - {} - error message


The info() method is used in order to log a message with a severity level of INFO. The method takes a msg input which is the message that will be logged. We are also able to provide an extra dictionary which might contain any extra information that we want to log and a stacklevel to be used in the log.

logger.info('test message')
2024-09-17 19:38:20,946 - INFO - {} - info message


The warning() method is used in order to log a message with a severity level of WARNING. The method takes a msg input which is the message that will be logged. We are also able to provide an extra dictionary which might contain any extra information that we want to log and a stacklevel to be used in the log.

logger.warning('test message')
2024-09-17 19:38:20,946 - WARNING - {} - warning message


The slack() method is used in order to send a message to a Slack channel. The first argument is a title string, the second argument is a message string, and the third mandatory argument is the color of the message, which boils down to either danger, success, or warning.

The message also accepts a url param indicating the URL of the Slack channel, it defaults to the SLACK_URL located in the settings file if it's not provided.

You can set up your Slack webhook by following the instructions provided in this guide.

After you are done setting up your Slack webhook, the URL will look something like this:

    https://hooks.slack.com/services/T00000000/B0000/XXXXXXX

Now you can use the URL above to send messages to a specific Slack channel

logger.slack(
    title='test title',
    message='test message',
    color='danger',
    url='https://hooks.slack.com/services/T00000000/B0000/XXXXXXX'
)
2024-10-22 18:44:19,868 - ERROR - {} - Slack message: test title -> test message

The url parameter can be configured in order to update the default Slack URL.

You can learn more about sending Slack and webhooks messages by visiting the official Slack Documentation.


Formatter

The Formatter class from the log module is used in order to convert log records to strings. It contains a couple of private methods that are used to format the log records.

The formatMessage() method is used in order to display the message in the terminal or inside Google Log Explorer. It takes a record object as input and calls the _get_result_dict() method to convert the log record to a dictionary, then it returns the serialized dictionary.

from everysk.core.log import Formatter

formatter = Formatter()
record = logging.LogRecord(
    args=(),
    exc_info=None,
    func='test_function',
    level=logging.INFO,
    lineno=10,
    msg='test message',
    name='formatter-test',
    pathname='test.py',
)

record.http_headers = {'transparent': 'header'}
record.payload = {'status': 'OK'}

formatter.formatMessage(record)
'{"message": "test message", "severity": "INFO", "labels": {}, "logName": "formatter-test", "traceback": "", "http": {"headers": {"transparent": "header"}, "payload": {"status": "OK"}}, "logging.googleapis.com/sourceLocation": { "file": "test.py", "function":"test_function", "line": 10}, "logging.googleapis.com/spanId": "", "logging.googleapis.com/trace": "", "logging.googleapis.com/trace_sampled": False}'


Logger Manager

The LoggerManager class is used in order to create a context manager for the Logger object allowing to add extra information to the log records. Take a look at the following example to see how this works in practice.

We can start by running the following import statement:

from everysk.core.log import LoggerManager

Now, let's see how we can use the LoggerManager class in order to propagate the http_headers to other log records:

with LoggerManager(http_headers={}):
    headers = get_http_headers()
    with LoggerManager(http_headers=headers):
        try:
            response = make_request()
        except Exception as e:
            logger.exception('Error making request')

LoggerManager.reset()