AWS Lambda自定义S3日志器执行异常排查求助
问题排查:AWS Lambda搭配自定义日志器后续调用返回null的问题
我通过Serverless Framework部署了一个AWS Lambda处理器,搭配自定义日志器,每次执行会将日志写入唯一的AWS S3对象。首次通过HTTP客户端调用函数端点时一切正常,但后续调用均返回null,仅在间隔较长时间后才会恢复正常输出。移除该日志器后则无此问题,现附上Logger类及处理器代码,请求协助排查日志器的问题:
Logger类代码
const AWS = require('aws-sdk'); var os = require("os"); var stream = require("stream"); AWS.config.update({accessKeyId: process.env.ACCESS_KEY, secretAccessKey: process.env.SECRET_ACCESS_KEY, region: 'ap-south-1'}); const s3 = new AWS.S3(); const BUCKET = process.env.S3_BUCKET; var s3Stream = require('s3-upload-stream')(new AWS.S3()) class Logger{ constructor(id){ var date = new Date().toLocaleString('en-US', {timeZone: 'Asia/Kolkata'}); var currentDate = new Date(date); var month = currentDate.getMonth() + 1; var date = currentDate.getDate() this.file = currentDate.getFullYear().toString() + ((month.toString().length < 2)? ('0' + month) : ('' + month)) + ((date.toString().length < 2)? ('0' + date) : ('' + date)) + "-" + id; console.log("FILE: "+this.file) this.bucket = BUCKET; this.prefix = "logs/"; this.stream = new stream.PassThrough(); this.id = id; this.logContent = ""; } streamLog(type,component,data){ var date = new Date(); var millis = date.getTime(); this.stream.write(millis.toString() + " - " + this.id + " - [" + type + "]" + " - (" + component + "): " + data + os.EOL); this.logContent = this.logContent + millis.toString() + " - " + this.id + " - [" + type + "]" + " - (" + component + "): " + data + os.EOL; } uploadLogToS3(){ this.stream.end(); var upload = s3Stream.upload({ "Bucket": BUCKET, "Key": this.prefix + this.file + ".log" }); return new Promise((resolve,reject) => { console.log("ENTERED PROMISE"); upload.on('error',function(error){ console.log("ERROR STREAMING LOG TO S3: " + error); reject(0); }) upload.on('uploaded',function(details){ console.log("log successfully streamed to S3"); resolve(1); }) upload.on('part',function(details){ console.log("UPLOADING LOG TO S3"); }) this.stream.pipe(upload); }) } async finish(){ try { console.log("ENTERED LOGGER FINISH"); await this.uploadLogToS3(); } catch (error) { console.log("WRITING THE LOG STREAM TO CONSOLE"); console.log(this.logContent); } } }
处理器代码
module.exports.server = async function(event,context){ console.log("ENTERED HANDLER"); const MODULE_DESCRIPTION = "CONTROLLER GET BIRTHDAYS METHOD"; var birthDays; var date = new Date().toLocaleString('en-US', {timeZone: 'Asia/Kolkata'}); var currentDate = new Date(date); try { console.log("ENTERED TRY OF CONTROLLER GET BIRTHDAYS"); [birthDays] = await Promise.all([API_LIB.getBirthDays(parseInt(currentDate.getDate()), parseInt(currentDate.getMonth() + 1),LOGGER)]); if (birthDays.length == 1){ if (birthDays[0].hasOwnProperty("error")){ LOGGER.streamLog("ERROR",MODULE_DESCRIPTION, birthDays[0].error || "ERROR with API call"); await LOGGER.finish(); return {status: 501,body: {error: 1,message: birthDays[0].error || "Internal server error"}} } else{ LOGGER.streamLog("INFO",MODULE_DESCRIPTION,birthDays.toString()); await LOGGER.finish(); return {status: 200,body: birthDays} } } else{ LOGGER.streamLog("INFO",MODULE_DESCRIPTION,birthDays.toString()); await LOGGER.finish(); return {status: 200,body: birthDays} } } catch (error) { LOGGER.streamLog("ERROR",MODULE_DESCRIPTION,error || "Internal server error"); await LOGGER.finish(); return {status: 501,body: {error: 1,message: error || "Internal server error"}} } }
我在整个代码中使用Logger.streamLog()写入日志,恳请协助排查该日志器的问题,谢谢!
问题根源与修复方案
核心问题
- Lambda环境复用导致流资源耗尽:Lambda冷启动后会保持执行环境(暖状态),如果
LOGGER是全局实例,第一次调用finish()时执行了this.stream.end()关闭了PassThrough流,后续调用再执行streamLog()时,向已关闭的流写入数据会静默失败,导致后续逻辑卡住,最终函数返回null。 - 上传流绑定逻辑错误:
uploadLogToS3()中先调用this.stream.end()再绑定上传流,会导致流数据无法正常传递到S3上传流,Promise可能一直处于pending状态,Lambda超时无响应。
修复步骤
每次调用创建独立Logger实例
不要使用全局LOGGER,在处理器内部每次调用时生成新实例,确保资源独立:module.exports.server = async function(event,context){ console.log("ENTERED HANDLER"); const MODULE_DESCRIPTION = "CONTROLLER GET BIRTHDAYS METHOD"; // 用请求ID作为唯一标识,每次调用生成新Logger const LOGGER = new Logger(context.awsRequestId); var birthDays; var date = new Date().toLocaleString('en-US', {timeZone: 'Asia/Kolkata'}); var currentDate = new Date(date); // 后续逻辑保持不变... }调整流结束时机
在uploadLogToS3()中,先绑定上传流再结束PassThrough流,确保数据完整传递:uploadLogToS3(){ var upload = s3Stream.upload({ "Bucket": BUCKET, "Key": this.prefix + this.file + ".log" }); return new Promise((resolve,reject) => { console.log("ENTERED PROMISE"); upload.on('error',function(error){ console.log("ERROR STREAMING LOG TO S3: " + error); reject(0); }) upload.on('uploaded',function(details){ console.log("log successfully streamed to S3"); resolve(1); }) upload.on('part',function(details){ console.log("UPLOADING LOG TO S3"); }) this.stream.pipe(upload); // 绑定流之后再结束 this.stream.end(); }) }增加流状态校验(可选)
如果需要复用Logger实例,在streamLog()中检查流是否可写,不可写则重新创建:streamLog(type,component,data){ if (!this.stream.writable){ this.stream = new stream.PassThrough(); } var date = new Date(); var millis = date.getTime(); this.stream.write(millis.toString() + " - " + this.id + " - [" + type + "]" + " - (" + component + "): " + data + os.EOL); this.logContent = this.logContent + millis.toString() + " - " + this.id + " - [" + type + "]" + " - (" + component + "): " + data + os.EOL; }
内容的提问来源于stack exchange,提问作者kumarp
相关产品推荐
相关产品推荐

