Logging is most crucial part of any application or process you create, as it helps you debug or keep track of whats going on. Logging is a very vast topic in itself but super useful when you want to perform some side effects or redirect your output to some other services or perform some side computations etc.
Highly configurable by design, we can add extend functionalities of existing loggers, using custom Handlers
.
We will try to extend functionality of existing logger to e-mail
exceptions occurred during code execution. Let us create a python Logger
using built-in library logging
.
<span>import</span> <span>logging</span><span>logger</span><span>:</span> <span>logging</span><span>.</span><span>Logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>()</span><span>import</span> <span>logging</span> <span>logger</span><span>:</span> <span>logging</span><span>.</span><span>Logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>()</span>import logging logger: logging.Logger = logging.getLogger()
Enter fullscreen mode Exit fullscreen mode
Above code will return a root
logger instance, since we are not mentioning any name explicitly. If you want a named logger
instance, you can pass name
to getLogger
function.
Let us now create a custom Handler
to handle records and perform some side effect. We can inherit from logging.Handler
abstract base class to create our custom Handler.
logging.Handler
base class provides multiple hooks that you can override. We will override emit
hook for our requirement.
<span>import</span> <span>logging</span><span>class</span> <span>MailHandler</span><span>(</span><span>logging</span><span>.</span><span>Handler</span><span>):</span><span>def</span> <span>emit</span><span>(</span><span>self</span><span>,</span> <span>record</span><span>:</span> <span>logging</span><span>.</span><span>LogRecord</span><span>)</span> <span>-></span> <span>None</span><span>:</span><span>if</span> <span>record</span><span>.</span><span>exc_info</span><span>:</span><span>exception</span> <span>=</span> <span>""</span><span>.</span><span>join</span><span>(</span><span>traceback</span><span>.</span><span>format_exception</span><span>(</span><span>*</span><span>record</span><span>.</span><span>exc_info</span><span>))</span><span>else</span><span>:</span><span>exception</span> <span>=</span> <span>""</span><span>.</span><span>join</span><span>(</span><span>traceback</span><span>.</span><span>format_exception</span><span>(</span><span>*</span><span>sys</span><span>.</span><span>exc_info</span><span>()))</span><span>self</span><span>.</span><span>_send_mail</span><span>(</span><span>exception</span><span>)</span><span>import</span> <span>logging</span> <span>class</span> <span>MailHandler</span><span>(</span><span>logging</span><span>.</span><span>Handler</span><span>):</span> <span>def</span> <span>emit</span><span>(</span><span>self</span><span>,</span> <span>record</span><span>:</span> <span>logging</span><span>.</span><span>LogRecord</span><span>)</span> <span>-></span> <span>None</span><span>:</span> <span>if</span> <span>record</span><span>.</span><span>exc_info</span><span>:</span> <span>exception</span> <span>=</span> <span>""</span><span>.</span><span>join</span><span>(</span><span>traceback</span><span>.</span><span>format_exception</span><span>(</span><span>*</span><span>record</span><span>.</span><span>exc_info</span><span>))</span> <span>else</span><span>:</span> <span>exception</span> <span>=</span> <span>""</span><span>.</span><span>join</span><span>(</span><span>traceback</span><span>.</span><span>format_exception</span><span>(</span><span>*</span><span>sys</span><span>.</span><span>exc_info</span><span>()))</span> <span>self</span><span>.</span><span>_send_mail</span><span>(</span><span>exception</span><span>)</span>import logging class MailHandler(logging.Handler): def emit(self, record: logging.LogRecord) -> None: if record.exc_info: exception = "".join(traceback.format_exception(*record.exc_info)) else: exception = "".join(traceback.format_exception(*sys.exc_info())) self._send_mail(exception)
Enter fullscreen mode Exit fullscreen mode
We have added set of statements to intercept exception from the record and create formatted stack trace
.
emit
hook will receive logging.LogRecord
which will contain all the details regarding the record like message, timestamp, line no, exception info etc. We have also added a instance method _send_mail
to send formatted stack trace to the user.
Let us now complete _send_mail
function. We will use AWS SES
for sending e-mail. You may also use smtp
as an alternative.
<span>import</span> <span>boto3</span><span>class</span> <span>MailHandler</span><span>(</span><span>logging</span><span>.</span><span>Handler</span><span>):</span><span>def</span> <span>_send_mail</span><span>(</span><span>self</span><span>,</span> <span>message</span><span>):</span><span>self</span><span>.</span><span>client</span> <span>=</span> <span>boto3</span><span>.</span><span>client</span><span>(</span><span>'ses'</span><span>)</span><span>ses_arn</span> <span>=</span> <span>os</span><span>.</span><span>getenv</span><span>(</span><span>'SES_ARN'</span><span>)</span><span>source</span> <span>=</span> <span>os</span><span>.</span><span>getenv</span><span>(</span><span>'SES_SOURCE'</span><span>)</span><span>html</span> <span>=</span> <span>f</span><span>""" <p>Exception occurred while execution. Please check. </p> <pre></span><span>{</span><span>message</span><span>}</span><span></pre> """</span><span>self</span><span>.</span><span>client</span><span>.</span><span>send_email</span><span>(</span><span>Source</span><span>=</span><span>source</span><span>,</span><span>Destination</span><span>=</span><span>{</span><span>'ToAddresses'</span><span>:</span> <span>[</span><span>'foobar@gmail.com'</span><span>,</span><span>],</span><span>},</span><span>Message</span><span>=</span><span>{</span><span>'Subject'</span><span>:</span> <span>{</span><span>'Data'</span><span>:</span> <span>'Exception occurred while executing Lambda. Please check.'</span><span>,</span><span>},</span><span>'Body'</span><span>:</span> <span>{</span><span>'Html'</span><span>:</span> <span>{</span><span>'Data'</span><span>:</span> <span>html</span><span>}</span><span>}</span><span>},</span><span>SourceArn</span><span>=</span><span>ses_arn</span><span>)</span><span>import</span> <span>boto3</span> <span>class</span> <span>MailHandler</span><span>(</span><span>logging</span><span>.</span><span>Handler</span><span>):</span> <span>def</span> <span>_send_mail</span><span>(</span><span>self</span><span>,</span> <span>message</span><span>):</span> <span>self</span><span>.</span><span>client</span> <span>=</span> <span>boto3</span><span>.</span><span>client</span><span>(</span><span>'ses'</span><span>)</span> <span>ses_arn</span> <span>=</span> <span>os</span><span>.</span><span>getenv</span><span>(</span><span>'SES_ARN'</span><span>)</span> <span>source</span> <span>=</span> <span>os</span><span>.</span><span>getenv</span><span>(</span><span>'SES_SOURCE'</span><span>)</span> <span>html</span> <span>=</span> <span>f</span><span>""" <p>Exception occurred while execution. Please check. </p> <pre></span><span>{</span><span>message</span><span>}</span><span></pre> """</span> <span>self</span><span>.</span><span>client</span><span>.</span><span>send_email</span><span>(</span> <span>Source</span><span>=</span><span>source</span><span>,</span> <span>Destination</span><span>=</span><span>{</span> <span>'ToAddresses'</span><span>:</span> <span>[</span> <span>'foobar@gmail.com'</span><span>,</span> <span>],</span> <span>},</span> <span>Message</span><span>=</span><span>{</span> <span>'Subject'</span><span>:</span> <span>{</span> <span>'Data'</span><span>:</span> <span>'Exception occurred while executing Lambda. Please check.'</span><span>,</span> <span>},</span> <span>'Body'</span><span>:</span> <span>{</span> <span>'Html'</span><span>:</span> <span>{</span> <span>'Data'</span><span>:</span> <span>html</span> <span>}</span> <span>}</span> <span>},</span> <span>SourceArn</span><span>=</span><span>ses_arn</span> <span>)</span>import boto3 class MailHandler(logging.Handler): def _send_mail(self, message): self.client = boto3.client('ses') ses_arn = os.getenv('SES_ARN') source = os.getenv('SES_SOURCE') html = f""" <p>Exception occurred while execution. Please check. </p> <pre>{message}</pre> """ self.client.send_email( Source=source, Destination={ 'ToAddresses': [ 'foobar@gmail.com', ], }, Message={ 'Subject': { 'Data': 'Exception occurred while executing Lambda. Please check.', }, 'Body': { 'Html': { 'Data': html } } }, SourceArn=ses_arn )
Enter fullscreen mode Exit fullscreen mode
We are using boto3
library for connecting to SES
and send e-mail.
I am reading ses_arn
and source
from environment variables. These will be required to send e-mail to the destination address using your configured SES
record.
We are done with creating our custom handler. Let us register this with our logger instance.
<span>import</span> <span>logging</span><span>logger</span><span>:</span> <span>logging</span><span>.</span><span>Logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>()</span><span>handler</span> <span>=</span> <span>MailHandler</span><span>()</span><span>handler</span><span>.</span><span>setLevel</span><span>(</span><span>logging</span><span>.</span><span>ERROR</span><span>)</span><span>logger</span><span>.</span><span>logger_</span><span>.</span><span>addHandler</span><span>(</span><span>handler</span><span>)</span><span>import</span> <span>logging</span> <span>logger</span><span>:</span> <span>logging</span><span>.</span><span>Logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>()</span> <span>handler</span> <span>=</span> <span>MailHandler</span><span>()</span> <span>handler</span><span>.</span><span>setLevel</span><span>(</span><span>logging</span><span>.</span><span>ERROR</span><span>)</span> <span>logger</span><span>.</span><span>logger_</span><span>.</span><span>addHandler</span><span>(</span><span>handler</span><span>)</span>import logging logger: logging.Logger = logging.getLogger() handler = MailHandler() handler.setLevel(logging.ERROR) logger.logger_.addHandler(handler)
Enter fullscreen mode Exit fullscreen mode
We have registered our custom handler with our logger instance. It will be only activated on error
record type as we have set level to logging.ERROR
. You may now test your custom handler as below.
<span>logger</span><span>.</span><span>error</span><span>(</span><span>Exception</span><span>(</span><span>"Fake Exception"</span><span>))</span><span>logger</span><span>.</span><span>error</span><span>(</span><span>Exception</span><span>(</span><span>"Fake Exception"</span><span>))</span>logger.error(Exception("Fake Exception"))
Enter fullscreen mode Exit fullscreen mode
You should receive an email, with exception
and stack trace.
Below is the complete code for custom handler.
<span>import</span> <span>boto3</span><span>import</span> <span>logging</span><span>class</span> <span>MailHandler</span><span>(</span><span>logging</span><span>.</span><span>Handler</span><span>):</span><span>def</span> <span>emit</span><span>(</span><span>self</span><span>,</span> <span>record</span><span>:</span> <span>logging</span><span>.</span><span>LogRecord</span><span>)</span> <span>-></span> <span>None</span><span>:</span><span>if</span> <span>record</span><span>.</span><span>exc_info</span><span>:</span><span>exception</span> <span>=</span> <span>""</span><span>.</span><span>join</span><span>(</span><span>traceback</span><span>.</span><span>format_exception</span><span>(</span><span>*</span><span>record</span><span>.</span><span>exc_info</span><span>))</span><span>else</span><span>:</span><span>exception</span> <span>=</span> <span>""</span><span>.</span><span>join</span><span>(</span><span>traceback</span><span>.</span><span>format_exception</span><span>(</span><span>*</span><span>sys</span><span>.</span><span>exc_info</span><span>()))</span><span>self</span><span>.</span><span>_send_mail</span><span>(</span><span>exception</span><span>)</span><span>def</span> <span>_send_mail</span><span>(</span><span>self</span><span>,</span> <span>message</span><span>):</span><span>self</span><span>.</span><span>client</span> <span>=</span> <span>boto3</span><span>.</span><span>client</span><span>(</span><span>'ses'</span><span>)</span><span>ses_arn</span> <span>=</span> <span>os</span><span>.</span><span>getenv</span><span>(</span><span>'SES_ARN'</span><span>)</span><span>source</span> <span>=</span> <span>os</span><span>.</span><span>getenv</span><span>(</span><span>'SES_SOURCE'</span><span>)</span><span>html</span> <span>=</span> <span>f</span><span>""" <p>Exception occurred while execution. Please check. </p> <pre></span><span>{</span><span>message</span><span>}</span><span></pre> """</span><span>self</span><span>.</span><span>client</span><span>.</span><span>send_email</span><span>(</span><span>Source</span><span>=</span><span>source</span><span>,</span><span>Destination</span><span>=</span><span>{</span><span>'ToAddresses'</span><span>:</span> <span>[</span><span>'foobar@gmail.com'</span><span>,</span><span>],</span><span>},</span><span>Message</span><span>=</span><span>{</span><span>'Subject'</span><span>:</span> <span>{</span><span>'Data'</span><span>:</span> <span>'Exception occurred while executing Lambda. Please check.'</span><span>,</span><span>},</span><span>'Body'</span><span>:</span> <span>{</span><span>'Html'</span><span>:</span> <span>{</span><span>'Data'</span><span>:</span> <span>html</span><span>}</span><span>}</span><span>},</span><span>SourceArn</span><span>=</span><span>ses_arn</span><span>)</span><span>logger</span><span>:</span> <span>logging</span><span>.</span><span>Logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>()</span><span>handler</span> <span>=</span> <span>MailHandler</span><span>()</span><span>handler</span><span>.</span><span>setLevel</span><span>(</span><span>logging</span><span>.</span><span>ERROR</span><span>)</span><span>logger</span><span>.</span><span>logger_</span><span>.</span><span>addHandler</span><span>(</span><span>handler</span><span>)</span><span>#raising exception </span><span>try</span><span>:</span><span>raise</span> <span>Exception</span><span>(</span><span>"Fake Exception"</span><span>)</span><span>except</span> <span>Exception</span> <span>as</span> <span>e</span><span>:</span><span>logger</span><span>.</span><span>error</span><span>(</span><span>e</span><span>,</span> <span>exc_info</span><span>=</span><span>True</span><span>)</span><span>import</span> <span>boto3</span> <span>import</span> <span>logging</span> <span>class</span> <span>MailHandler</span><span>(</span><span>logging</span><span>.</span><span>Handler</span><span>):</span> <span>def</span> <span>emit</span><span>(</span><span>self</span><span>,</span> <span>record</span><span>:</span> <span>logging</span><span>.</span><span>LogRecord</span><span>)</span> <span>-></span> <span>None</span><span>:</span> <span>if</span> <span>record</span><span>.</span><span>exc_info</span><span>:</span> <span>exception</span> <span>=</span> <span>""</span><span>.</span><span>join</span><span>(</span><span>traceback</span><span>.</span><span>format_exception</span><span>(</span><span>*</span><span>record</span><span>.</span><span>exc_info</span><span>))</span> <span>else</span><span>:</span> <span>exception</span> <span>=</span> <span>""</span><span>.</span><span>join</span><span>(</span><span>traceback</span><span>.</span><span>format_exception</span><span>(</span><span>*</span><span>sys</span><span>.</span><span>exc_info</span><span>()))</span> <span>self</span><span>.</span><span>_send_mail</span><span>(</span><span>exception</span><span>)</span> <span>def</span> <span>_send_mail</span><span>(</span><span>self</span><span>,</span> <span>message</span><span>):</span> <span>self</span><span>.</span><span>client</span> <span>=</span> <span>boto3</span><span>.</span><span>client</span><span>(</span><span>'ses'</span><span>)</span> <span>ses_arn</span> <span>=</span> <span>os</span><span>.</span><span>getenv</span><span>(</span><span>'SES_ARN'</span><span>)</span> <span>source</span> <span>=</span> <span>os</span><span>.</span><span>getenv</span><span>(</span><span>'SES_SOURCE'</span><span>)</span> <span>html</span> <span>=</span> <span>f</span><span>""" <p>Exception occurred while execution. Please check. </p> <pre></span><span>{</span><span>message</span><span>}</span><span></pre> """</span> <span>self</span><span>.</span><span>client</span><span>.</span><span>send_email</span><span>(</span> <span>Source</span><span>=</span><span>source</span><span>,</span> <span>Destination</span><span>=</span><span>{</span> <span>'ToAddresses'</span><span>:</span> <span>[</span> <span>'foobar@gmail.com'</span><span>,</span> <span>],</span> <span>},</span> <span>Message</span><span>=</span><span>{</span> <span>'Subject'</span><span>:</span> <span>{</span> <span>'Data'</span><span>:</span> <span>'Exception occurred while executing Lambda. Please check.'</span><span>,</span> <span>},</span> <span>'Body'</span><span>:</span> <span>{</span> <span>'Html'</span><span>:</span> <span>{</span> <span>'Data'</span><span>:</span> <span>html</span> <span>}</span> <span>}</span> <span>},</span> <span>SourceArn</span><span>=</span><span>ses_arn</span> <span>)</span> <span>logger</span><span>:</span> <span>logging</span><span>.</span><span>Logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>()</span> <span>handler</span> <span>=</span> <span>MailHandler</span><span>()</span> <span>handler</span><span>.</span><span>setLevel</span><span>(</span><span>logging</span><span>.</span><span>ERROR</span><span>)</span> <span>logger</span><span>.</span><span>logger_</span><span>.</span><span>addHandler</span><span>(</span><span>handler</span><span>)</span> <span>#raising exception </span><span>try</span><span>:</span> <span>raise</span> <span>Exception</span><span>(</span><span>"Fake Exception"</span><span>)</span> <span>except</span> <span>Exception</span> <span>as</span> <span>e</span><span>:</span> <span>logger</span><span>.</span><span>error</span><span>(</span><span>e</span><span>,</span> <span>exc_info</span><span>=</span><span>True</span><span>)</span>import boto3 import logging class MailHandler(logging.Handler): def emit(self, record: logging.LogRecord) -> None: if record.exc_info: exception = "".join(traceback.format_exception(*record.exc_info)) else: exception = "".join(traceback.format_exception(*sys.exc_info())) self._send_mail(exception) def _send_mail(self, message): self.client = boto3.client('ses') ses_arn = os.getenv('SES_ARN') source = os.getenv('SES_SOURCE') html = f""" <p>Exception occurred while execution. Please check. </p> <pre>{message}</pre> """ self.client.send_email( Source=source, Destination={ 'ToAddresses': [ 'foobar@gmail.com', ], }, Message={ 'Subject': { 'Data': 'Exception occurred while executing Lambda. Please check.', }, 'Body': { 'Html': { 'Data': html } } }, SourceArn=ses_arn ) logger: logging.Logger = logging.getLogger() handler = MailHandler() handler.setLevel(logging.ERROR) logger.logger_.addHandler(handler) #raising exception try: raise Exception("Fake Exception") except Exception as e: logger.error(e, exc_info=True)
Enter fullscreen mode Exit fullscreen mode
You can now customize logging handlers has per your requirements.
Happy Logging .
暂无评论内容