基于Django的channels实时查看服务日志,类似tail命令

本文主要使用的技术栈:

  • django :Django documentation | Django documentation | Django
  • channels :Installation — Channels 4.0.0 documentation
  • vue3 :简介 | Vue.js
  • xterm: Xterm.js

主要实现功能:

通过websocket实时查看指定文件日志,类似tail -f 命令

一,channels安装及配置

 参考官方文档:Installation — Channels 4.0.0 documentation

 a.通过pip安装

python -m pip install -U channels["daphne"]

b.在django配置文件 INSTALLED_APPS 最开始添加 daphne

INSTALLED_APPS = (
    "daphne",  # 一定放到第一位
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.sites",
    ...
)

c.创建一个 message 的app

python3 manage.py startapp message

该操作会创建一个 message的目录,文件如下

message/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py

将该app注册到settings里面

INSTALLED_APPS = (
    "daphne",  # 一定放到第一位
    "message", # 刚才创建的app
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.sites",
    ...
)

d.新增路由文件 routing.py

from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r"ws/message/(?P[\w+|\-?]+)+/(?P\w+)$", consumers.RobotMessageConsumer.as_asgi()),
]

e.新增consumers文件consumers.py

import asyncio
import logging
import os

import aiofiles
from channels.generic.websocket import AsyncJsonWebsocketConsumer

logger = logging.getLogger(__name__)


class RobotMessageConsumer(AsyncJsonWebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super().__init__(args, kwargs)
        self.room_group_name = None
        self.disconnected = True

    async def connect(self):
        username = self.scope["url_route"]["kwargs"].get('username')
        token = self.scope["url_route"]["kwargs"].get('token')

        if username == token:
            self.disconnected = False
            self.room_group_name = f"message_{username}"
            # Join room group
            await self.channel_layer.group_add(self.room_group_name, self.channel_name)
            await self.accept()
        else:
            logger.error(f"username:{username} token:{token} auth failed")
            await self.close()

    async def disconnect(self, close_code):
        self.disconnected = True
        if self.room_group_name:
            await self.channel_layer.group_discard(self.room_group_name, self.channel_name)

    # Receive message from WebSocket
    async def receive_json(self, content, **kwargs):
        filepath = content.get('filepath')
        await self.async_handle_task(filepath)

    async def async_handle_task(self, filepath):
        while not self.disconnected:
            if not os.path.exists(filepath):
                await self.send_json({'message': '.', 'filepath': filepath})
                await asyncio.sleep(0.5)
            else:
                await self.send_task_log(filepath)
                break

    async def send_task_log(self, filepath):
        await self.send_json({'message': '\r\n'})
        try:
            logger.debug('file log path: {}'.format(filepath))
            async with aiofiles.open(filepath, 'rb') as log_f:
                await log_f.seek(0, os.SEEK_END)
                print(await log_f.tell())
                backup = min(4096 * 5, await log_f.tell())
                await log_f.seek(-backup, os.SEEK_END)
                while not self.disconnected:
                    data = await log_f.read(4096)
                    if data:
                        data = data.replace(b'\n', b'\r\n')
                        await self.send_json(
                            {'message': data.decode(errors='ignore'), 'filepath': filepath}
                        )
                    await asyncio.sleep(0.2)
        except OSError as e:
            logger.warning('file log path open failed: {}'.format(e))

f.修改asgi.py,增加路由配置

import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')

django_asgi_app = get_asgi_application()

import message.routing

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(
                message.routing.websocket_urlpatterns
            ))
        ),
    }
)

g.在settings.py中增加asgi 支持

ASGI_APPLICATION = "server.asgi.application"
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [f"redis://:[email protected]:6379/0"],
        },
    },
}

二,vue及配置

yarn add xterm
yarn add xterm-addon-fit

创建 MessageTail.vue文件,内容如下: 




基于Django的channels实时查看服务日志,类似tail命令_第1张图片

 

你可能感兴趣的:(vue,Django,技能,python,django,vue3,websocket,channels)