@@ -395,69 +395,65 @@ function timestampToDateTime643(unixMS) {
395395 return ( unixMS / 1000 ) . toFixed ( 3 ) ;
396396}
397397
398- function logServerAccess ( req , res ) {
399- if ( ! req . serverAccessLog || ! res . serverAccessLog || ! serverAccessLogger ) {
400- return ;
401- }
402-
403- const params = req . serverAccessLog ;
404- const errorCode = res . serverAccessLog . errorCode ;
405- const endTurnAroundTime = res . serverAccessLog . endTurnAroundTime ;
406- const requestID = res . serverAccessLog . requestID ;
407- const bytesSent = res . serverAccessLog . bytesSent ;
398+ /**
399+ * Builds a log entry object with common fields
400+ * @param {object } req - HTTP request object
401+ * @param {object } params - Server access log parameters from req.serverAccessLog
402+ * @param {object } options - Variable fields for the log entry
403+ * @return {object } Log entry object
404+ */
405+ function buildLogEntry ( req , params , options ) {
408406 const authInfo = params . authInfo ;
409407
410- serverAccessLogger . write ( ` ${ JSON . stringify ( {
408+ return {
411409 // Werelog fields
412410 // logs.access_logs_ingest.timestamp in Clickhouse is of type DateTime so it expects seconds as an integer.
413411 time : Math . floor ( Date . now ( ) / 1000 ) ,
414412 hostname,
415413 pid : process . pid ,
416414
417415 // Analytics
418- action : params . analyticsAction ?? undefined ,
416+ action : options . action ?? undefined ,
419417 accountName : params . analyticsAccountName ?? undefined ,
420418 userName : params . analyticsUserName ?? undefined ,
421- httpMethod : req . method ?? undefined ,
422- bytesDeleted : params . analyticsBytesDeleted ?? undefined ,
423- bytesReceived : Number . isInteger ( req . parsedContentLength ) ? req . parsedContentLength : undefined ,
424- bodyLength : req . headers [ 'content-length' ] !== undefined
425- ? parseInt ( req . headers [ 'content-length' ] , 10 )
426- : undefined ,
427- contentLength : getObjectSize ( req , res ) ?? undefined ,
419+ httpMethod : options . httpMethod ?? undefined ,
420+ bytesDeleted : options . bytesDeleted ?? undefined ,
421+ bytesReceived : options . bytesReceived ?? undefined ,
422+ bodyLength : options . bodyLength ?? undefined ,
423+ contentLength : options . contentLength ?? undefined ,
428424 // eslint-disable-next-line camelcase
429- elapsed_ms : calculateElapsedMS ( params . startTime , params . onCloseEndTime ) ?? undefined ,
425+ elapsed_ms : options . elapsed_ms ?? undefined ,
430426
431427 // AWS access server logs fields https://docs.aws.amazon.com/AmazonS3/latest/userguide/LogFormat.html
432428 startTime : timestampToDateTime643 ( params . startTimeUnixMS ) ?? undefined , // AWS "Time" field
433429 requester : getRequester ( authInfo ) ?? undefined ,
434- operation : getOperation ( req ) ,
435- requestURI : getURI ( req ) ?? undefined ,
436- errorCode : errorCode ?? undefined ,
437- objectSize : params . objectSize ?? getObjectSize ( req , res ) ?? undefined ,
438- totalTime : calculateTotalTime ( params . startTime , params . onFinishEndTime ) ?? undefined ,
439- turnAroundTime : calculateTurnAroundTime ( params . startTurnAroundTime , endTurnAroundTime ) ?? undefined ,
440- referer : req . headers . referer ?? undefined ,
441- userAgent : req . headers [ 'user-agent' ] ?? undefined ,
442- versionID : req . query ?. versionId ?? undefined ,
430+ operation : options . operation ?? undefined ,
431+ requestURI : options . requestURI ?? undefined ,
432+ errorCode : options . errorCode ?? undefined ,
433+ objectSize : options . objectSize ?? undefined ,
434+ totalTime : options . totalTime ?? undefined ,
435+ turnAroundTime : options . turnAroundTime ?? undefined ,
436+ referer : req . headers ? .referer ?? undefined ,
437+ userAgent : req . headers ?. [ 'user-agent' ] ?? undefined ,
438+ versionID : options . versionID ?? undefined ,
443439 signatureVersion : authInfo ?. getAuthVersion ( ) ?? undefined ,
444- cipherSuite : req . socket . encrypted ? req . socket . getCipher ( ) [ 'standardName' ] : undefined ,
440+ cipherSuite : req . socket ? .encrypted ? req . socket . getCipher ( ) [ 'standardName' ] : undefined ,
445441 authenticationType : authInfo ?. getAuthType ( ) ?? undefined ,
446- hostHeader : req . headers . host ?? undefined ,
447- tlsVersion : req . socket . encrypted ? req . socket . getCipher ( ) [ 'version' ] : undefined ,
448- aclRequired : undefined , // TODO: CLDSRV-774
442+ hostHeader : req . headers ? .host ?? undefined ,
443+ tlsVersion : req . socket ? .encrypted ? req . socket . getCipher ( ) [ 'version' ] : undefined ,
444+ aclRequired : options . aclRequired ?? undefined , // TODO: CLDSRV-774
449445 // hostID: undefined, // NOT IMPLEMENTED
450446 // accessPointARN: undefined, // NOT IMPLEMENTED
451447
452448 // Shared between AWS access server logs and Analytics logs
453449 bucketOwner : params . bucketOwner ?? undefined ,
454450 bucketName : params . bucketName ?? undefined , // AWS "Bucket" field
455451 // eslint-disable-next-line camelcase
456- req_id : requestID ?? undefined , // AWS "Request ID" field
457- bytesSent : getBytesSent ( res , bytesSent ) ?? undefined ,
452+ req_id : options . requestID ?? undefined , // AWS "Request ID" field
453+ bytesSent : options . bytesSent ?? undefined ,
458454 clientIP : getRemoteIPFromRequest ( req ) ?? undefined , // AWS 'Remote IP' field
459- httpCode : res . statusCode ?? undefined , // AWS "HTTP Status" field
460- objectKey : params . objectKey ?? undefined , // AWS "Key" field
455+ httpCode : options . httpCode ?? undefined , // AWS "HTTP Status" field
456+ objectKey : options . objectKey ?? undefined , // AWS "Key" field
461457
462458 // Scality server access logs extra fields
463459 logFormatVersion : SERVER_ACCESS_LOG_FORMAT_VERSION ,
@@ -472,11 +468,90 @@ function logServerAccess(req, res) {
472468 // Rate Limiting fields (only present when rate limited)
473469 rateLimited : params . rateLimited ?? undefined ,
474470 rateLimitSource : params . rateLimitSource ?? undefined ,
475- } ) } \n`) ;
471+ } ;
472+ }
473+
474+ /**
475+ * Logs a BATCH.DELETE.OBJECT entry for individual object deletions in multi-object delete
476+ * @param {object } req - HTTP request object
477+ * @param {string } requestID - Request ID from the response
478+ * @param {string } objectKey - Key of the object being deleted
479+ * @param {number|null } objectSize - Size of the object, or null if object doesn't exist
480+ * @param {Error|null } error - Error object if deletion failed, null if successful
481+ * @return {undefined }
482+ */
483+ function logBatchDeleteObject ( req , requestID , objectKey , objectSize , error ) {
484+ if ( ! req . serverAccessLog || ! serverAccessLogger ) {
485+ return ;
486+ }
487+
488+ const params = req . serverAccessLog ;
489+ let httpCode = 204 ;
490+ let errorCode = undefined ;
491+
492+ if ( error ) {
493+ httpCode = error . code ? parseInt ( error . code , 10 ) : 500 ;
494+ errorCode = error . message || 'InternalError' ;
495+ }
496+
497+ const logEntry = buildLogEntry ( req , params , {
498+ action : 'DeleteObject' ,
499+ httpMethod : 'POST' ,
500+ bytesDeleted : objectSize ,
501+ contentLength : objectSize ,
502+ operation : 'BATCH.DELETE.OBJECT' ,
503+ objectSize,
504+ httpCode,
505+ errorCode,
506+ objectKey,
507+ requestID,
508+ } ) ;
509+
510+ serverAccessLogger . write ( `${ JSON . stringify ( logEntry ) } \n` ) ;
511+ }
512+
513+ function logServerAccess ( req , res ) {
514+ if ( ! req . serverAccessLog || ! res . serverAccessLog || ! serverAccessLogger ) {
515+ return ;
516+ }
517+
518+ const params = req . serverAccessLog ;
519+ const errorCode = res . serverAccessLog . errorCode ;
520+ const endTurnAroundTime = res . serverAccessLog . endTurnAroundTime ;
521+ const requestID = res . serverAccessLog . requestID ;
522+ const bytesSent = res . serverAccessLog . bytesSent ;
523+
524+ const logEntry = buildLogEntry ( req , params , {
525+ action : params . analyticsAction ,
526+ httpMethod : req . method ,
527+ bytesDeleted : params . analyticsBytesDeleted ,
528+ bytesReceived : Number . isInteger ( req . parsedContentLength ) ? req . parsedContentLength : undefined ,
529+ bodyLength : req . headers [ 'content-length' ] !== undefined
530+ ? parseInt ( req . headers [ 'content-length' ] , 10 )
531+ : undefined ,
532+ contentLength : getObjectSize ( req , res ) ,
533+ // eslint-disable-next-line camelcase
534+ elapsed_ms : calculateElapsedMS ( params . startTime , params . onCloseEndTime ) ,
535+ operation : getOperation ( req ) ,
536+ requestURI : getURI ( req ) ,
537+ errorCode,
538+ objectSize : params . objectSize ?? getObjectSize ( req , res ) ,
539+ totalTime : calculateTotalTime ( params . startTime , params . onFinishEndTime ) ,
540+ turnAroundTime : calculateTurnAroundTime ( params . startTurnAroundTime , endTurnAroundTime ) ,
541+ versionID : req . query ?. versionId ,
542+ aclRequired : undefined , // TODO: CLDSRV-774
543+ requestID,
544+ bytesSent : getBytesSent ( res , bytesSent ) ,
545+ httpCode : res . statusCode ,
546+ objectKey : params . objectKey ,
547+ } ) ;
548+
549+ serverAccessLogger . write ( `${ JSON . stringify ( logEntry ) } \n` ) ;
476550}
477551
478552module . exports = {
479553 logServerAccess,
554+ logBatchDeleteObject,
480555 setServerAccessLogger,
481556 ServerAccessLogger,
482557
0 commit comments