mirror of https://github.com/jumpserver/jumpserver
parent
a7316bc7c1
commit
79edff5fca
@ -1,5 +1,6 @@
|
|||||||
from .connect_methods import *
|
from .connect_methods import *
|
||||||
from .endpoint import *
|
from .endpoint import *
|
||||||
|
from .loki_log import *
|
||||||
from .status import *
|
from .status import *
|
||||||
from .storage import *
|
from .storage import *
|
||||||
from .terminal import *
|
from .terminal import *
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from common.permissions import OnlySuperUser
|
||||||
|
from common.utils import get_logger
|
||||||
|
from terminal import serializers
|
||||||
|
from terminal.mixin import LokiMixin
|
||||||
|
|
||||||
|
__all__ = ['LokiLogAPI', ]
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class LokiLogAPI(APIView, LokiMixin):
|
||||||
|
http_method_names = ['get', ]
|
||||||
|
permission_classes = [OnlySuperUser]
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
serializer = serializers.LokiLogSerializer(data=request.query_params)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
components = serializer.validated_data.get('components')
|
||||||
|
search = serializer.validated_data.get('search', '')
|
||||||
|
start = serializer.validated_data.get('start', )
|
||||||
|
end = serializer.validated_data.get('end', )
|
||||||
|
loki_logs = self.query_components_log(components, search, start, end)
|
||||||
|
return Response(data=loki_logs)
|
||||||
|
|
||||||
|
def query_components_log(self, components, search, start, end):
|
||||||
|
# 秒转纳秒
|
||||||
|
start_ns = int(start * 1e9)
|
||||||
|
end_ns = int(end * 1e9)
|
||||||
|
query = self.create_loki_query(components, search)
|
||||||
|
loki_client = self.get_loki_client()
|
||||||
|
loki_response = loki_client.query_range(query, start_ns, end_ns, limit=100)
|
||||||
|
return loki_response['data']['result']
|
@ -0,0 +1,15 @@
|
|||||||
|
from terminal.utils.loki_client import get_loki_client
|
||||||
|
|
||||||
|
__all__ = ['LokiMixin', ]
|
||||||
|
|
||||||
|
class LokiMixin:
|
||||||
|
|
||||||
|
def get_loki_client(self):
|
||||||
|
return get_loki_client()
|
||||||
|
|
||||||
|
def create_loki_query(self, components, search):
|
||||||
|
stream_selector = '{component!=""}'
|
||||||
|
if components:
|
||||||
|
stream_selector = '{component=~"%s"}' % components
|
||||||
|
query = f'{stream_selector} |="{search}"'
|
||||||
|
return query
|
@ -0,0 +1,14 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'LokiLogSerializer',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class LokiLogSerializer(serializers.Serializer):
|
||||||
|
components = serializers.CharField(required=False, )
|
||||||
|
start = serializers.IntegerField()
|
||||||
|
end = serializers.IntegerField(default=time.time)
|
||||||
|
search = serializers.CharField(required=False, default='')
|
@ -0,0 +1,57 @@
|
|||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from django.conf import settings
|
||||||
|
from websockets.sync.client import connect as ws_connect
|
||||||
|
|
||||||
|
|
||||||
|
def get_loki_client():
|
||||||
|
# TODO: 补充 auth 认证相关
|
||||||
|
return LokiClient(base_url=settings.LOKI_BASE_URL)
|
||||||
|
|
||||||
|
|
||||||
|
# https://grafana.com/docs/loki/latest/reference/loki-http-api/
|
||||||
|
|
||||||
|
class LokiClient(object):
|
||||||
|
query_range_url = '/loki/api/v1/query_range'
|
||||||
|
tail_url = '/loki/api/v1/tail'
|
||||||
|
|
||||||
|
def __init__(self, base_url: str):
|
||||||
|
self.base_url = base_url.rstrip('/')
|
||||||
|
|
||||||
|
def query_range(self, query, start, end, limit=100):
|
||||||
|
params = {
|
||||||
|
'query': query,
|
||||||
|
'start': start,
|
||||||
|
'end': end,
|
||||||
|
'limit': limit,
|
||||||
|
}
|
||||||
|
url = f"{self.base_url}{self.query_range_url}"
|
||||||
|
response = requests.get(url, params=params)
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(response.text)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def create_tail_ws(self, query, limit=100):
|
||||||
|
data = {'query': query, 'limit': limit}
|
||||||
|
params = urllib.parse.urlencode(data)
|
||||||
|
ws_url = f"ws://{self.base_url[7:]}"
|
||||||
|
if self.base_url.startswith('https://'):
|
||||||
|
ws_url = f"wss://{self.base_url[8:]}"
|
||||||
|
url = f"{ws_url}{self.tail_url}?{params}"
|
||||||
|
ws = ws_connect(url)
|
||||||
|
return LokiTailWs(ws)
|
||||||
|
|
||||||
|
|
||||||
|
class LokiTailWs(object):
|
||||||
|
|
||||||
|
def __init__(self, ws):
|
||||||
|
self._ws = ws
|
||||||
|
|
||||||
|
def messages(self):
|
||||||
|
for message in self._ws:
|
||||||
|
yield message
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self._ws:
|
||||||
|
self._ws.close()
|
Loading…
Reference in new issue