mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
parent
460687928a
commit
e8ee373fc4
@ -434,7 +434,7 @@ func (b *BaseApi) ContainerLogs(c *gin.Context) {
|
||||
follow := c.Query("follow") == "true"
|
||||
tail := c.Query("tail")
|
||||
|
||||
if err := containerService.ContainerLogs(wsConn, container, since, tail, follow); err != nil {
|
||||
if err := containerService.ContainerLogs(wsConn, "container", container, since, tail, follow); err != nil {
|
||||
_ = wsConn.WriteMessage(1, []byte(err.Error()))
|
||||
return
|
||||
}
|
||||
@ -658,7 +658,7 @@ func (b *BaseApi) ComposeLogs(c *gin.Context) {
|
||||
follow := c.Query("follow") == "true"
|
||||
tail := c.Query("tail")
|
||||
|
||||
if err := containerService.ComposeLogs(wsConn, compose, since, tail, follow); err != nil {
|
||||
if err := containerService.ContainerLogs(wsConn, "compose", compose, since, tail, follow); err != nil {
|
||||
_ = wsConn.WriteMessage(1, []byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@ -55,7 +56,7 @@ type IContainerService interface {
|
||||
ContainerRename(req dto.ContainerRename) error
|
||||
ContainerLogClean(req dto.OperationWithName) error
|
||||
ContainerOperation(req dto.ContainerOperation) error
|
||||
ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error
|
||||
ContainerLogs(wsConn *websocket.Conn, containerType, container, since, tail string, follow bool) error
|
||||
ContainerStats(id string) (*dto.ContainerStats, error)
|
||||
Inspect(req dto.InspectReq) (string, error)
|
||||
DeleteNetwork(req dto.BatchDelete) error
|
||||
@ -67,7 +68,6 @@ type IContainerService interface {
|
||||
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
|
||||
|
||||
LoadContainerLogs(req dto.OperationWithNameAndType) string
|
||||
ComposeLogs(wsConn *websocket.Conn, composePath, since, tail string, follow bool) error
|
||||
}
|
||||
|
||||
func NewIContainerService() IContainerService {
|
||||
@ -592,11 +592,15 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, container, since, tail string, follow bool) error {
|
||||
func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType, container, since, tail string, follow bool) error {
|
||||
defer func() { wsConn.Close() }()
|
||||
if cmd.CheckIllegal(container, since, tail) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
command := fmt.Sprintf("docker logs %s", container)
|
||||
if containerType == "compose" {
|
||||
command = fmt.Sprintf("docker-compose -f %s logs", container)
|
||||
}
|
||||
if tail != "0" {
|
||||
command += " --tail " + tail
|
||||
}
|
||||
@ -608,6 +612,14 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, container, sinc
|
||||
}
|
||||
command += " 2>&1"
|
||||
cmd := exec.Command("bash", "-c", command)
|
||||
if !follow {
|
||||
stdout, _ := cmd.CombinedOutput()
|
||||
if err := wsConn.WriteMessage(websocket.TextMessage, stdout); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -615,23 +627,40 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, container, sinc
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
_ = cmd.Wait()
|
||||
}()
|
||||
var exitCh chan struct{}
|
||||
if follow {
|
||||
go func() {
|
||||
_, wsData, _ := wsConn.ReadMessage()
|
||||
if string(wsData) == "close conn" {
|
||||
exitCh <- struct{}{}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
n, err := stdout.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
select {
|
||||
case <-exitCh:
|
||||
return nil
|
||||
default:
|
||||
n, err := stdout.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return err
|
||||
}
|
||||
global.LOG.Errorf("read bytes from log failed, err: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
global.LOG.Errorf("read bytes from container log failed, err: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with container log to ws failed, err: %v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error) {
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gorilla/websocket"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -262,45 +261,3 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ComposeLogs(wsConn *websocket.Conn, composePath, since, tail string, follow bool) error {
|
||||
if cmd.CheckIllegal(composePath, since, tail) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
command := fmt.Sprintf("docker-compose -f %s logs", composePath)
|
||||
if tail != "0" {
|
||||
command += " --tail " + tail
|
||||
}
|
||||
if since != "all" {
|
||||
command += " --since " + since
|
||||
}
|
||||
if follow {
|
||||
command += " -f"
|
||||
}
|
||||
command += " 2>&1"
|
||||
cmd := exec.Command("bash", "-c", command)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
n, err := stdout.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
global.LOG.Errorf("read bytes from compose log failed, err: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
|
||||
global.LOG.Errorf("send message with compose log to ws failed, err: %v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-drawer v-model="open" :size="globalStore.isFullScreen ? '100%' : '50%'">
|
||||
<el-drawer v-model="open" :size="globalStore.isFullScreen ? '100%' : '50%'" :before-close="handleClose">
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('commons.button.log')" :resource="resource" :back="handleClose">
|
||||
<template #extra v-if="!mobile">
|
||||
@ -53,7 +53,7 @@
|
||||
<script lang="ts" setup>
|
||||
import i18n from '@/lang';
|
||||
import { dateFormatForName, downloadWithContent } from '@/utils/util';
|
||||
import { computed, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
|
||||
import { computed, reactive, ref, shallowRef, watch } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
@ -86,8 +86,8 @@ const logSearch = reactive({
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
terminalSocket.value!.send('close conn');
|
||||
open.value = false;
|
||||
terminalSocket.value?.close();
|
||||
};
|
||||
|
||||
function toggleFullscreen() {
|
||||
@ -180,10 +180,6 @@ const acceptParams = (props: DialogProps): void => {
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
terminalSocket.value?.close();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
|
@ -155,7 +155,7 @@ const onClean = async () => {
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
terminalSocket.value?.close();
|
||||
terminalSocket.value!.send('close conn');
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
|
@ -4,6 +4,7 @@
|
||||
v-model="logVisible"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="handleClose"
|
||||
:size="globalStore.isFullScreen ? '100%' : '50%'"
|
||||
>
|
||||
<template #header>
|
||||
@ -58,7 +59,7 @@
|
||||
/>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="logVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
@ -131,8 +132,8 @@ const loadTooltip = () => {
|
||||
return i18n.global.t('commons.button.' + (screenfull.isFullscreen ? 'quitFullscreen' : 'fullscreen'));
|
||||
};
|
||||
const handleClose = async () => {
|
||||
terminalSocket.value!.send('close conn');
|
||||
logVisible.value = false;
|
||||
terminalSocket.value.close();
|
||||
};
|
||||
watch(logVisible, (val) => {
|
||||
if (screenfull.isEnabled && !val && !mobile.value) screenfull.exit();
|
||||
@ -208,7 +209,7 @@ const acceptParams = (props: DialogProps): void => {
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
terminalSocket.value?.close();
|
||||
handleClose();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
|
Loading…
Reference in New Issue
Block a user