91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Django如何實現web端tailf日志文件功能

發布時間:2021-07-10 11:42:00 來源:億速云 閱讀:254 作者:小新 欄目:開發技術

小編給大家分享一下Django如何實現web端tailf日志文件功能,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

效果圖

Django如何實現web端tailf日志文件功能

接著我們來看下具體的實現過程

技術實現

所有代碼均基于以下軟件版本:

  • python==3.6.3

  • django==2.2

  • channels==2.1.7

  • celery==4.3.0

celery4在windows下支持不完善,所以請 在linux下運行 測試

日志數據定義

我們只希望用戶能夠查詢固定的幾個日志文件,就不是用數據庫僅借助settings.py文件里寫全局變量來實現數據存儲

在settings.py里添加一個叫 TAILF 的變量,類型為字典,key標識文件的編號,value標識文件的路徑

TAILF = {
 1: '/ops/coffee/error.log',
 2: '/ops/coffee/access.log',
}

基礎Web頁面搭建

假設你已經創建好了一個叫tailf的app,并添加到了settings.py的INSTALLED_APPS中,app的目錄結構大概如下

tailf
 - migrations
 - __init__.py
 - __init__.py
 - admin.py
 - apps.py
 - models.py
 - tests.py
 - views.py

依然先構建一個標準的Django頁面,相關代碼如下

url:

from django.urls import path
from django.contrib.auth.views import LoginView,LogoutView
from tailf.views import tailf
urlpatterns = [
 path('tailf', tailf, name='tailf-url'),
 path('login', LoginView.as_view(template_name='login.html'), name='login-url'),
 path('logout', LogoutView.as_view(template_name='login.html'), name='logout-url'),
]

因為我們規定只有通過登錄的用戶才能查看日志,所以引入Django自帶的LoginView,logoutView幫助我們快速構建Login,Logout功能

指定了登錄模板使用 login.html ,它就是一個標準的登錄頁面,post傳入username和password兩個參數即可,不貼代碼了

view:

from django.conf import settings
from django.shortcuts import render
from django.contrib.auth.decorators import login_required

# Create your views here.
@login_required(login_url='/login')
def tailf(request):
 logDict = settings.TAILF
 return render(request, 'tailf/index.html', {"logDict": logDict})

引入了 login_required 裝飾器,來判斷用戶是否登錄,未登錄就給跳到 /login 登錄頁面

logDict去setting里取我們定義好的 TAILF 字典賦值,并傳遞給前端

template:

{% extends "base.html" %}

{% block content %}
<div class="col-sm-8">
 <select class="form-control" id="file">
 <option value="">選擇要監聽的日志</option>
 {% for k,v in logDict.items %}
 <option value="{{ k }}">{{ v }}</option>
 {% endfor %}
 </select>
</div>
<div class="col-sm-2">
 <input class="btn btn-success btn-block" type="button" onclick="connect()" value="開始監聽"/><br/>
</div>
<div class="col-sm-2">
 <input class="btn btn-warning btn-block" type="button" onclick="goclose()" value="終止監聽"/><br/>
</div>
<div class="col-sm-12">
 <textarea class="form-control" id="chat-log" disabled rows="20"></textarea>
</div>
{% endblock %}

前端拿到 TAILF 后通過循環的方式填充到select選擇框下,因為數據是字典格式,使用 logDict.items 的方式可以循環出字典的key和value

這樣一個日志監聽頁面就完成了,但還無法實現日志的監聽,繼續往下

集成Channels實現WebSocket

日志監聽功能主要的設計思路就是頁面跟后端服務器建立websocket長連接,后端通過celery異步執行while循環不斷的讀取日志文件然后發送到websocket的channel里,實現頁面上的實時顯示

接著我們來集成channels

先添加routing路由,直接修改 webapp/routing.py

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path, re_path
from chat.consumers import ChatConsumer
from tailf.consumers import TailfConsumer
application = ProtocolTypeRouter({
 'websocket': AuthMiddlewareStack(
 URLRouter([
 path('ws/chat/', ChatConsumer),
 re_path(r'^ws/tailf/(?P<id>\d+)/$', TailfConsumer),
 ])
 )
})

直接將路由信息寫入到了 URLRouter 里,注意路由信息的外層多了一個list,區別于上一篇中介紹的寫路由文件路徑的方式

頁面需要將監聽的日志文件傳遞給后端,我們使用routing正則 P<id>\d+ 傳文件ID給后端程序,后端程序拿到ID之后根據settings中指定的 TAILF 解析出日志路徑

routing的寫法跟Django中的url寫法完全一致,使用 re_path 匹配正則routing路由

添加consumer在 tailf/consumers.py 文件中

import json
from channels.generic.websocket import WebsocketConsumer
from tailf.tasks import tailf
class TailfConsumer(WebsocketConsumer):
 def connect(self):
 self.file_id = self.scope["url_route"]["kwargs"]["id"]
 self.result = tailf.delay(self.file_id, self.channel_name)
 print('connect:', self.channel_name, self.result.id)
 self.accept()
 def disconnect(self, close_code):
 # 中止執行中的Task
 self.result.revoke(terminate=True)
 print('disconnect:', self.file_id, self.channel_name)
 def send_message(self, event):
 self.send(text_data=json.dumps({
 "message": event["message"]
 }))

這里使用Channels的單通道模式,每一個新連接都會啟用一個新的channel,彼此互不影響,可以隨意終止任何一個監聽日志的請求

connect

我們知道 self.scope 類似于Django中的request,記錄了豐富的請求信息,通過 self.scope["url_route"]["kwargs"]["id"] 取出routing中正則匹配的日志ID

然后將 id 和 channel_name 傳遞給celery的任務函數tailf,tailf根據 id 取到日志文件的路徑,然后循環文件,將新內容根據 channel_name 寫入對應channel

disconnect

當websocket連接斷開的時候我們需要終止Celery的Task執行,以清除celery的資源占用

終止Celery任務使用到 revoke 指令,采用如下代碼來實現

self.result.revoke(terminate=True)

注意 self.result 是一個result對象,而非id

參數 terminate=True 的意思是是否立即終止Task,為True時無論Task是否正在執行都立即終止,為False(默認)時需要等待Task運行結束之后才會終止,我們使用了While循環不設置為True就永遠不會終止了

終止Celery任務的另外一種方法是:

from webapp.celery import app
app.control.revoke(result.id, terminate=True)
send_message

方便我們通過Django的view或者Celery的task調用給channel發送消息,官方也比較推薦這種方式

使用Celery異步循環讀取日志

上邊已經集成了Channels實現了WebSocket,但connect函數中的celery任務 tailf 還沒有實現,下邊來實現它

關于Celery的詳細內容可以看這篇文章: 《Django配置Celery執行異步任務和定時任務》 ,本文就不介紹集成使用以及細節原理,只講一下任務task

task實現代碼如下:

from __future__ import absolute_import
from celery import shared_task
import time
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
from django.conf import settings
@shared_task
def tailf(id, channel_name):
 channel_layer = get_channel_layer()
 filename = settings.TAILF[int(id)]
 try:
 with open(filename) as f:
 f.seek(0, 2)
 while True:
 line = f.readline()
 if line:
  print(channel_name, line)
  async_to_sync(channel_layer.send)(
  channel_name,
  {
  "type": "send.message",
  "message": "微信公眾號【運維咖啡吧】原創 版權所有 " + str(line)
  }
  )
 else:
  time.sleep(0.5)
 except Exception as e:
 print(e)

這里邊主要涉及到Channels中另一個非常重要的點: 從Channels的外部發送消息給Channel

其實 上篇文章 中檢查通道層是否能夠正常工作的時候使用的方法就是從外部給Channel通道發消息的示例,本文的具體代碼如下

async_to_sync(channel_layer.send)(
 channel_name,
 {
 "type": "send.message",
 "message": "微信公眾號【運維咖啡吧】原創 版權所有 " + str(line)
 }
)

channel_name對應于傳遞給這個任務的channel_name,發送消息給這個名字的channel

type對應于我們Channels的TailfConsumer類中的 send_message 方法,將方法中的 _ 換成 . 即可

message就是要發送給這個channel的具體信息

上邊是發送給單Channel的情況,如果是需要發送到Group的話需要使用如下代碼

async_to_sync(channel_layer.group_send)(
 group_name,
 {
 'type': 'chat.message',
 'message': '歡迎關注公眾號【運維咖啡吧】'
 }
)

只需要將發送單channel的 send 改為 group_send , channel_name 改為 group_name 即可

需要特別注意的是: 使用了channel layer之后一定要通過async_to_sync來異步執行

頁面添加WebSocket支持

后端功能都已經完成,我們最后需要添加前端頁面支持WebSocket

function connect() {
 if ( $('#file').val() ) {
 window.chatSocket = new WebSocket(
 'ws://' + window.location.host + '/ws/tailf/' + $('#file').val() + '/');

 chatSocket.onmessage = function(e) {
 var data = JSON.parse(e.data);
 var message = data['message'];
 document.querySelector('#chat-log').value += (message);
 // 跳轉到頁面底部
 $('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
 };

 chatSocket.onerror = function(e) {
 toastr.error('服務端連接異常!')
 };

 chatSocket.onclose = function(e) {
 toastr.error('websocket已關閉!')
 };
 } else {
 toastr.warning('請選擇要監聽的日志文件')
 }
 }

上一篇文章 中有詳細介紹過websocket的消息類型,這里不多介紹了

至此我們一個日志監聽頁面完成了,包含了完整的監聽功能,但還無法終止,接著看下面的內容

Web頁面主動斷開WebSocket

web頁面上“終止監聽”按鈕的主要邏輯就是觸發WebSocket的onclose方法,從而可以觸發Channels后端consumer的 disconnect 方法,進而終止Celery的循環讀取日志任務

前端頁面通過 .close() 可以直接觸發WebSocket關閉,當然你如果直接關掉頁面的話也會觸發WebSocket的onclose消息,所以不用擔心Celery任務無法結束的問題

function goclose() {
 console.log(window.chatSocket);

 window.chatSocket.close();
 window.chatSocket.onclose = function(e) {
 toastr.success('已終止日志監聽!')
 };
 }

至此我們包含完善功能的Tailf日志監聽、終止頁面就全部完成了

以上是“Django如何實現web端tailf日志文件功能”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宝清县| 丰原市| 和平县| 四子王旗| 行唐县| 阿合奇县| 金沙县| 宜昌市| 安达市| 开江县| 常熟市| 沙湾县| 和林格尔县| 镇江市| 武陟县| 松潘县| 杭锦后旗| 桂林市| 青岛市| 陕西省| 铁岭县| 厦门市| 巧家县| 景泰县| 依兰县| 大田县| 枞阳县| 云梦县| 金门县| 浦东新区| 大庆市| 上虞市| 阿坝| 雅江县| 景德镇市| 南丰县| 南通市| 河间市| 丹寨县| 扶绥县| 承德市|