Monday, December 1, 2008

Отправляем сообщения об ошибках используя Gmail и модуль logging в Python.

Наверное многие как и я пользуются сервисами Google в том числе Google Apps for your domain, который позволяется не загоняться как минимум почтой на своем сервере и не бороться со спамерами использующими SMTP релей. В том числе сам Gmail имеет много преимуществ перед другими почтовиками. Отправляю почту я как правило тоже используя сервера gmail.

У программистов иногда стоит задача скриптами отправлять почту ( я не про спам ;) ), это может быть как контактная форма, так и какая то служебная информация от скриптов. Например скрипт оповещает о завершении какой-либо задачи, это могут быть какие то периодические отчеты, либо в случае ошибки отправлять письмо с данными о "происшествии".

Так вот для последнего в Python есть такая библиотека которая называется, как не сложно догадаться, logging. У нее есть много возможностей, но основная ее задача это логирование сообщений со скриптов. Вот так выглядит простейшее использование этой библиотеки:
import logging
logging.basicConfig(level=logging.DEBUG,
filename='/tmp/my_demo.log',
filemode='a')

Потом в любом месте скрипта делаем так
import logging
logging.debug('My demo log string...')

и в файле /tmp/my_demo.log мы увидем строчку
DEBUG:root:Demom log string...

Так вот когда я сказал что у модуля logging есть много возможностей, я не имел ввиду что логировать так как показано в примере это круто. По умолчанию да, сообщения логируются просто в файл. Но изначально существуют обработчики, назовем их логерами.
  • StreamHandler посылает логируемое сообщение в поток (файл подобные объекты).
  • FileHandler – в файл на диске.
  • BaseRotatingHandler - это базовый класс для обработчиков которые "ротируют" лог файлы. Не рекомендуется использовать его напрямую, используйте вместо него TimedRotatingFileHandler.
  • RotatingFileHandler - сообщения пишутся в файл на диске, ротируются по достижении указанного размера файла.
  • TimedRotatingFileHandler тоже самое что и предыдущий, только ротируется при определенных временных интервалах.
  • SocketHandler пишет сообщение в TCP/IP сокет.
  • DatagramHandler – в UDP сокет.
  • SMTPHandler отправляет сообщение по на почту.
  • SysLogHandler отправляет сообщение демону syslog в Unix, возможно и на удаленную машину.
  • NTEventLogHandler посылает сообщение логу событий Windows NT/2000/XP (о винде даже позаботились, хотя зачем ей это :) ).
  • MemoryHandler пишем в буфер в памяти который сбрасывается при определенных критериях.
  • HTTPHandler отправляет сообщение в виде "GET" или "POST" HTTP запроса на сервер.

Вот какой богатый набор. Но и этого бывает недостаточно иногда. Об этом сегодня и говорим, и мне недостаточно стандартного SMTPHandler, т.к. у него есть один недостаток - он не может авторизоваться на сервере. Но и для того чтобы отсылать через smtp сервер gmail, нужно еще небольшое дополнение.

Все что нам потребуется, это расширить стандартный SMTPHandler, немножко дописав метод __init__ чтобы он принимал наши логин и пароль, и переписать метод emit, так, чтобы он правильно отправил сообщение через гмыло. Вот что получилось.
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import logging, logging.handlers, types, smtplib
from email.MIMEText import MIMEText

class GmailHandler(logging.handlers.SMTPHandler):
    def __init__(self, mailhost, fromaddr, toaddrs, subject, *args, **kwargs):
        logging.Handler.__init__(self)
        if type(mailhost) == types.TupleType:
            host, port = mailhost
            self.mailhost = host
            self.mailport = port
        else:
            self.mailhost = mailhost
            self.mailport = None
            self.fromaddr = fromaddr
        if type(toaddrs) == types.StringType:
            toaddrs = [toaddrs]
            self.toaddrs = toaddrs
            self.subject = subject

        # added this lines
        self.auth_username = kwargs.get('auth_username', None)
        self.auth_password = kwargs.get('auth_password', None)
        if self.auth_username and self.auth_password:
            self.auth_required = True
        else:
            self.auth_required = False

    def emit(self, record):
        try:
            msg = MIMEText(self.format(record), 'html', 'utf-8')
            msg['Subject'] = self.getSubject(record)
            msg['From'] = self.fromaddr
            msg['Sender'] = self.fromaddr
            msg['To'] = ','.join(self.toaddrs)

            port = self.mailport
            if not port:
                port = smtplib.SMTP_PORT

            session = smtplib.SMTP(self.mailhost, port)
            session.ehlo()
            # TLS
            session.starttls()
            session.ehlo()
            if self.auth_required:
                session.login(self.auth_username, self.auth_password)
            session.sendmail(self.fromaddr, ','.join(self.toaddrs), msg.as_string())
            session.quit()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)

Пользоваться вот так
import logging
from gmail_logging_handler import GmailHandler

rootLogger = logging.getLogger('')
gmail_handler = GmailHandler(
    ('smtp.gmail.com', 587),  
    'from_email@gmail.com', 
    'to_email@gmail.com', 
    "Error at site.com", 
    auth_username="GMAIL_EMAIL", 
    auth_password="PASSWORD"
)
rootLogger.addHandler(gmail_handler)

logging.error('This is an error message that should be sent by mail.')

Естественно вам надо вставить свои логин и пароль от gmail.

Скачать GmailLoggingHandler одним файлом.

Пользуйтесь наздоровье.

Вот тут например есть реализация обработчика который отсылает сообщения на Jabber аккаунт (он же Google Talk). А куда вам еще надо отсылать лог сообщения?

1 comments:

Как скрыть код скрипта на Python для последующей его продажи. | Vladimir Prudnikov said...

[...] Если вы пишите скрипты на продажу либо на заказ рано или поздно встает вопрос как скрыть код скрипта который вы продаете и не отдавать исходный код. Зачем это надо? Например для того чтобы скрипт сам по себе не пошел по рукам, за что вы будете получать 0$ со скрипта, который вы сделали для продажи. Для PHP был создан Zend Encoder. Но я уже не сижу на PHP, поэтому сегодня расскажу как скрыть код скрипта на Python. [...]

Post a Comment