在上下文中使用大型生成器流式传输 Django 模板
Streaming Django template with large generator in context
我想在 Django 模板中呈现大型日志文件。
为此,模板呈现由从磁盘流式传输日志文件的生成器提供的各个行。
模板渲染在一段时间后停止,我不确定哪里出错了。
我的 Django 应用程序是 运行 gunicorn 和 nginx(配置如下)。
我可以看到相关响应 headers 已设置,所以我不知道为什么日志在 ~30-40 秒内停止呈现:
HTTP/1.1 200 OK
Server: nginx/1.17.4
Date: Wed, 12 Feb 2020 12:53:43 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
模板相关部分:
<div class="log-body">
{% for line in log %}
{{ line }}
{% endfor %}
</div>
views.py
中的渲染函数
def render_log(request):
log = read_log_file_gen("some_path.log")
if not log:
log = ["No logs found"]
template = loader.get_template('show_log.html')
context = {
'log': log,
}
return StreamingHttpResponse(template.render(context, request))
def read_log_file_gen(path):
_256KB = 256 * 1024
try:
with open(path) as f:
while True:
lines = f.readlines(_256KB)
if not lines:
break
yield "".join(lines)
except IOError:
return None
应用贯穿docker-compose:
version: '3'
services:
web:
build: ./app
command: gunicorn app.wsgi:application --bind 0.0.0.0:8000 -k gevent
volumes:
- static_volume:/usr/src/app/static
- /var/run/docker.sock:/var/run/docker.sock
- /mnt/data:/mnt/data
expose:
- 8000
env_file:
- ./env/.env.prod
nginx:
build: ./nginx
volumes:
- static_volume:/usr/src/app/static
ports:
- 8888:80
depends_on:
- web
volumes:
static_volume:
我找到了一种方法来做到这一点,但在这个过程中我意识到尝试在浏览器中显示大日志文件是个坏主意,因为其中一些确实非常大。
万一有人尝试这样做(大文件稍微少一点),请注意模板基本上是同步呈现的,因此上述方法实际上不起作用。
相反,您可以加载父模板,并在其中进行 AJAX 调用:
<script>
$(document).ready(function () {
$.ajax({
type: "GET",
url: "{% url 'myapp:logs' log.id %}",
dataType: "text/plain",
success: function (logText) {
$("#logText").replaceWith(logText)
}
});
</script>
url myapp:logs
处的函数然后可以使用 StreamingHttpResponse
:
from django.template import loader
def log(request, log_id):
log_gen = read_log_file_gen(f"{log_id}.log")
template = loader.get_template('mytemplates/log.html')
return StreamingHttpResponse(log_render_gen(template, log_gen))
def log_render_gen(template, log_gen):
for log in log_gen:
yield template.render({'log_line': log})
def read_log_file_gen(path):
_256KB = 256 * 1024
try:
with open(path) as f:
while True:
lines = f.readlines(_256KB)
if not lines:
break
yield "".join(lines)
except IOError:
return None
呈现 log.html
的异步结果放在 <pre>
中,所以我只按原样呈现日志文本:
{{ log_line }}
我想在 Django 模板中呈现大型日志文件。
为此,模板呈现由从磁盘流式传输日志文件的生成器提供的各个行。
模板渲染在一段时间后停止,我不确定哪里出错了。
我的 Django 应用程序是 运行 gunicorn 和 nginx(配置如下)。
我可以看到相关响应 headers 已设置,所以我不知道为什么日志在 ~30-40 秒内停止呈现:
HTTP/1.1 200 OK
Server: nginx/1.17.4
Date: Wed, 12 Feb 2020 12:53:43 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
模板相关部分:
<div class="log-body">
{% for line in log %}
{{ line }}
{% endfor %}
</div>
views.py
def render_log(request):
log = read_log_file_gen("some_path.log")
if not log:
log = ["No logs found"]
template = loader.get_template('show_log.html')
context = {
'log': log,
}
return StreamingHttpResponse(template.render(context, request))
def read_log_file_gen(path):
_256KB = 256 * 1024
try:
with open(path) as f:
while True:
lines = f.readlines(_256KB)
if not lines:
break
yield "".join(lines)
except IOError:
return None
应用贯穿docker-compose:
version: '3'
services:
web:
build: ./app
command: gunicorn app.wsgi:application --bind 0.0.0.0:8000 -k gevent
volumes:
- static_volume:/usr/src/app/static
- /var/run/docker.sock:/var/run/docker.sock
- /mnt/data:/mnt/data
expose:
- 8000
env_file:
- ./env/.env.prod
nginx:
build: ./nginx
volumes:
- static_volume:/usr/src/app/static
ports:
- 8888:80
depends_on:
- web
volumes:
static_volume:
我找到了一种方法来做到这一点,但在这个过程中我意识到尝试在浏览器中显示大日志文件是个坏主意,因为其中一些确实非常大。
万一有人尝试这样做(大文件稍微少一点),请注意模板基本上是同步呈现的,因此上述方法实际上不起作用。
相反,您可以加载父模板,并在其中进行 AJAX 调用:
<script>
$(document).ready(function () {
$.ajax({
type: "GET",
url: "{% url 'myapp:logs' log.id %}",
dataType: "text/plain",
success: function (logText) {
$("#logText").replaceWith(logText)
}
});
</script>
url myapp:logs
处的函数然后可以使用 StreamingHttpResponse
:
from django.template import loader
def log(request, log_id):
log_gen = read_log_file_gen(f"{log_id}.log")
template = loader.get_template('mytemplates/log.html')
return StreamingHttpResponse(log_render_gen(template, log_gen))
def log_render_gen(template, log_gen):
for log in log_gen:
yield template.render({'log_line': log})
def read_log_file_gen(path):
_256KB = 256 * 1024
try:
with open(path) as f:
while True:
lines = f.readlines(_256KB)
if not lines:
break
yield "".join(lines)
except IOError:
return None
呈现 log.html
的异步结果放在 <pre>
中,所以我只按原样呈现日志文本:
{{ log_line }}