django 異步 查看 服務器日誌 | 利用 channels==2.0.2
阿新 • • 發佈:2018-05-09
channels 異步查看日誌 django django查看日誌 django 異步 查看 服務器日誌
實例 可以查看我編寫的這個項目:
https://github.com/hequan2017/chain
模塊
需要安裝以下模塊
安裝後會有一個版本號報錯,不影響
channels==2.0.2 channels-redis==2.1.0 amqp==1.4.9 anyjson==0.3.3 asgi-redis==1.4.3 asgiref==2.3.0 async-timeout==2.0.0 attrs==17.4.0 cd /tmp/ wget https://files.pythonhosted.org/packages/12/2a/e9e4fb2e6b2f7a75577e0614926819a472934b0b85f205ba5d5d2add54d0/Twisted-18.4.0.tar.bz2 tar xf Twisted-18.4.0.tar.bz2 cd Twisted-18.4.0 python3 setup.py install
啟動redis
原理
點擊 查看日誌頁面,啟動一個websocket連接,執行命令。
根據 登錄的用戶名,返回後端生成的消息,前端負責消費。
目錄
chain/
chain/
settings.py
asgi.py
consumers.py
routing.py
templates/
tasks/
tail.html
settings.py
# django-channels配置 CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [("127.0.0.1", 6379)], }, }, } # 配置ASGI ASGI_APPLICATION = "chain.routing.application"
consumers.py
from asgiref.sync import async_to_sync from channels.generic.websocket import WebsocketConsumer from channels.layers import get_channel_layer channel_layer = get_channel_layer() class EchoConsumer(WebsocketConsumer): def connect(self): # 創建channels group, 命名為:用戶名,並使用channel_layer寫入到redis async_to_sync(self.channel_layer.group_add)(self.scope[‘user‘].username, self.channel_name) # 返回給receive方法處理 self.accept() def receive(self, text_data): async_to_sync(self.channel_layer.group_send)( self.scope[‘user‘].username, { "type": "user.message", "text": text_data, }, ) def user_message(self, event): # 消費 self.send(text_data=event["text"]) def disconnect(self, close_code): async_to_sync(self.channel_layer.group_discard)(self.scope[‘user‘].username, self.channel_name)
asgi.py
import os
import django
from channels.routing import get_default_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "chain.settings")
django.setup()
application = get_default_application()
routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import URLRouter, ProtocolTypeRouter
from django.urls import path
from .consumers import EchoConsumer
application = ProtocolTypeRouter({
"websocket": AuthMiddlewareStack(
URLRouter([
path(r"ws/", EchoConsumer),
# path(r"stats/", StatsConsumer),
])
)
})
網頁設置:
邏輯:
- tail頁面 向後面 post 主機信息,要查看的日誌路徑
- 後端去利用 paramiko 執行命令,一直讀取此接口,先返回一遍信息,如有新日誌生成,再次返回給前端。
- 點擊停止,會修改一個環境變量, 上面paramiko監測到此環境變量為false後,就停止執行命令。
<a id="tail" class="btn btn-primary" type="submit">查看</a>
<a id="tail_stop" class="btn btn-danger" type="submit">不看了,必須點停止</a>
<div class="ibox-content" >
<pre id="output_append" ></pre>
</div>
$(function () {
$(document).on(‘click‘,‘#tail‘,function () {
$.ajax({
url: "{% url ‘tasks:tail_perform‘ %}",
timeout : 5000, //超時時間設置,單位毫秒
type: ‘POST‘,
data: $(‘.cmd_from‘).serialize(),
success: function (data) {
var obj = JSON.parse(data);
if (obj.status) {
toastr.success("執行成功!")
} else {
toastr.error(obj.error)
}
}
})
});
$(document).on(‘click‘,‘#tail_stop‘,function () {
$.ajax({
url: "{% url ‘tasks:tail_perform_stop‘ %}",
timeout : 5000, //超時時間設置,單位毫秒
type: ‘POST‘,
data: {"status":"stop"},
success: function (data) {
var obj = JSON.parse(data);
if (obj.status) {
toastr.success("停止成功!")
} else {
toastr.success("停止失敗!")
}
}
})
});
});
$(document).ready(function () {
CreateWebSocket();
});
function CreateWebSocket() {
var socket = new WebSocket(‘ws://‘ + window.location.host + ‘/ws/‘);
socket.onmessage = function (message) {
var result = JSON.parse(message.data);
var status = result.status;
var data = result.data;
var output_html = ‘‘;
if (status === 0) {
{# $(‘#output_append‘).empty();#}
output_html = data;
}
else if (status === 1) {
$(‘#output_append‘).empty();
output_html = data;
}
$("#output_append").prepend(output_html);
}
}
urls.py
path(‘tail.html‘, views.TasksTail.as_view(), name=‘tail‘),
path(‘tailperform.html‘, views.taskstailperform, name=‘tail_perform‘),
path(‘tailperform-stop.html‘, views.taskstailstopperform, name=‘tail_perform_stop‘),
views.py
def taillog(request, hostname, port, username, password, private, tail):
"""
執行 tail log 接口
"""
channel_layer = get_channel_layer()
user = request.user.username
os.environ["".format(user)] = "true"
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if password:
ssh.connect(hostname=hostname, port=port, username=username, password=decrypt_p(password))
else:
pkey = paramiko.RSAKey.from_private_key_file(private)
ssh.connect(hostname=hostname, port=port, username=username, pkey=pkey)
cmd = "tail " + tail
stdin, stdout, stderr = ssh.exec_command(cmd, get_pty=True)
for line in iter(stdout.readline, ""):
if os.environ.get("".format(user)) == ‘false‘:
break
result = {"status": 0, ‘data‘: line}
result_all = json.dumps(result)
async_to_sync(channel_layer.group_send)(user, {"type": "user.message", ‘text‘: result_all})
def taskstailperform(request):
"""
執行 tail_log 命令
"""
if request.method == "POST":
ret = {‘status‘: True, ‘error‘: None, }
name = Names.objects.get(username=request.user)
ids = request.POST.get(‘id‘)
tail = request.POST.get(‘tail‘, None)
if not ids or not tail:
ret[‘status‘] = False
ret[‘error‘] = "請選擇服務器,輸入參數及日誌地址."
return HttpResponse(json.dumps(ret))
obj = AssetInfo.objects.get(id=ids)
try:
taillog(request, obj.network_ip, obj.port, obj.user.username, obj.user.password, obj.user.private_key, tail)
except Exception as e:
ret[‘status‘] = False
ret[‘error‘] = "錯誤{0}".format(e)
logger.error(e)
return HttpResponse(json.dumps(ret))
def taskstailstopperform(request):
"""
執行 tail_log 命令
"""
if request.method == "POST":
ret = {‘status‘: True, ‘error‘: None, }
name = request.user.username
os.environ["".format(name)] = "false"
return HttpResponse(json.dumps(ret))
django 異步 查看 服務器日誌 | 利用 channels==2.0.2