@@ -33,11 +33,15 @@ class LambdaLogExporterTest {
3333 private static final Pattern READER_MESSAGE_FILTER = Pattern .compile ("DurableExecutionTelemetry" );
3434
3535 private static String exportAndCapture (DurableExecutionTelemetryRecord record ) {
36+ return exportAndCapture (new LambdaLogExporter (), record );
37+ }
38+
39+ private static String exportAndCapture (LambdaLogExporter exporter , DurableExecutionTelemetryRecord record ) {
3640 PrintStream original = System .out ;
3741 ByteArrayOutputStream buffer = new ByteArrayOutputStream ();
3842 try (PrintStream capture = new PrintStream (buffer , true , StandardCharsets .UTF_8 )) {
3943 System .setOut (capture );
40- new LambdaLogExporter () .export (record );
44+ exporter .export (record );
4145 } finally {
4246 System .setOut (original );
4347 }
@@ -84,21 +88,16 @@ void timestampAlwaysHasThreeFractionDigitsInUtc() {
8488 }
8589
8690 @ Test
87- void truncationIsMeasuredOnTheRecordNotTheEnvelope () {
91+ void lineThatFitsIsPrintedWhole () {
8892 var record = RecordFactory .sample ();
8993 var exporter = new LambdaLogExporter ();
90- int recordBytes = Json .byteSize (exporter .render (record ));
91-
92- // A limit equal to the rendered record keeps it whole, even though the printed envelope line is larger.
93- var shaped = Truncation .truncateRecord (record , recordBytes , exporter ::render );
94- assertSame (record , shaped );
95- assertFalse (shaped .toWireMap ().containsKey ("truncated" ));
96- String line = exportAndCapture (shaped ).strip ();
97- String recordJson = Json .stringify (exporter .render (shaped ));
94+ String line = exportAndCapture (record ).strip ();
95+ String recordJson = Json .stringify (exporter .render (record ));
9896 assertEquals (
99- recordBytes + ENVELOPE_BYTES + escapedChars (recordJson ),
97+ Json . byteSize ( exporter . render ( record )) + ENVELOPE_BYTES + escapedChars (recordJson ),
10098 line .getBytes (StandardCharsets .UTF_8 ).length ,
10199 "line = record + 68-byte envelope + one byte per quote or backslash in the record" );
100+ assertFalse (line .contains ("\\ \" truncated\\ \" " ), "not truncated: " + line );
102101 }
103102
104103 /** Bytes the envelope adds around an empty {@code message}. */
@@ -115,23 +114,31 @@ void envelopeAddsSixtyEightBytes() {
115114 }
116115
117116 /**
118- * An escape-heavy record just under the default 256,000-byte budget is not truncated, and its printed line is about
119- * twice the record: at most {@code 2 × maxRecordSizeBytes + 68}, and under the 1 MB CloudWatch Logs event limit.
117+ * The limit caps the printed line, escaping included. An escape-heavy record that fits the record budget but whose
118+ * line does not (300,000 backslashes are about 600 KB of record and 1.2 MB of line) is truncated again until the
119+ * line fits; its message still parses and says it was truncated.
120120 */
121121 @ Test
122- void escapeHeavyRecordNearTheLimitDoublesButStaysUnderOneMegabyte () {
123- var exporter = new LambdaLogExporter ();
124- int limit = exporter .maxRecordSizeBytes ();
125- for (char ch : new char [] {'"' , '\\' }) {
126- var record = RecordFactory .sampleWithInput (String .valueOf (ch ).repeat (127_000 ));
127- int recordBytes = Json .byteSize (exporter .render (record ));
128- assertTrue (recordBytes <= limit && recordBytes > limit - 2_000 , "record near the limit: " + recordBytes );
129- var shaped = Truncation .truncateRecord (record , limit , exporter ::render );
130- assertSame (record , shaped , "not truncated" );
131- int lineBytes = exportAndCapture (shaped ).strip ().getBytes (StandardCharsets .UTF_8 ).length ;
132- assertTrue (lineBytes > 2 * recordBytes - 1_000 , "line about twice the record: " + lineBytes );
133- assertTrue (lineBytes <= 2 * limit + ENVELOPE_BYTES , "line within 2 x limit + 68: " + lineBytes );
134- assertTrue (lineBytes < 1_048_576 , "line under the 1 MB CloudWatch Logs event limit: " + lineBytes );
122+ @ SuppressWarnings ("unchecked" )
123+ void escapeHeavyRecordIsTruncatedUntilThePrintedLineFits () throws Exception {
124+ for (int limit : new int [] {256_000 , 1_000_000 }) {
125+ var exporter = new LambdaLogExporter (limit );
126+ for (char ch : new char [] {'"' , '\\' }) {
127+ int count = limit == 1_000_000 ? 300_000 : 127_000 ;
128+ var record = RecordFactory .sampleWithInput (String .valueOf (ch ).repeat (count ));
129+ int recordBytes = Json .byteSize (exporter .render (record ));
130+ assertTrue (recordBytes <= limit , "record fits the record budget: " + recordBytes );
131+ var shaped = Truncation .truncateRecord (record , limit , exporter ::render );
132+ assertSame (record , shaped , "record-level truncation keeps it whole" );
133+
134+ String line = exportAndCapture (exporter , shaped ).strip ();
135+ int lineBytes = line .getBytes (StandardCharsets .UTF_8 ).length ;
136+ assertTrue (lineBytes <= limit , limit + ": printed line within the limit: " + lineBytes );
137+ Map <String , Object > envelope = MAPPER .readValue (line , Map .class );
138+ Map <String , Object > parsed = MAPPER .readValue ((String ) envelope .get ("message" ), Map .class );
139+ assertEquals (Boolean .TRUE , parsed .get ("truncated" ), "record says it was truncated" );
140+ assertEquals (record .executionArn (), parsed .get ("executionArn" ));
141+ }
135142 }
136143 }
137144}
0 commit comments