Skip to content

Commit 6ac505c

Browse files
patmeilerPatrick Meileralexcasalboni
authored
[Bug Fix] Improved JSON parsing for statistics (#268)
* improved JSON parse for statistics * fixed linting issues * added test to fix coverage * Minor error message fix * Minor refactor to avoid JSON parsing multiple times --------- Co-authored-by: Patrick Meiler <pameiler@amazon.de> Co-authored-by: Alex Casalboni <alexsaxmib@gmail.com>
1 parent 4b404ed commit 6ac505c

File tree

2 files changed

+141
-15
lines changed

2 files changed

+141
-15
lines changed

lambda/utils.js

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -653,24 +653,33 @@ module.exports.extractDurationFromText = (log, durationType) => {
653653
* Extract duration (in ms) from a given JSON log (multi-line) and duration type.
654654
*/
655655
module.exports.extractDurationFromJSON = (log, durationType) => {
656-
// extract each line and parse it to JSON object
657-
const lines = log.split('\n').filter((line) => line.startsWith('{')).map((line) => {
658-
try {
659-
return JSON.parse(line);
660-
} catch (e) {
661-
console.error(`Detected invalid JSON line: ${line}`);
662-
return '';
656+
// Check occurance of platform.report in log
657+
if (!log.includes('platform.report')) {
658+
throw new Error('Invalid JSON log does not contain platform.report');
659+
}
660+
661+
if (!log.includes(durationType)) {
662+
throw new Error(`Invalid JSON log does not contain ${durationType}`);
663+
}
664+
665+
let lines = [];
666+
try {
667+
const parsedLog = JSON.parse(log); // this might throw an Error (because of multi-line)
668+
if (Array.isArray(parsedLog)) {
669+
lines = parsedLog; // we already have a list of lines
670+
} else {
671+
lines.push(parsedLog); // we only have 1 line
663672
}
664-
});
665-
// find the log corresponding to the invocation report
666-
const durationLine = lines.find((line) => line.type === 'platform.report');
667-
if (durationLine){
668-
let field = durationType;
669-
// Default to 0 if the specific duration is not found in the log line
670-
return durationLine.record.metrics[field] || 0;
673+
} catch (e) {
674+
// in case the log is not pretty printed, the string needs to be transformed first
675+
console.log('Json Log not pretty printed');
676+
lines = log.split('\n').filter((line) => line.includes('platform.report')).map((line) => {
677+
return JSON.parse(line);
678+
});
671679
}
672680

673-
throw new Error('Unrecognized JSON log');
681+
const durationLine = lines.find((line) => line.type === 'platform.report');
682+
return durationLine.record.metrics[durationType] || 0;
674683
};
675684

676685

test/unit/test-utils.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,4 +1337,121 @@ describe('Lambda Utils', () => {
13371337
expect(value).to.be('RAM128-1');
13381338
});
13391339
});
1340+
1341+
describe('extractDurationFromJSON', () => {
1342+
it('should handle pretty-printed logs from Powertools', () => {
1343+
const prettyPrintedLog = `
1344+
{
1345+
"cold_start": true,
1346+
"function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:TestFunction",
1347+
"function_memory_size": "128",
1348+
"function_name": "TestFunction",
1349+
"function_request_id": "test-id",
1350+
"level": "INFO",
1351+
"message": "Lambda invocation event",
1352+
"timestamp": "2024-12-12T17:00:03.173Z",
1353+
"type": "platform.report",
1354+
"record": {
1355+
"metrics": {
1356+
"durationMs": 100.0,
1357+
"initDurationMs": 200.0
1358+
}
1359+
}
1360+
}`;
1361+
const duration = utils.extractDurationFromJSON(prettyPrintedLog, utils.DURATIONS.durationMs);
1362+
expect(duration).to.be(100.0);
1363+
});
1364+
it('should handle multiline pretty printed logs', () => {
1365+
const logLine = `
1366+
[{
1367+
"cold_start": true,
1368+
"function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:TestFunction",
1369+
"function_memory_size": "128",
1370+
"function_name": "TestFunction",
1371+
"function_request_id": "test-id",
1372+
"level": "INFO",
1373+
"message": "Lambda invocation event",
1374+
"timestamp": "2024-12-12T17:00:03.173Z",
1375+
"type": "platform.report",
1376+
"record": {
1377+
"metrics": {
1378+
"durationMs": 100.0,
1379+
"initDurationMs": 200.0
1380+
}
1381+
}
1382+
},{
1383+
"cold_start": true,
1384+
"function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:TestFunction",
1385+
"function_memory_size": "128",
1386+
"function_name": "TestFunction",
1387+
"function_request_id": "test-id",
1388+
"level": "INFO",
1389+
"message": "Lambda invocation event",
1390+
"timestamp": "2024-12-12T17:00:03.173Z",
1391+
"type": "platform.test",
1392+
"record": {
1393+
"metrics": {
1394+
"durationMs": 100.0,
1395+
"initDurationMs": 200.0
1396+
}
1397+
}
1398+
}]`;
1399+
const duration = utils.extractDurationFromJSON(logLine, utils.DURATIONS.durationMs);
1400+
expect(duration).to.be(100.0);
1401+
});
1402+
it('should handle empty lines in logs pretty printed', () => {
1403+
const logWithEmptyLines = `
1404+
1405+
[
1406+
1407+
{
1408+
"type": "platform.report",
1409+
"record": {
1410+
"metrics": {
1411+
"durationMs": 100.0
1412+
}
1413+
}
1414+
}
1415+
,
1416+
{
1417+
"some": "other log"
1418+
}
1419+
1420+
]
1421+
1422+
`;
1423+
const duration = utils.extractDurationFromJSON(logWithEmptyLines, utils.DURATIONS.durationMs);
1424+
expect(duration).to.be(100.0);
1425+
});
1426+
it('should handle logs with no platform.report', () => {
1427+
const logWithNoPlatformReport = `
1428+
{
1429+
"message": "some log"
1430+
}
1431+
{
1432+
"another": "log"
1433+
}`;
1434+
expect(() => utils.extractDurationFromJSON(logWithNoPlatformReport, utils.DURATIONS.durationMs)).to.throwError();
1435+
});
1436+
it('should handle logs with no platform.report', () => {
1437+
const logWithoutDurationMS = `
1438+
{
1439+
"cold_start": true,
1440+
"function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:TestFunction",
1441+
"function_memory_size": "128",
1442+
"function_name": "TestFunction",
1443+
"function_request_id": "test-id",
1444+
"level": "INFO",
1445+
"message": "Lambda invocation event",
1446+
"timestamp": "2024-12-12T17:00:03.173Z",
1447+
"type": "platform.report",
1448+
"record": {
1449+
"metrics": {
1450+
"initDurationMs": 200.0
1451+
}
1452+
}
1453+
}`;
1454+
expect(() => utils.extractDurationFromJSON(logWithoutDurationMS, utils.DURATIONS.durationMs)).to.throwError();
1455+
});
1456+
});
13401457
});

0 commit comments

Comments
 (0)