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.
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.
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.
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.
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.
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.
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.
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:
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:
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()