1、需求
1)把重要日志通过邮件发出去
2)做成定期任务,比如每日定时发送一次
2、开始有过但后来弃用的思路
Django自带发送邮件的方法send_mail,很简单,原本准备直接用,比如像这样:
4 from celery import shared_task
5 from django.core.mail import send_mail
6
10
11 from confs.email_settings import EMAIL_HOST_USER
12
13 @shared_task
14 def send_a_email(subject="默认", message="默认", rec_addr_list=[], sender_addr=EMAIL_HOST_USER):
15 if rec_addr_list:
16 send_mail(subject=subject,message=message,from_email=sender_addr,recipient_list=rec_addr_list,fail_silently=False)
17 print('mail sent')
18 return 1
19 else:
20 return 0
23 if __name__ == "__main__":
24 print('begin to send a email')
25 rec_addr_list=['xxx@qq.com']
26 res = send_a_email.delay(rec_addr_list=rec_addr_list)
27 print(res.ready())
28 print(res.get())
但这段代码用起来很麻烦,行不通:
发送邮件明显是公用功能,不与任何一个具体的app绑定,所以我把她在一个叫做utils的文件夹内,utils是文件夹不是app。
如果直接python运行这个文件会报错,提示找不到Django的环境(因为utils/不是注册app,相当于django环境之外使用django组件,首先需要引入django的环境)更别说celery调用的tasks也要求是在app内的task。
所以放弃这条思路!
3、最终的思路
后来参考这篇文章somenzz:Python运维-分分钟搭建邮件报警接口 ,决定依葫芦画瓢:
把发送邮件单独放在一个app内部,这个app就是一个API功能。
文章内容很丰富,涉及了很多常用的东西,但又浅显能看懂,比如curl、uwsgi、Restful……好评~
理解RESTful架构 - 阮一峰的网络日志 https:// blog.csdn.net/delicious ion/article/details/78062521
爬虫工具之curl
参考上面的文章,我的思路如下:
1)实现一个发送邮件功能的Django app,并作为API对外提供。
2)调用这个API,可以用Python的requests库
3)调动的定期执行,可以用Python的schedule库
4、实现:
1)实现邮件发送API(基本照抄上面)
其中涉及账号密码的隐私配置是单独放在一个文件,没有提交到Git哈。
现在没有做身份认证,只检查参数,还是有点不安全……不过先将就一下吧。。
"""
一个发送邮件的API。
调用方式:POST+参数
参数有4个:subject message from_email to_emial,除message外不能为空
返回是json:例如{'code':0,'message':'邮件发送成功.'},code为0表示成功发送,其他则为异常
2019.03.26
"""
import logging
logger = logging.getLogger('mysite.mailapi.info')
from django.http import JsonResponse
# Create your views here.
from django.core.mail import send_mail
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from utils.get_client_infos import get_visitor_ip
from utils.get_ip_infos import get_location_calling_free_api
#先禁用防跨站请求伪造功能,方便 curl post 测试和调用
@csrf_exempt
@require_POST
def send_email(request):
return_data = {'code':0,'message':'邮件发送成功.'}
subject = request.POST.get('subject', '')
message = request.POST.get('message', '')
from_email = request.POST.get('from_email', '')
to_email = request.POST.get('to_email', '')
#设置 from_email 的默认值
if not message:
message = '' # 内容留空是允许的吧?
print("subject",subject)
print("message",message)
print("from_email",from_email)
if subject and to_email and from_email:
try:
to_email = to_email.split(';') #多个收件人以;分隔
print("to_email",to_email)
send_mail(subject, message, from_email, to_email)
except BadHeaderError:
return_data['code'] = 1
return_data['message'] = '邮件发送失败.'
else:
# In reality we'd use a form class
# to get proper validation errors.
return_data['code'] = 0
return_data['message'] = '检查必要字段是否完整'
ip = get_visitor_ip(request)
ip_infos = get_location_calling_free_api(ip)
logger.info(f"ip = {ip}, subject = {subject },message = {message }, from_email = {from_email }, to_email = {to_email }, return_data = {return_data }")
return JsonResponse(return_data)
先用curl -d 测试:
curl -d "subject=邮件报警测试&message=这是一个邮件测试&to_email=xxx@163.com&from_email=xxx@163.com" "http://localhost:8001/api/sendemail/"
![b82c1bf9f8f832d86334730153444066.png](https://img-blog.csdnimg.cn/img_convert/b82c1bf9f8f832d86334730153444066.png)
再据此把调用写成Python脚本send_emails.py(脚本放在utils文件夹内,用requests实现http请求):
19 def send_a_email(subject, message, to_email, from_email='tianweigrace@qq.com'):
20 url_api = f"http://localhost:{http_port}/api/sendemail/"
21 request_method = "post"
22 form_data = {
23 'subject': subject,
24 'message': message,
25 'to_email': to_email,
26 'from_email': from_email
27 }
28 res_json = get_res(url_api, request_method, form_data=form_data)
29 return res_json
30
31
32 if __name__ == "__main__":
33 subject = "测试主体"
34 message = "测试内容"
35 to_email = "xxx@qq.com"
36 res = send_a_email(subject, message, to_email)
37 print(res)
2)获取当天的日志内容get_log_today:
1 import sys, os
2 import datetime
3 cur_dir = os.path.dirname(os.path.abspath(__file__))
4 proj_dir = os.path.dirname(cur_dir)
5 log_dir = os.path.join(proj_dir, 'logs')
6
7 sys.path.append(log_dir)
8
9 def get_date():
10 today=datetime.date.today() #'2018-01-01'
11 return str(today)
12
13
14 def get_log_today(file_name):
15 content = ''
16 today = get_date()
17
18 file_path = os.path.join(log_dir, file_name)
19 with open (file_path, 'r') as f:
20 for line in f.readlines():
21 if today in line:
22 content += line
23 return content
24
25
26
27 if __name__ == "__main__":
28 file_name = 'article_infos.log'
3)实现定期任务(用到了Python schedule库)
1 import sys, os
2 import schedule
3 import time
4
5 proj_dir = os.path.dirname(os.path.abspath(__file__))
6 utils_dir = os.path.join(proj_dir, "utils")
7 sys.path.append(utils_dir)
8
9
10 from get_logs import get_logs_today
11 from send_emails import send_a_email
12
13 def send_logs_today():
14 file_name = 'article_infos.log'
15 subject = "[今天的日志]来自lovelyhouse"
16 to_email = "xxx@qq.com"
17 message = get_logs_today(file_name)
18 res = send_a_email(subject, message, to_email)
19
20
21
22 schedule.every().day.at("18:29").do(send_logs_today)
23
24 while True:
25 schedule.run_pending()
26 time.sleep(1)
然后按时收到啦~撒花庆祝~
![4beb33f0922205ffbdbac5c40f6e331f.png](https://img-blog.csdnimg.cn/img_convert/4beb33f0922205ffbdbac5c40f6e331f.png)
总的来说,因为大体思路清晰也没什么太大的难点,就是步骤多一点,封装调用来来去去……
5、后续遗留
部署上还是偏手工,不够先进吧。
如何自动化部署,保证每次依赖和环境等等到了生产环境能够不遗漏?
再说吧!