Skip to content

Commit f4c8dcd

Browse files
author
Elad Ben-Israel
authored
feat(core): show token creation stack trace upon resolve error (#2886)
Allows implementers of IResolvable to supply a stack trace, which will automatically be attached to errors thrown during resolution. Implement this for lazy and intrinsic tokens.
1 parent c3667d7 commit f4c8dcd

File tree

4 files changed

+51
-14
lines changed

4 files changed

+51
-14
lines changed

packages/@aws-cdk/cdk/lib/lazy.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { createStackTrace } from './private/stack-trace';
12
import { IResolvable, IResolveContext } from "./resolvable";
23
import { Token } from "./token";
34

@@ -119,8 +120,13 @@ export class Lazy {
119120
}
120121

121122
abstract class LazyBase implements IResolvable {
122-
public abstract resolve(context: IResolveContext): any;
123+
public readonly creationStack: string[];
124+
125+
constructor() {
126+
this.creationStack = createStackTrace();
127+
}
123128

129+
public abstract resolve(context: IResolveContext): any;
124130
public toString() {
125131
return Token.asString(this);
126132
}

packages/@aws-cdk/cdk/lib/private/intrinsic.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class Intrinsic implements IResolvable {
1616
/**
1717
* The captured stack trace which represents the location in which this token was created.
1818
*/
19-
protected readonly trace: string[];
19+
public readonly creationStack: string[];
2020

2121
private readonly value: any;
2222

@@ -25,7 +25,7 @@ export class Intrinsic implements IResolvable {
2525
throw new Error(`Argument to Intrinsic must be a plain value object, got ${value}`);
2626
}
2727

28-
this.trace = createStackTrace();
28+
this.creationStack = createStackTrace();
2929
this.value = value;
3030
}
3131

@@ -68,7 +68,7 @@ export class Intrinsic implements IResolvable {
6868
* @param message Error message
6969
*/
7070
protected newError(message: string): any {
71-
return new Error(`${message}\nToken created:\n at ${this.trace.join('\n at ')}\nError thrown:`);
71+
return new Error(`${message}\nToken created:\n at ${this.creationStack.join('\n at ')}\nError thrown:`);
7272
}
7373
}
7474

packages/@aws-cdk/cdk/lib/resolvable.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ export interface IResolveContext {
2929
* Tokens are special objects that participate in synthesis.
3030
*/
3131
export interface IResolvable {
32+
/**
33+
* The creation stack of this resolvable which will be appended to errors
34+
* thrown during resolution.
35+
*/
36+
readonly creationStack?: string[];
37+
3238
/**
3339
* Produce the Token's value at resolution time
3440
*/
@@ -113,14 +119,22 @@ export class DefaultTokenResolver implements ITokenResolver {
113119
* then finally post-process it.
114120
*/
115121
public resolveToken(t: IResolvable, context: IResolveContext, postProcessor: IPostProcessor) {
116-
let resolved = t.resolve(context);
117-
118-
// The token might have returned more values that need resolving, recurse
119-
resolved = context.resolve(resolved);
120-
121-
resolved = postProcessor.postProcess(resolved, context);
122-
123-
return resolved;
122+
try {
123+
let resolved = t.resolve(context);
124+
125+
// The token might have returned more values that need resolving, recurse
126+
resolved = context.resolve(resolved);
127+
resolved = postProcessor.postProcess(resolved, context);
128+
return resolved;
129+
} catch (e) {
130+
let message = `Resolution error: ${e.message}.`;
131+
if (t.creationStack && t.creationStack.length > 0) {
132+
message += `\nObject creation stack:\n at ${t.creationStack.join('\n at ')}`;
133+
}
134+
135+
e.message = message;
136+
throw e;
137+
}
124138
}
125139

126140
/**
@@ -145,4 +159,4 @@ export class DefaultTokenResolver implements ITokenResolver {
145159

146160
return fragments.mapTokens({ mapToken: context.resolve }).firstValue;
147161
}
148-
}
162+
}

packages/@aws-cdk/cdk/test/test.tokens.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ export = {
477477
function fn2() {
478478
class ExposeTrace extends Intrinsic {
479479
public get creationTrace() {
480-
return this.trace;
480+
return this.creationStack;
481481
}
482482
}
483483

@@ -582,6 +582,23 @@ export = {
582582

583583
return tests;
584584
})(),
585+
586+
'creation stack is attached to errors emitted during resolve'(test: Test) {
587+
function showMeInTheStackTrace() {
588+
return Lazy.stringValue({ produce: () => { throw new Error('fooError'); } });
589+
}
590+
591+
const x = showMeInTheStackTrace();
592+
let message;
593+
try {
594+
resolve(x);
595+
} catch (e) {
596+
message = e.message;
597+
}
598+
599+
test.ok(message && message.includes('showMeInTheStackTrace'));
600+
test.done();
601+
}
585602
};
586603

587604
class Promise2 implements IResolvable {

0 commit comments

Comments
 (0)