66 "encoding/base64"
77 "encoding/json"
88 "fmt"
9+ "github.com/gin-gonic/gin"
910 "io"
1011 "net/http"
1112 "net/url"
@@ -19,9 +20,6 @@ import (
1920 "sync"
2021 "syscall"
2122 "time"
22- "unicode/utf8"
23-
24- "github.com/gin-gonic/gin"
2523
2624 "github.com/pkg/errors"
2725
@@ -44,7 +42,6 @@ import (
4442 "github.com/docker/docker/api/types/volume"
4543 "github.com/docker/docker/client"
4644 "github.com/docker/go-connections/nat"
47- "github.com/gorilla/websocket"
4845 v1 "github.com/opencontainers/image-spec/specs-go/v1"
4946 "github.com/shirou/gopsutil/v3/cpu"
5047 "github.com/shirou/gopsutil/v3/mem"
@@ -74,7 +71,6 @@ type IContainerService interface {
7471 ContainerCommit (req dto.ContainerCommit ) error
7572 ContainerLogClean (req dto.OperationWithName ) error
7673 ContainerOperation (req dto.ContainerOperation ) error
77- ContainerLogs (wsConn * websocket.Conn , containerType , container , since , tail string , follow bool ) error
7874 DownloadContainerLogs (containerType , container , since , tail string , c * gin.Context ) error
7975 ContainerStats (id string ) (* dto.ContainerStats , error )
8076 Inspect (req dto.InspectReq ) (string , error )
@@ -87,6 +83,8 @@ type IContainerService interface {
8783 Prune (req dto.ContainerPrune ) (dto.ContainerPruneReport , error )
8884
8985 LoadContainerLogs (req dto.OperationWithNameAndType ) string
86+
87+ StreamLogs (ctx * gin.Context , params dto.StreamLog )
9088}
9189
9290func NewIContainerService () IContainerService {
@@ -794,87 +792,87 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
794792 return nil
795793}
796794
797- func (u * ContainerService ) ContainerLogs (wsConn * websocket.Conn , containerType , container , since , tail string , follow bool ) error {
798- defer func () { wsConn .Close () }()
799- if cmd .CheckIllegal (container , since , tail ) {
800- return buserr .New (constant .ErrCmdIllegal )
795+ func (u * ContainerService ) StreamLogs (ctx * gin.Context , params dto.StreamLog ) {
796+ messageChan := make (chan string , 1024 )
797+ errorChan := make (chan error , 1 )
798+
799+ go collectLogs (params , messageChan , errorChan )
800+
801+ ctx .Stream (func (w io.Writer ) bool {
802+ select {
803+ case msg , ok := <- messageChan :
804+ if ! ok {
805+ return false
806+ }
807+ _ , err := fmt .Fprintf (w , "data: %v\n \n " , msg )
808+ if err != nil {
809+ return false
810+ }
811+ return true
812+ case err := <- errorChan :
813+ _ , err = fmt .Fprintf (w , "data: {\" event\" : \" error\" , \" data\" : \" %s\" }\n \n " , err .Error ())
814+ if err != nil {
815+ return false
816+ }
817+ return false
818+ case <- ctx .Request .Context ().Done ():
819+ return false
820+ }
821+ })
822+ }
823+
824+ func collectLogs (params dto.StreamLog , messageChan chan <- string , errorChan chan <- error ) {
825+ defer close (messageChan )
826+ defer close (errorChan )
827+
828+ var cmdArgs []string
829+ if params .Type == "compose" {
830+ cmdArgs = []string {"compose" , "-f" , params .Compose }
801831 }
802- commandName := "docker"
803- commandArg := []string {"logs" , container }
804- if containerType == "compose" {
805- commandArg = []string {"compose" , "-f" , container , "logs" }
832+ cmdArgs = append (cmdArgs , "logs" )
833+ if params .Follow {
834+ cmdArgs = append (cmdArgs , "-f" )
806835 }
807- if tail != "0" {
808- commandArg = append (commandArg , "--tail" )
809- commandArg = append (commandArg , tail )
836+ if params .Tail != "all" {
837+ cmdArgs = append (cmdArgs , "--tail" , params .Tail )
810838 }
811- if since != "all" {
812- commandArg = append (commandArg , "--since" )
813- commandArg = append (commandArg , since )
839+ if params .Since != "all" {
840+ cmdArgs = append (cmdArgs , "--since" , params .Since )
814841 }
815- if follow {
816- commandArg = append (commandArg , "-f" )
817- }
818- if ! follow {
819- cmd := exec .Command (commandName , commandArg ... )
820- cmd .Stderr = cmd .Stdout
821- stdout , _ := cmd .CombinedOutput ()
822- if ! utf8 .Valid (stdout ) {
823- return errors .New ("invalid utf8" )
824- }
825- if err := wsConn .WriteMessage (websocket .TextMessage , stdout ); err != nil {
826- global .LOG .Errorf ("send message with log to ws failed, err: %v" , err )
827- }
828- return nil
842+ if params .Container != "" {
843+ cmdArgs = append (cmdArgs , params .Container )
829844 }
845+ cmd := exec .Command ("docker" , cmdArgs ... )
830846
831- cmd := exec .Command (commandName , commandArg ... )
832847 stdout , err := cmd .StdoutPipe ()
833848 if err != nil {
834- _ = cmd . Process . Signal ( syscall . SIGTERM )
835- return err
849+ errorChan <- fmt . Errorf ( "failed to get stdout pipe: %v" , err )
850+ return
836851 }
837- cmd .Stderr = cmd .Stdout
838852 if err := cmd .Start (); err != nil {
839- _ = cmd . Process . Signal ( syscall . SIGTERM )
840- return err
853+ errorChan <- fmt . Errorf ( "failed to start command: %v" , err )
854+ return
841855 }
842- exitCh := make (chan struct {})
843- go func () {
844- _ , wsData , _ := wsConn .ReadMessage ()
845- if string (wsData ) == "close conn" {
846- _ = cmd .Process .Signal (syscall .SIGTERM )
847- exitCh <- struct {}{}
848- }
849- }()
850856
851- go func () {
852- buffer := make ([]byte , 1024 )
853- for {
854- select {
855- case <- exitCh :
856- return
857- default :
858- n , err := stdout .Read (buffer )
859- if err != nil {
860- if err == io .EOF {
861- return
862- }
863- global .LOG .Errorf ("read bytes from log failed, err: %v" , err )
864- return
865- }
866- if ! utf8 .Valid (buffer [:n ]) {
867- continue
868- }
869- if err = wsConn .WriteMessage (websocket .TextMessage , buffer [:n ]); err != nil {
870- global .LOG .Errorf ("send message with log to ws failed, err: %v" , err )
871- return
872- }
873- }
857+ scanner := bufio .NewScanner (stdout )
858+ lineNumber := 0
859+
860+ for scanner .Scan () {
861+ lineNumber ++
862+ message := scanner .Text ()
863+ select {
864+ case messageChan <- message :
865+ case <- time .After (time .Second ):
866+ errorChan <- fmt .Errorf ("message channel blocked" )
867+ return
874868 }
875- }()
876- _ = cmd .Wait ()
877- return nil
869+ }
870+
871+ if err := scanner .Err (); err != nil {
872+ errorChan <- fmt .Errorf ("scanner error: %v" , err )
873+ return
874+ }
875+ cmd .Wait ()
878876}
879877
880878func (u * ContainerService ) DownloadContainerLogs (containerType , container , since , tail string , c * gin.Context ) error {
0 commit comments