@@ -405,6 +405,15 @@ Client.prototype._resetEncodedMetadata = function () {
405
405
406
406
// This is the only code path that should set `_encodedMetadata`.
407
407
this . _encodedMetadata = this . _encode ( { metadata } , Client . encoding . METADATA ) ;
408
+ if ( ! this . _encodedMetadata ) {
409
+ // The APM client cannot function without encoded metadata. Handling this
410
+ // could be improved (e.g. log details and disable the APM agent). However,
411
+ // this suffices for now as we have never observed a metadata encoding
412
+ // failure.
413
+ throw new Error (
414
+ 'could not encode metadata (trace-level logging will include details)' ,
415
+ ) ;
416
+ }
408
417
this . _log . trace (
409
418
{ _encodedMetadata : this . _encodedMetadata } ,
410
419
'_resetEncodedMetadata' ,
@@ -504,6 +513,9 @@ Client.prototype._write = function (obj, enc, cb) {
504
513
} else {
505
514
const t = process . hrtime ( ) ;
506
515
const chunk = this . _encode ( obj , enc ) ;
516
+ if ( ! chunk ) {
517
+ return ;
518
+ }
507
519
this . _numEventsEnqueued ++ ;
508
520
this . _chopper . write ( chunk , cb ) ;
509
521
this . _log . trace (
@@ -579,12 +591,18 @@ Client.prototype._writeBatch = function (objs, cb) {
579
591
const chunks = [ ] ;
580
592
for ( var i = 0 ; i < objs . length ; i ++ ) {
581
593
const obj = objs [ i ] ;
582
- chunks . push ( this . _encode ( obj . chunk , obj . encoding ) ) ;
594
+ const encoded = this . _encode ( obj . chunk , obj . encoding ) ;
595
+ if ( encoded ) {
596
+ chunks . push ( encoded ) ;
597
+ }
598
+ }
599
+ if ( chunks . length === 0 ) {
600
+ return ;
583
601
}
584
602
const chunk = chunks . join ( '' ) ;
585
603
const encodeTimeMs = deltaMs ( t ) ;
586
604
587
- this . _numEventsEnqueued += objs . length ;
605
+ this . _numEventsEnqueued += chunks . length ;
588
606
this . _chopper . write ( chunk , cb ) ;
589
607
const fullTimeMs = deltaMs ( t ) ;
590
608
@@ -601,7 +619,7 @@ Client.prototype._writeBatch = function (objs, cb) {
601
619
{
602
620
encodeTimeMs,
603
621
fullTimeMs,
604
- numEvents : objs . length ,
622
+ numEvents : chunks . length ,
605
623
numBytes : chunk . length ,
606
624
} ,
607
625
'_writeBatch' ,
@@ -687,24 +705,54 @@ Client.prototype._maybeUncork = function () {
687
705
} ;
688
706
689
707
Client . prototype . _encode = function ( obj , enc ) {
690
- const out = { } ;
708
+ let thing ;
709
+ let truncFunc ;
710
+ let outAttr ;
691
711
switch ( enc ) {
692
712
case Client . encoding . SPAN :
693
- out . span = truncate . span ( obj . span , this . _conf ) ;
713
+ thing = obj . span ;
714
+ truncFunc = truncate . span ;
715
+ outAttr = 'span' ;
694
716
break ;
695
717
case Client . encoding . TRANSACTION :
696
- out . transaction = truncate . transaction ( obj . transaction , this . _conf ) ;
718
+ thing = obj . transaction ;
719
+ truncFunc = truncate . transaction ;
720
+ outAttr = 'transaction' ;
697
721
break ;
698
722
case Client . encoding . METADATA :
699
- out . metadata = truncate . metadata ( obj . metadata , this . _conf ) ;
723
+ thing = obj . metadata ;
724
+ truncFunc = truncate . metadata ;
725
+ outAttr = 'metadata' ;
700
726
break ;
701
727
case Client . encoding . ERROR :
702
- out . error = truncate . error ( obj . error , this . _conf ) ;
728
+ thing = obj . error ;
729
+ truncFunc = truncate . error ;
730
+ outAttr = 'error' ;
703
731
break ;
704
732
case Client . encoding . METRICSET :
705
- out . metricset = truncate . metricset ( obj . metricset , this . _conf ) ;
733
+ thing = obj . metricset ;
734
+ truncFunc = truncate . metricset ;
735
+ outAttr = 'metricset' ;
706
736
break ;
707
737
}
738
+
739
+ const out = { } ;
740
+ try {
741
+ out [ outAttr ] = truncFunc ( thing , this . _conf ) ;
742
+ } catch ( err ) {
743
+ this . _log . warn (
744
+ {
745
+ err,
746
+ // Only log full problematic object at TRACE level to limit noise.
747
+ thing : this . _log . isLevelEnabled ( 'trace' ) ? thing : '[REDACTED]' ,
748
+ thing_id : thing ?. id ,
749
+ thing_name : thing ?. name ,
750
+ } ,
751
+ `could not encode "${ outAttr } " object` ,
752
+ ) ;
753
+ return null ;
754
+ }
755
+
708
756
return ndjson . serialize ( out ) ;
709
757
} ;
710
758
@@ -765,7 +813,6 @@ Client.prototype.lambdaRegisterTransaction = function (trans, awsRequestId) {
765
813
{ awsRequestId, traceId : trans . trace_id , transId : trans . id } ,
766
814
'lambdaRegisterTransaction start' ,
767
815
) ;
768
- var out = this . _encode ( { transaction : trans } , Client . encoding . TRANSACTION ) ;
769
816
770
817
const finish = ( errOrErrMsg ) => {
771
818
const durationMs = performance . now ( ) - startTime ;
@@ -784,6 +831,12 @@ Client.prototype.lambdaRegisterTransaction = function (trans, awsRequestId) {
784
831
resolve ( ) ; // always resolve, never reject
785
832
} ;
786
833
834
+ var out = this . _encode ( { transaction : trans } , Client . encoding . TRANSACTION ) ;
835
+ if ( ! out ) {
836
+ finish ( 'could not encode transaction' ) ;
837
+ return ;
838
+ }
839
+
787
840
// Every `POST /register/transaction` request must set the
788
841
// `x-elastic-aws-request-id` header. Instead of creating a new options obj
789
842
// each time, we just modify in-place.
0 commit comments