diff --git a/docs/release_notes/0.0.54.md b/docs/release_notes/0.0.54.md index 595a58405..3c1acd5cc 100644 --- a/docs/release_notes/0.0.54.md +++ b/docs/release_notes/0.0.54.md @@ -5,4 +5,4 @@ ## Bugfixes ## Other -- Truncate log lines to 5000 bytes (#436) +- Truncate log lines to 5000 bytes (#436, #444) diff --git a/pkg/client/core/api/rest/client.go b/pkg/client/core/api/rest/client.go index 64dc4cdef..ac20d7d1d 100644 --- a/pkg/client/core/api/rest/client.go +++ b/pkg/client/core/api/rest/client.go @@ -9,6 +9,8 @@ import ( "net/http" "strings" + "github.com/oslokommune/okctl/pkg/truncate" + validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/mishudark/errors" @@ -84,9 +86,13 @@ func (c *HTTPClient) Do(method, endpoint string, body interface{}, into interfac err = resp.Body.Close() }() + const logLineMaxlength = 5000 + if into != nil { if c.Debug { - _, err = fmt.Fprintf(c.Progress, "client (method: %s, endpoint: %s) received data: %s", method, endpoint, out) + truncatedOut := truncate.Bytes(out, logLineMaxlength) + + _, err = fmt.Fprintf(c.Progress, "client (method: %s, endpoint: %s) received data: %s", method, endpoint, truncatedOut) if err != nil { return fmt.Errorf("failed to write debug output: %w", err) } @@ -99,7 +105,9 @@ func (c *HTTPClient) Do(method, endpoint string, body interface{}, into interfac } if c.Debug { - _, err = io.Copy(c.Progress, strings.NewReader(string(out))) + truncatedOut := truncate.Bytes(out, logLineMaxlength) + + _, err = io.Copy(c.Progress, strings.NewReader(string(truncatedOut))) if err != nil { return fmt.Errorf("%s: %w", pretty("failed to write progress for", method, endpoint), err) } diff --git a/pkg/middleware/logger/logger.go b/pkg/middleware/logger/logger.go index 0c5fabf43..68b3de9e8 100644 --- a/pkg/middleware/logger/logger.go +++ b/pkg/middleware/logger/logger.go @@ -6,6 +6,8 @@ import ( "strings" "time" + "github.com/oslokommune/okctl/pkg/truncate" + "github.com/go-kit/kit/endpoint" "github.com/sanity-io/litter" "github.com/sirupsen/logrus" @@ -79,25 +81,9 @@ func (l *logging) ProcessResponse(err error, response interface{}, begin time.Ti d = litter.Sdump(response) } - truncatedDump := Truncate(&d, 5000) + truncatedDump := truncate.String(&d, 5000) l.log.Trace("response: ", truncatedDump) } l.log.Debug("request completed in: ", time.Since(begin).String()) } - -// Truncate truncates a string to the minimum of its length and the given max length -func Truncate(s *string, maxLength int) string { - // This way of doing substrings assumes ASCII and doesn't support UTF-8. - // See https://stackoverflow.com/a/56129336 - truncateLength := min(maxLength, len(*s)) - return (*s)[:truncateLength] -} - -func min(x, y int) int { - if x < y { - return x - } - - return y -} diff --git a/pkg/middleware/logger/logger_test.go b/pkg/middleware/logger/logger_test.go index 35791b91c..10172b4b2 100644 --- a/pkg/middleware/logger/logger_test.go +++ b/pkg/middleware/logger/logger_test.go @@ -76,37 +76,3 @@ func TestLogging(t *testing.T) { }) } } - -func TestTruncate(t *testing.T) { - testCases := []struct { - name string - input string - maxLength int - expected string - }{ - { - name: "Should truncate string", - input: "1234567890", - maxLength: 5, - expected: "12345", - }, - { - name: "Should keep string if it's equal to maxLength", - input: "1234567890", - maxLength: 10, - expected: "1234567890", - }, - { - name: "Should keep string if it's over maxLength", - input: "1234567890", - maxLength: 11, - expected: "1234567890", - }, - } - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expected, logger.Truncate(&tc.input, tc.maxLength)) - }) - } -} diff --git a/pkg/truncate/truncate.go b/pkg/truncate/truncate.go new file mode 100644 index 000000000..25d50d659 --- /dev/null +++ b/pkg/truncate/truncate.go @@ -0,0 +1,42 @@ +// Package truncate implements truncating variables +package truncate + +import "fmt" + +// String truncates a string to the minimum of its length and the given max length +func String(s *string, maxLength int) string { + // This way of doing substrings assumes ASCII and doesn't support UTF-8. + // See https://stackoverflow.com/a/56129336 + truncateLength := min(maxLength, len(*s)) + truncated := (*s)[:truncateLength] + + if len(*s) > truncateLength { + bytesTruncated := len(*s) - truncateLength + truncated += fmt.Sprintf(" [truncated %d bytes]", bytesTruncated) + } + + return truncated +} + +// Bytes truncates a byte array to the minimum of its length and the given max length +func Bytes(b []byte, maxLength int) []byte { + truncateLength := min(maxLength, len(b)) + + truncated := b[:truncateLength] + + if len(b) > truncateLength { + bytesTruncated := len(b) - truncateLength + truncateInfo := fmt.Sprintf(" [truncated %d bytes]", bytesTruncated) + truncated = append(truncated, truncateInfo...) + } + + return truncated +} + +func min(x, y int) int { + if x < y { + return x + } + + return y +} diff --git a/pkg/truncate/truncate_test.go b/pkg/truncate/truncate_test.go new file mode 100644 index 000000000..0ab07aaf0 --- /dev/null +++ b/pkg/truncate/truncate_test.go @@ -0,0 +1,57 @@ +package truncate_test + +import ( + "testing" + + "github.com/oslokommune/okctl/pkg/truncate" + "github.com/stretchr/testify/assert" +) + +func TestTruncate(t *testing.T) { + testCases := []struct { + name string + input string + maxLength int + expected string + }{ + { + name: "Should truncate string", + input: "1234567890", + maxLength: 5, + expected: "12345 [truncated 5 bytes]", + }, + { + name: "Should truncate some other string", + input: "1234567890", + maxLength: 7, + expected: "1234567 [truncated 3 bytes]", + }, + { + name: "Should keep string if it's equal to maxLength", + input: "1234567890", + maxLength: 10, + expected: "1234567890", + }, + { + name: "Should keep string if it's over maxLength", + input: "1234567890", + maxLength: 11, + expected: "1234567890", + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + // Test String string + truncatedString := truncate.String(&tc.input, tc.maxLength) + assert.Equal(t, tc.expected, truncatedString) + + // Test String bytes + inputBytes := []byte(tc.input) + truncatedBytes := truncate.Bytes(inputBytes, tc.maxLength) + + expectedBytes := []byte(tc.expected) + assert.Equal(t, expectedBytes, truncatedBytes) + }) + } +}