# -*- coding: utf-8 -*-
"""
@author: [email protected]
@version: 1.0.0
@file: log.py
@time: 2023/2/23 20:00
"""
import logging
def create_logger():
logger = logging.getLogger("app")
formatter = logging.Formatter("%(asctime)s[%(levelname)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
file_handler = logging.FileHandler("app.log", encoding="utf8")
file_handler.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
get()
方法提供从队列批量取消息功能。r3.py
# -*- coding: utf-8 -*-
"""
@author: [email protected]
@version: 1.0.0
@file: r3.py
@time: 2023/2/23 20:25
"""
import logging
import re
import datetime
import queue
mq = queue.Queue()
class R3:
logger = logging.getLogger("app")
@staticmethod
def put(item: str, level="INFO"):
dtf = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
mq.put(f"{dtf}[{level}]: {item}")
@staticmethod
def debug(item, log=True):
R3.put(item, level="DEBUG")
if log:
R3.logger.debug(item)
@staticmethod
def info(item, log=True):
R3.put(item, level="INFO")
if log:
R3.logger.info(item)
@staticmethod
def warning(item, log=True):
R3.put(item, level="WARNING")
if log:
R3.logger.warning(item)
@staticmethod
def error(item, log=True):
R3.put(item, level="ERROR")
if log:
R3.logger.error(item)
@staticmethod
def critical(item, log=True):
R3.put(item, level="CRITICAL")
if log:
R3.logger.critical(item)
@staticmethod
def get(count):
data = []
if mq.empty():
return data
pattern = re.compile(r"^.*?\[(INFO|ERROR|WARNING|DEBUG)]:.*$")
try:
for _ in range(count):
item = mq.get_nowait()
mq.task_done()
match = re.match(pattern, item)
message_level = match.group(1) if match else "INFO"
data.append({"message": item, "level": message_level})
return data
except queue.Empty:
return data
r3 = R3
main.py
# -*- coding:utf-8 -*-
"""
@author: [email protected]
@version: 1.0.0
@file: main.py
@time: 2023/2/23 19:58
"""
import random
import time
import threading
from flask import Flask, request, render_template
from faker import Faker
from log import create_logger
from r3 import r3
fake = Faker()
def simulated_generation_logs():
"""
模拟生成后台日志,作为生产者
实际生产环境中,各个业务逻辑产生日志
:return:
"""
def wrap():
while True:
sentence = fake.sentence()
func = random.choice([r3.info, r3.warning, r3.error]) # 生成随机等级的日志
func(sentence)
time.sleep(1)
thread = threading.Thread(target=wrap, name="simulated_generation_logs_thread", daemon=True)
thread.start()
def create_app():
app = Flask(__name__)
# 创建日志
create_logger()
@app.get("/generate")
def generate_logs():
# 模拟生成日志
simulated_generation_logs()
return {"code": 200, "data": None, "msg": "ok"}
@app.get("/message")
def get_message():
count = request.args.get("count", 50)
data = r3.get(count)
return {"code": 200, "data": data, "msg": "ok"}
@app.get("/")
def index_view():
return render_template("index.html")
return app
if __name__ == "__main__":
app = create_app()
app.run("127.0.0.1", port=5000, debug=True)
- 路由
/generate
: 将会产生一个线程,不断的产生日志消息(消息等级随机),记录在日志文件,并同时加入队列。在实际业务中,日志message由实际业务流程中产生。- 路由
/message
: 从服务器批量获取消息
启动服务后访问/generate
,让服务器不断产生日志,可以看到请求成功,并且后端成功产生日志记录
访问/message
,看能否正常取到后端服务器日志。可以看到是能够取到日志的,说明接口没问题,接下来就是第四步了。在前端展示日志
此步的原理就是在前端利用定时器和ajax,不断的请求
/message
接口,成功返回数据后异步渲染在前端页面,直接上代码。其中jquery资源在网上自己下载。代码整体目录结构如下:
index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页title>
<script type="text/javascript" src="/static/jquery-2.1.1.min.js">script>
<style>
p{
margin-block-start: 0;
margin-block-end: 0;
}
style>
head>
<body style="background-color: #999999;">
<div class="main-body" style="background-color: #fff;width: 1000px;height: 95vh;overflow-y: scroll;margin: 20px auto;padding: 20px">
div>
<script>
let main = $(".main-body");
get_message()
setInterval(get_message, 2000)
function get_message() {
$.ajax({
url: "/message",
type: "get",
contentType: "application/json",
async: true,
success: (res)=>{
console.log(res);
if(res.code ===200) {
res.data.forEach(function (item, idx) {
let elem = $(`${item.message}`)
if (item.level === "INFO") {
elem.css("color", "#11B45BFF")
} else if (item.level === "ERROR") {
elem.css("color", "#ef2309")
} else {
elem.css("color", "#f1a506")
}
main.append(elem)
})
}
}
})
}
script>
body>
html>
以上,就是通过队列实现Flask框架下后端日志在前端的实时输出渲染。
思路采用了消费者和生产者模式,服务器后端server作为日志Message的生产者,前端浏览器(Ajax请求)作为消费者。
原文地址:https://liyinbing.cn/article/det/49
整个代码地址:https://gitee.com/Bingo11/my-projects/tree/master/Web/Flask%E5%9F%BA%E4%BA%8E%E9%98%9F%E5%88%97Queue%E5%AE%9E%E7%8E%B0%E5%90%8E%E7%AB%AF%E6%97%A5%E5%BF%97%E5%9C%A8%E5%89%8D%E7%AB%AF%E5%AE%9E%E6%97%B6%E5%B1%95%E7%A4%BA