1414 * limitations under the License.
1515 */
1616
17- import * as path from 'path' ;
18- import * as fs from 'fs' ;
17+ import { env } from 'process' ;
1918
2019import {
2120 InstrumentationBase ,
@@ -58,8 +57,13 @@ import {
5857
5958import { AwsLambdaInstrumentationConfig , EventContextExtractor } from './types' ;
6059import { PACKAGE_NAME , PACKAGE_VERSION } from './version' ;
61- import { env } from 'process' ;
62- import { LambdaModule } from './internal-types' ;
60+ import {
61+ isInvalidHandler ,
62+ moduleRootAndHandler ,
63+ resolveHandler ,
64+ splitHandlerString ,
65+ tryPath ,
66+ } from './user-function' ;
6367
6468const awsPropagator = new AWSXRayPropagator ( ) ;
6569const headerGetter : TextMapGetter < APIGatewayProxyEventHeaders > = {
@@ -92,6 +96,7 @@ export class AwsLambdaInstrumentation extends InstrumentationBase {
9296 this . _config . disableAwsContextPropagation = true ;
9397 }
9498 }
99+ this . _traceForceFlusher = this . _traceForceFlush ( trace . getTracerProvider ( ) ) ;
95100 }
96101
97102 override setConfig ( config : AwsLambdaInstrumentationConfig = { } ) {
@@ -110,61 +115,106 @@ export class AwsLambdaInstrumentation extends InstrumentationBase {
110115 ) ;
111116 return [ ] ;
112117 }
118+ if ( isInvalidHandler ( handlerDef ) ) {
119+ this . _diag . debug (
120+ 'Skipping lambda instrumentation: _HANDLER/lambdaHandler is invalid.' ,
121+ { taskRoot, handlerDef }
122+ ) ;
123+ return [ ] ;
124+ }
113125
114- const handler = path . basename ( handlerDef ) ;
115- const moduleRoot = handlerDef . substr ( 0 , handlerDef . length - handler . length ) ;
116-
117- const [ module , functionName ] = handler . split ( '.' , 2 ) ;
118-
119- // Lambda loads user function using an absolute path.
120- let filename = path . resolve ( taskRoot , moduleRoot , module ) ;
121- if ( ! filename . endsWith ( '.js' ) ) {
122- // its impossible to know in advance if the user has a cjs or js file.
123- // check that the .js file exists otherwise fallback to next known possibility
124- try {
125- fs . statSync ( `${ filename } .js` ) ;
126- filename += '.js' ;
127- } catch ( e ) {
128- // fallback to .cjs
129- filename += '.cjs' ;
130- }
126+ const [ moduleRoot , moduleAndHandler ] = moduleRootAndHandler ( handlerDef ) ;
127+ const [ module , handlerPath ] = splitHandlerString ( moduleAndHandler ) ;
128+
129+ if ( ! module || ! handlerPath ) {
130+ this . _diag . debug (
131+ 'Skipping lambda instrumentation: _HANDLER/lambdaHandler is invalid.' ,
132+ { taskRoot, handlerDef, moduleRoot, module, handlerPath }
133+ ) ;
134+ return [ ] ;
135+ }
136+
137+ const filename = tryPath ( taskRoot , moduleRoot , module ) ;
138+ if ( ! filename ) {
139+ this . _diag . debug (
140+ 'Skipping lambda instrumentation: _HANDLER/lambdaHandler and LAMBDA_TASK_ROOT did not resolve to a file.' ,
141+ {
142+ taskRoot,
143+ handlerDef,
144+ moduleRoot,
145+ module,
146+ handlerPath,
147+ }
148+ ) ;
149+ return [ ] ;
131150 }
132151
133152 diag . debug ( 'Instrumenting lambda handler' , {
134153 taskRoot,
135154 handlerDef,
136- handler ,
155+ filename ,
137156 moduleRoot,
138157 module,
139- filename,
140- functionName,
158+ handlerPath,
141159 } ) ;
142160
161+ const patch = ( moduleExports : object ) => {
162+ const [ container , functionName ] = resolveHandler (
163+ moduleExports ,
164+ handlerPath
165+ ) ;
166+ if (
167+ container == null ||
168+ functionName == null ||
169+ typeof container [ functionName ] !== 'function'
170+ ) {
171+ this . _diag . debug (
172+ 'Skipping lambda instrumentation: _HANDLER/lambdaHandler did not resolve to a function.' ,
173+ {
174+ taskRoot,
175+ handlerDef,
176+ filename,
177+ moduleRoot,
178+ module,
179+ handlerPath,
180+ }
181+ ) ;
182+ return moduleExports ;
183+ }
184+
185+ if ( isWrapped ( container [ functionName ] ) ) {
186+ this . _unwrap ( container , functionName ) ;
187+ }
188+ this . _wrap ( container , functionName , this . _getHandler ( ) ) ;
189+ return moduleExports ;
190+ } ;
191+ const unpatch = ( moduleExports ?: object ) => {
192+ if ( moduleExports == null ) return ;
193+ const [ container , functionName ] = resolveHandler (
194+ moduleExports ,
195+ handlerPath
196+ ) ;
197+ if (
198+ container == null ||
199+ functionName == null ||
200+ typeof container [ functionName ] !== 'function'
201+ ) {
202+ return ;
203+ }
204+
205+ this . _unwrap ( container , functionName ) ;
206+ } ;
207+
143208 return [
144209 new InstrumentationNodeModuleDefinition (
145- // NB: The patching infrastructure seems to match names backwards, this must be the filename, while
146- // InstrumentationNodeModuleFile must be the module name.
210+ // The patching infrastructure properly supports absolute paths when registering hooks but not when
211+ // actually matching against filenames when patching, so we need to provide a file instrumentation
212+ // that will actually match by using a relative path.
147213 filename ,
148214 [ '*' ] ,
149- undefined ,
150- undefined ,
151- [
152- new InstrumentationNodeModuleFile (
153- module ,
154- [ '*' ] ,
155- ( moduleExports : LambdaModule ) => {
156- if ( isWrapped ( moduleExports [ functionName ] ) ) {
157- this . _unwrap ( moduleExports , functionName ) ;
158- }
159- this . _wrap ( moduleExports , functionName , this . _getHandler ( ) ) ;
160- return moduleExports ;
161- } ,
162- ( moduleExports ?: LambdaModule ) => {
163- if ( moduleExports == null ) return ;
164- this . _unwrap ( moduleExports , functionName ) ;
165- }
166- ) ,
167- ]
215+ patch ,
216+ unpatch ,
217+ [ new InstrumentationNodeModuleFile ( module , [ '*' ] , patch , unpatch ) ]
168218 ) ,
169219 ] ;
170220 }
0 commit comments