get logging

This commit is contained in:
Jonathan Baecker 2020-04-27 16:07:28 +02:00
parent 815e453acf
commit f4aa0ac02b
5 changed files with 170 additions and 60 deletions

View File

@ -46,6 +46,16 @@ def write_json(data):
json.dump(data, outfile, indent=4) json.dump(data, outfile, indent=4)
def read_log(type):
config = read_yaml()
log_path = config['logging']['log_path']
log_file = os.path.join(log_path, '{}.log'.format(type))
if os.path.isfile(log_file):
with open(log_file, 'r') as log:
return log.read().strip()
def sizeof_fmt(num, suffix='B'): def sizeof_fmt(num, suffix='B'):
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
if abs(num) < 1024.0: if abs(num) < 1024.0:
@ -61,7 +71,8 @@ class PlayoutService:
self.proc = None self.proc = None
def run_cmd(self): def run_cmd(self):
self.proc = run(self.cmd + self.service, stdout=PIPE, stderr=STDOUT, encoding="utf-8").stdout self.proc = run(self.cmd + self.service, stdout=PIPE, stderr=STDOUT,
encoding="utf-8").stdout
def start(self): def start(self):
self.cmd.append('start') self.cmd.append('start')
@ -86,8 +97,8 @@ class PlayoutService:
return self.proc.replace('\n', '') return self.proc.replace('\n', '')
def log(self): def log(self):
self.cmd = ['sudo', '/bin/systemctl', 'journalctl', self.cmd = ['sudo', '/bin/journalctl', '-n', '1000', '-u']
'-n', '1000', '-u'] + self.service
self.run_cmd() self.run_cmd()
return self.proc return self.proc

View File

@ -12,7 +12,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from .utils import (PlayoutService, SystemStats, get_media_path, read_json, from .utils import (PlayoutService, SystemStats, get_media_path, read_json,
read_yaml, write_json, write_yaml) read_yaml, write_json, write_yaml, read_log)
class CurrentUserView(APIView): class CurrentUserView(APIView):
@ -104,6 +104,22 @@ class SystemCtl(APIView):
return Response({"success": False}) return Response({"success": False})
class LogReader(APIView):
def get(self, request, *args, **kwargs):
if 'type' in request.GET.dict():
type = request.GET.dict()['type']
log = read_log(type)
if log:
return Response({'log': log})
else:
return Response({
"success": False,
"error": "PLayout log file not found!"})
else:
return Response({"success": False})
class Playlist(APIView): class Playlist(APIView):
""" """
read and write config from ffplayout engine read and write config from ffplayout engine

View File

@ -35,6 +35,7 @@ urlpatterns = [
path('api/system/', views.SystemCtl.as_view()), path('api/system/', views.SystemCtl.as_view()),
path('api/playlist/', views.Playlist.as_view()), path('api/playlist/', views.Playlist.as_view()),
path('api/stats/', views.Statistics.as_view()), path('api/stats/', views.Statistics.as_view()),
path('api/log/', views.LogReader.as_view()),
path('api/current/user/', views.CurrentUserView.as_view()), path('api/current/user/', views.CurrentUserView.as_view()),
path('api/media/', views.Media.as_view()), path('api/media/', views.Media.as_view()),
re_path(r'^api/media/upload/(?P<filename>[^/]+)$', views.FileUpload.as_view()), re_path(r'^api/media/upload/(?P<filename>[^/]+)$', views.FileUpload.as_view()),

View File

@ -397,7 +397,7 @@ export default {
{ headers: { Authorization: 'Bearer ' + this.$store.state.auth.jwtToken } } { headers: { Authorization: 'Bearer ' + this.$store.state.auth.jwtToken } }
) )
await this.getStatus() setTimeout(() => { this.getStatus() }, 1000)
}, },
async getPlaylist () { async getPlaylist () {
await this.$store.dispatch('auth/inspectToken') await this.$store.dispatch('auth/inspectToken')

View File

@ -1,75 +1,82 @@
<template> <template>
<div style="height:100%;"> <div>
<Menu /> <Menu />
<div class="logging"> <b-card no-body>
1<br> <b-tabs pills card vertical>
2<br> <b-tab title="Playout" active @click="getLog('ffplayout')">
3<br> <b-container class="log-container">
4<br> <!-- eslint-disable-next-line -->
5<br> <pre v-if="currentLog" :inner-html.prop="currentLog | formatStr" class="log-content" />
6<br> </b-container>
7<br> </b-tab>
8<br> <b-tab title="Decoder" @click="getLog('decoder')">
9<br> <b-container class="log-container">
10<br> <!-- eslint-disable-next-line -->
11<br> <pre v-if="currentLog" :inner-html.prop="currentLog | formatStr" class="log-content" />
12<br> </b-container>
13<br> </b-tab>
14<br> <b-tab title="Encoder" @click="getLog('encoder')">
15<br> <b-container class="log-container">
16<br> <!-- eslint-disable-next-line -->
17<br> <pre v-if="currentLog" :inner-html.prop="currentLog | formatStr" class="log-content" />
18<br> </b-container>
19<br> </b-tab>
20<br> <b-tab title="System" @click="getSystemLog()">
1<br> <b-container class="log-container">
2<br> <!-- eslint-disable-next-line -->
3<br> <pre v-if="currentLog" :inner-html.prop="currentLog | formatStr" class="log-content" />
4<br> </b-container>
5<br> </b-tab>
6<br> </b-tabs>
7<br> </b-card>
8<br> <Login :show="showLogin" />
9<br>
10<br>
11<br>
12<br>
13<br>
14<br>
15<br>
16<br>
17<br>
18<br>
19<br>
20<br>
10<br>
11<br>
12<br>
13<br>
14<br>
15<br>
16<br>
17<br>
18<br>
19<br>
20<br>
</div>
</div> </div>
</template> </template>
<script> <script>
// import { mapState } from 'vuex' // import { mapState } from 'vuex'
import Menu from '@/components/Menu.vue' import Menu from '@/components/Menu.vue'
import Login from '@/components/Login.vue'
export default { export default {
name: 'Media', name: 'Media',
components: { components: {
Menu Menu,
Login
},
filters: {
formatStr (text) {
return text
.replace(/("\[.*")/g, '<span class="log-cmd">$1</span>')
.replace(/("\/.*")/g, '<span class="log-path">$1</span>')
.replace(/(\/[\w\d.\-/]+\n)/g, '<span class="log-path">$1</span>')
.replace(/((tcp|https?):\/\/[\w\d.:]+)/g, '<span class="log-url">$1</span>')
.replace(/(\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[0-9,.]+\])/g, '<span class="log-time">$1</span>')
.replace(/\[INFO\]/g, '<span class="log-info">[INFO]</span>')
.replace(/\[WARNING\]/g, '<span class="log-warning">[WARNING]</span>')
.replace(/\[ERROR\]/g, '<span class="log-error">[ERROR]</span>')
.replace(/\[DEBUG\]/g, '<span class="log-debug">[DEBUG]</span>')
}
},
async asyncData ({ app, store }) {
await store.dispatch('auth/inspectToken')
let login = false
if (!store.state.auth.isLogin) {
login = true
}
return {
showLogin: login
}
}, },
data () { data () {
return { return {
currentLog: null
} }
}, },
@ -77,16 +84,91 @@ export default {
}, },
async created () { async created () {
await this.getLog('ffplayout')
}, },
methods: { methods: {
async getLog (type) {
await this.$store.dispatch('auth/inspectToken')
const response = await this.$axios.get(
`api/log/?type=${type}`,
{ headers: { Authorization: 'Bearer ' + this.$store.state.auth.jwtToken } }
)
if (response.data.log) {
this.currentLog = response.data.log
}
},
async getSystemLog () {
await this.$store.dispatch('auth/inspectToken')
const response = await this.$axios.post(
'api/system/',
{ run: 'log' },
{ headers: { Authorization: 'Bearer ' + this.$store.state.auth.jwtToken } }
)
console.log(response.data.data)
if (response.data.data) {
this.currentLog = response.data.data
}
}
} }
} }
</script> </script>
<style> <style>
.col-auto {
width: 122px;
}
.tab-content {
max-width: calc(100% - 122px);
}
.log-container {
background: #1d2024;
max-width: 95%;
padding: 1em;
}
.logging { .logging {
height: 50%; height: 50%;
overflow: auto; overflow: auto;
} }
.log-content {
color: #ececec;
}
.log-time {
color: #a7a7a7;
}
.log-info {
color: #51d1de;
}
.log-warning {
color: #e4a428;
}
.log-error {
color: #e42e28;
}
.log-debug {
color: #23e493;
}
.log-path {
color: #e366cf;
}
.log-url {
color: #e3d666;
}
.log-cmd {
color: #f1aa77;
}
</style> </style>