66 "encoding/json"
77 "fmt"
88 "net/http"
9+ "runtime"
910 "strconv"
1011 "strings"
1112
@@ -721,7 +722,7 @@ func getJobLogData(ctx context.Context, client *github.Client, owner, repo strin
721722
722723 if returnContent {
723724 // Download and return the actual log content
724- content , originalLength , httpResp , err := downloadLogContent (url .String (), tailLines ) //nolint:bodyclose // Response body is closed in downloadLogContent, but we need to return httpResp
725+ content , originalLength , httpResp , err := downloadLogContent (ctx , url .String (), tailLines ) //nolint:bodyclose // Response body is closed in downloadLogContent, but we need to return httpResp
725726 if err != nil {
726727 // To keep the return value consistent wrap the response as a GitHub Response
727728 ghRes := & github.Response {
@@ -742,8 +743,7 @@ func getJobLogData(ctx context.Context, client *github.Client, owner, repo strin
742743 return result , resp , nil
743744}
744745
745- // downloadLogContent downloads the actual log content from a GitHub logs URL
746- func downloadLogContent (logURL string , tailLines int ) (string , int , * http.Response , error ) {
746+ func downloadLogContent (ctx context.Context , logURL string , tailLines int ) (string , int , * http.Response , error ) {
747747 httpResp , err := http .Get (logURL ) //nolint:gosec
748748 if err != nil {
749749 return "" , 0 , httpResp , fmt .Errorf ("failed to download logs: %w" , err )
@@ -758,40 +758,56 @@ func downloadLogContent(logURL string, tailLines int) (string, int, *http.Respon
758758 tailLines = 1000
759759 }
760760
761- const maxMemoryBytes = 5 * 1024 * 1024
762- var lines []string
761+ const maxLines = 50000
762+
763+ lines := make ([]string , maxLines )
763764 totalLines := 0
764- currentMemoryUsage := 0
765+ writeIndex := 0
765766
766767 scanner := bufio .NewScanner (httpResp .Body )
767768 scanner .Buffer (make ([]byte , 0 , 64 * 1024 ), 1024 * 1024 )
768769
769770 for scanner .Scan () {
770771 line := scanner .Text ()
771772 totalLines ++
772- lineSize := len (line ) + 1
773773
774- // Remove lines from the front until we have space for the new line
775- for currentMemoryUsage + lineSize > maxMemoryBytes && len (lines ) > 0 {
776- removedLineSize := len (lines [0 ]) + 1
777- currentMemoryUsage -= removedLineSize
778- lines = lines [1 :]
779- }
774+ lines [writeIndex ] = line
775+ writeIndex = (writeIndex + 1 ) % maxLines
780776
781- // Add the new line
782- lines = append ( lines , line )
783- currentMemoryUsage += lineSize
777+ if totalLines % 10000 == 0 {
778+ runtime . GC ( )
779+ }
784780 }
785781
786782 if err := scanner .Err (); err != nil {
787783 return "" , 0 , httpResp , fmt .Errorf ("failed to read log content: %w" , err )
788784 }
789785
790- if len (lines ) > tailLines {
791- lines = lines [len (lines )- tailLines :]
786+ var result []string
787+ linesInBuffer := totalLines
788+ if linesInBuffer > maxLines {
789+ linesInBuffer = maxLines
792790 }
793791
794- return strings .Join (lines , "\n " ), totalLines , httpResp , nil
792+ startIndex := 0
793+ if totalLines > maxLines {
794+ startIndex = writeIndex
795+ }
796+
797+ for i := 0 ; i < linesInBuffer ; i ++ {
798+ idx := (startIndex + i ) % maxLines
799+ if lines [idx ] != "" {
800+ result = append (result , lines [idx ])
801+ }
802+ }
803+
804+ if len (result ) > tailLines {
805+ result = result [len (result )- tailLines :]
806+ }
807+
808+ finalResult := strings .Join (result , "\n " )
809+
810+ return finalResult , totalLines , httpResp , nil
795811}
796812
797813// RerunWorkflowRun creates a tool to re-run an entire workflow run
0 commit comments