NginxLog.vue 3.9 KB

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