Tabla de contenido
descripción general
En el último capítulo, nos dimos cuenta de la visualización de la lista de implementación, etc., y cuando se agregaron, eliminaron o modificaron algunos recursos, la nueva lista se volvería a representar y cargar automáticamente.
Lo que este capítulo quiere lograr es capturar los registros del Contenedor (opcional) en el Pod a través de go-client y enviarlos continuamente a la consola.
extremo posterior
Los registros también se obtienen a través de la interfaz API.
Es solo que la interfaz define una conexión larga y no devuelve ningún dato.
Los parámetros requeridos son espacio de nombres, nombre de pod, nombre de contenedor
Luego obtenga rest.request de acuerdo con go-client
Luego obtenga el flujo de solicitud para obtener io.Reader
func (this *PodLogsCtl) GetLogs(c *gin.Context) {
ns := c.DefaultQuery("ns", "default")
podname := c.DefaultQuery("podname", "")
cname := c.DefaultQuery("cname", "")
req := this.Client.CoreV1().Pods(ns).GetLogs(podname, &v1.PodLogOptions{
Container: cname,
//follow为true代表的是流式获取,否则只返回单次日志
Follow: true,
})
//单次获取
//res, err := req.DoRaw(context.Background())
//goft.Error(err)
//return gin.H{
// "code": 20000,
// "data": string(res),
//}
//流式获取
//gin会给每个请求都起一个协程,不设超时时间就会阻塞在read,导致每刷新一次多一个协程
cc, _ := context.WithTimeout(context.Background(), time.Minute*5)
//通过request的流获取到ioReader
reader,_ := req.Stream(cc)
//延迟reader的关闭
defer reader.Close()
for {
b := make([]byte, 1024)
n, err := reader.Read(b)
if err != nil && err == io.EOF {
break
}
if n > 0 {
//长连接分段传输
c.Writer.Write(b[0:n])
c.Writer.(http.Flusher).Flush()
}
}
return
}
Luego, solo use segmentos de bytes para obtener del lector, y escriba esta parte de los datos en el escritor de contexto y empújelo con Flush, el front-end puede recibir la información
Interfaz
Siguiente vistazo a la parte delantera
Debido a que es una conexión larga, para la solicitud de front-end de la parte de depuración conjunta de front-end y back-end, mi enfoque es crear una conexión larga dedicada longrequest.js
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 0 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['X-Token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
La diferencia con el archivo original request.js es que el tiempo de espera de axios utilizado para la solicitud se establece en 0 (el valor predeterminado es 5000, 0 significa que no hay tiempo de espera)
Por lo tanto, en el archivo Vue, hay una introducción como esta
//这里使用了长连接,无超时时间
import request from '@/utils/longrequest';
He agregado una columna de operación a la lista de Pod, que tiene un enlace a la página de registro. Haga clic en él para ir a la interfaz de visualización de registros del Pod correspondiente. Esta interfaz está oculta en la barra de navegación del índice.
Las etiquetas se definen así:
<router-link :to="{name:'Podlogs',params:{ns:scope.row.NameSpace,name:scope.row.Name}}"> <el-link >日志<i class="el-icon-view el-icon--right"></i></el-link></router-link>
Se dirigió al componente llamado Podlogs y pasó el espacio de nombres y el nombre en los datos de la fila como parámetros al componente Vue.
En la función del sistema creado del componente Podlogs, debe recoger los parámetros enrutados
this.Name = this.$route.params.name
this.NameSpace = this.$route.params.ns
De acuerdo con estos parámetros, podemos solicitar nuevamente a la API de backend para obtener la lista de Contenedores en el Pod correspondiente, para que el usuario pueda seleccionar el contenedor que necesita para ver el Log en la interfaz.
Esto lleva a nuestro diseño de interfaz, que es muy simple. Posteriormente, la optimización de la visualización se puede realizar mediante la introducción de estilos de terceros.
<template>
<div>
<div style="padding-left: 20px;padding-top:30px">
容器: <el-select @change="containerChange" placeholder="选择容器"
v-model="selectedContainer">
<el-option v-for="c in containers "
:label="c.Name"
:value="c.Name"/>
</el-select>
</div>
<div class="logs">
{
{logs}}
</div>
</div>
</template>
<style>
.logs{
overflow: auto;
margin:10px auto;
min-height: 200px;
max-height: 400px;
border: solid 1px black;
background-color: #454545;
padding: 10px;
color:#27aa5e;
line-height: 21pt;
white-space: pre;
width: 90%
}
</style>
Entre ellos, cuando cambia el valor en el cuadro de selección del contenedor, la función vinculada se llamará automáticamente
Esta función nos solicitará que implementemos una buena conexión larga en el backend para obtener la API de registro. Ver la función a continuación
containerChange(){
const ns=this.NameSpace
const podname=this.Name
const cname=this.selectedContainer
request({
url: '/v1/pods/logs?ns=' + ns + '&podname=' +podname + '&cname=' +cname,
method: 'GET',
//长连接等待后端传输数据,常用于下载进度条
onDownloadProgress: e => {
const dataChunk = e.currentTarget.response;
this.logs+=dataChunk
}
});
}
Aquí solicitaremos a través de la solicitud que definimos anteriormente.
Vale la pena mencionar que aquí se usa el método onDownloadProgress. Este método se usa a menudo para mostrar la barra de progreso a través de una larga conexión entre el front-end y el back-end. También es aplicable aquí. Solo necesita agregar el contenido de registro obtenido. a los datos para la interpolación cuando se disparan las variables de la expresión.
lograr efecto
Resumir
La captura de registro del pod se puede realizar fácilmente a través de una conexión larga. En el próximo capítulo, usaremos la biblioteca websocket y xterm para realizar el terminal remoto del pod.