- Notifications
You must be signed in to change notification settings - Fork2.7k
Add actions job log buffer and profiler#866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
8a5efb9
e6ef962
271c7c2
e65c0f0
ade3852
5a76cbd
8e60fb2
e1c3143
75dc8e7
b128e44
88d16d2
4bf84b2
6b8f2ba
8002fbd
52e531e
8f85398
0d19480
f104e67
9d273b9
4e43327
2ff2d4f
c6f5f7f
1c1061c
75b8c94
71bfac8
a43b03c
d9c8825
ec070ee
0434b7e
fb301c6
106d802
516c0f7
6c3c31a
82d4ce2
e40e289
4310db8
e0767fe
9677c07
ed6bc2d
696e5fd
10e1995
2eb2e16
47aaa01
6b910ff
4a673c9
556a41c
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,215 @@ | ||||||||||||||||||||||||||||
package profiler | ||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||
"context" | ||||||||||||||||||||||||||||
"fmt" | ||||||||||||||||||||||||||||
"os" | ||||||||||||||||||||||||||||
"runtime" | ||||||||||||||||||||||||||||
"strconv" | ||||||||||||||||||||||||||||
"time" | ||||||||||||||||||||||||||||
"log/slog" | ||||||||||||||||||||||||||||
"math" | ||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||
// Profile represents performance metrics for an operation | ||||||||||||||||||||||||||||
type Profile struct { | ||||||||||||||||||||||||||||
Operation string `json:"operation"` | ||||||||||||||||||||||||||||
Duration time.Duration `json:"duration_ns"` | ||||||||||||||||||||||||||||
MemoryBefore uint64 `json:"memory_before_bytes"` | ||||||||||||||||||||||||||||
MemoryAfter uint64 `json:"memory_after_bytes"` | ||||||||||||||||||||||||||||
MemoryDelta int64 `json:"memory_delta_bytes"` | ||||||||||||||||||||||||||||
LinesCount int `json:"lines_count,omitempty"` | ||||||||||||||||||||||||||||
BytesCount int64 `json:"bytes_count,omitempty"` | ||||||||||||||||||||||||||||
Timestamp time.Time `json:"timestamp"` | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
// String returns a human-readable representation of the profile | ||||||||||||||||||||||||||||
func (p *Profile) String() string { | ||||||||||||||||||||||||||||
return fmt.Sprintf("[%s] %s: duration=%v, memory_delta=%+dB, lines=%d, bytes=%d", | ||||||||||||||||||||||||||||
p.Timestamp.Format("15:04:05.000"), | ||||||||||||||||||||||||||||
p.Operation, | ||||||||||||||||||||||||||||
p.Duration, | ||||||||||||||||||||||||||||
p.MemoryDelta, | ||||||||||||||||||||||||||||
p.LinesCount, | ||||||||||||||||||||||||||||
p.BytesCount, | ||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
func safeMemoryDelta(after, before uint64) int64 { | ||||||||||||||||||||||||||||
if after > math.MaxInt64 || before > math.MaxInt64 { | ||||||||||||||||||||||||||||
if after >= before { | ||||||||||||||||||||||||||||
diff := after - before | ||||||||||||||||||||||||||||
if diff > math.MaxInt64 { | ||||||||||||||||||||||||||||
return math.MaxInt64 | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
return int64(diff) | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
diff := before - after | ||||||||||||||||||||||||||||
if diff > math.MaxInt64 { | ||||||||||||||||||||||||||||
return -math.MaxInt64 | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
return -int64(diff) | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
return int64(after) - int64(before) | ||||||||||||||||||||||||||||
CopilotAI |
returnint64(after)-int64(before) | |
ifafter>=before { | |
diff:=after-before | |
ifdiff> math.MaxInt64 { | |
returnmath.MaxInt64 | |
} | |
returnint64(diff) | |
} | |
diff:=before-after | |
ifdiff> math.MaxInt64 { | |
return-math.MaxInt64 | |
} | |
return-int64(diff) |
Copilot uses AI. Check for mistakes.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package buffer | ||
import ( | ||
"bufio" | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
) | ||
mattdholloway marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
// ProcessResponseAsRingBufferToEnd reads the body of an HTTP response line by line, | ||
// storing only the last maxJobLogLines lines using a ring buffer (sliding window). | ||
// This efficiently retains the most recent lines, overwriting older ones as needed. | ||
// | ||
// Parameters: | ||
// | ||
//httpResp: The HTTP response whose body will be read. | ||
//maxJobLogLines: The maximum number of log lines to retain. | ||
// | ||
// Returns: | ||
// | ||
//string: The concatenated log lines (up to maxJobLogLines), separated by newlines. | ||
//int: The total number of lines read from the response. | ||
//*http.Response: The original HTTP response. | ||
//error: Any error encountered during reading. | ||
// | ||
// The function uses a ring buffer to efficiently store only the last maxJobLogLines lines. | ||
// If the response contains more lines than maxJobLogLines, only the most recent lines are kept. | ||
func ProcessResponseAsRingBufferToEnd(httpResp *http.Response, maxJobLogLines int) (string, int, *http.Response, error) { | ||
lines := make([]string, maxJobLogLines) | ||
validLines := make([]bool, maxJobLogLines) | ||
totalLines := 0 | ||
writeIndex := 0 | ||
scanner := bufio.NewScanner(httpResp.Body) | ||
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024) | ||
mattdholloway marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page.
mattdholloway marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
for scanner.Scan() { | ||
line := scanner.Text() | ||
totalLines++ | ||
lines[writeIndex] = line | ||
validLines[writeIndex] = true | ||
writeIndex = (writeIndex + 1) % maxJobLogLines | ||
} | ||
if err := scanner.Err(); err != nil { | ||
return "", 0, httpResp, fmt.Errorf("failed to read log content: %w", err) | ||
} | ||
var result []string | ||
linesInBuffer := totalLines | ||
if linesInBuffer > maxJobLogLines { | ||
linesInBuffer = maxJobLogLines | ||
} | ||
startIndex := 0 | ||
if totalLines > maxJobLogLines { | ||
startIndex = writeIndex | ||
} | ||
for i := 0; i < linesInBuffer; i++ { | ||
idx := (startIndex + i) % maxJobLogLines | ||
if validLines[idx] { | ||
result = append(result, lines[idx]) | ||
} | ||
} | ||
return strings.Join(result, "\n"), totalLines, httpResp, nil | ||
} |
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.