NginxLog.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <script setup lang="ts">
  2. import {useGettext} from 'vue3-gettext'
  3. import ws from '@/lib/websocket'
  4. import {computed, nextTick, onMounted, onUnmounted, reactive, ref, watch} from 'vue'
  5. import ReconnectingWebSocket from 'reconnecting-websocket'
  6. import {useRoute, useRouter} from 'vue-router'
  7. import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
  8. import nginx_log from '@/api/nginx_log'
  9. import {debounce} from 'lodash'
  10. const {$gettext} = useGettext()
  11. const logContainer = ref(null)
  12. let websocket: ReconnectingWebSocket | WebSocket
  13. const route = useRoute()
  14. function logType() {
  15. return route.path.indexOf('access') > 0 ? 'access' : route.path.indexOf('error') > 0 ? 'error' : 'site'
  16. }
  17. const control = reactive({
  18. type: logType(),
  19. conf_name: route.query.conf_name,
  20. server_idx: parseInt(route.query.server_idx as string),
  21. directive_idx: parseInt(route.query.directive_idx as string)
  22. })
  23. function openWs() {
  24. websocket = ws('/api/nginx_log')
  25. websocket.onopen = () => {
  26. websocket.send(JSON.stringify({
  27. ...control
  28. }))
  29. }
  30. websocket.onmessage = (m: any) => {
  31. addLog(m.data + '\n')
  32. }
  33. }
  34. function addLog(data: string, prepend: boolean = false) {
  35. // const para = document.createElement('p')
  36. // para.appendChild(document.createTextNode(data.trim()))
  37. //
  38. // const node = (logContainer.value as any as Node)
  39. if (prepend) {
  40. buffer.value = data + buffer.value
  41. } else {
  42. buffer.value += data
  43. }
  44. nextTick(() => {
  45. const elem = (logContainer.value as any as Element)
  46. elem?.scroll({
  47. top: elem.scrollHeight,
  48. left: 0
  49. })
  50. })
  51. }
  52. const buffer = ref('')
  53. const page = ref(0)
  54. function init() {
  55. nginx_log.page(0, {
  56. conf_name: (route.query.conf_name as string),
  57. type: logType(),
  58. server_idx: parseInt(route.query.server_idx as string),
  59. directive_idx: parseInt(route.query.directive_idx as string)
  60. }).then(r => {
  61. page.value = r.page - 1
  62. addLog(r.content)
  63. })
  64. }
  65. onMounted(() => {
  66. init()
  67. openWs()
  68. })
  69. const auto_refresh = ref(true)
  70. watch(auto_refresh, (value) => {
  71. if (value) {
  72. openWs();
  73. (logContainer.value as any as Element).innerHTML = ''
  74. } else {
  75. websocket.close()
  76. }
  77. })
  78. watch(route, () => {
  79. init()
  80. control.type = logType();
  81. (logContainer.value as any as Element).innerHTML = ''
  82. nextTick(() => {
  83. websocket.send(JSON.stringify(control))
  84. })
  85. })
  86. watch(control, () => {
  87. (logContainer.value as any as Element).innerHTML = ''
  88. auto_refresh.value = true
  89. nextTick(() => {
  90. websocket.send(JSON.stringify(control))
  91. })
  92. })
  93. onUnmounted(() => {
  94. websocket.close()
  95. })
  96. const router = useRouter()
  97. const loading = ref(false)
  98. function on_scroll_log() {
  99. if (!loading.value && page.value > 0) {
  100. loading.value = true
  101. const elem = (logContainer.value as any as Element)
  102. if (elem?.scrollTop / elem?.scrollHeight < 0.333) {
  103. nginx_log.page(page.value, {
  104. conf_name: (route.query.conf_name as string),
  105. type: logType(),
  106. server_idx: 0,
  107. directive_idx: 0
  108. }).then(r => {
  109. page.value = r.page - 1
  110. addLog(r.content, true)
  111. }).finally(() => {
  112. loading.value = false
  113. })
  114. } else {
  115. loading.value = false
  116. }
  117. }
  118. }
  119. const filter = ref('')
  120. const computedBuffer = computed(() => {
  121. if (filter.value) {
  122. return buffer.value.split('\n').filter(line => line.match(filter.value)).join('\n')
  123. }
  124. return buffer.value
  125. })
  126. </script>
  127. <template>
  128. <a-card :title="$gettext('Nginx Log')" :bordered="false">
  129. <a-form layout="vertical">
  130. <a-form-item :label="$gettext('Auto Refresh')">
  131. <a-switch v-model:checked="auto_refresh"/>
  132. </a-form-item>
  133. <a-form-item :label="$gettext('Filter')">
  134. <a-input v-model:value="filter" style="max-width: 300px"/>
  135. </a-form-item>
  136. </a-form>
  137. <a-card>
  138. <pre class="nginx-log-container" ref="logContainer"
  139. @scroll="debounce(on_scroll_log,100, null)()" v-html="computedBuffer"/>
  140. </a-card>
  141. <footer-tool-bar v-if="control.type==='site'">
  142. <a-button @click="router.go(-1)">
  143. <translate>Back</translate>
  144. </a-button>
  145. </footer-tool-bar>
  146. </a-card>
  147. </template>
  148. <style lang="less">
  149. .nginx-log-container {
  150. height: 60vh;
  151. overflow: scroll;
  152. padding: 5px;
  153. margin-bottom: 0;
  154. font-size: 12px;
  155. line-height: 2;
  156. }
  157. </style>