Skip to content

Use optional module "@microsoft/typescript-etw" for ETW logging #32612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 6, 2019

Conversation

mrcrane
Copy link
Contributor

@mrcrane mrcrane commented Jul 29, 2019

Use the @microsoft/typescript-etw native module for ETW logging.

@microsoft/typescript-etw (and ETW in general) are only supported on Windows, so it is not a good idea to add it to the "dependencies" in package.json. Even on Windows we still do not want this module installed by default. Consumers who are interested (such as Visual Studio) should have to opt in. Therefore ETW logging will only be supported if @microsoft/typescript-etw is installed manually. The code will work if the module is found, otherwise it will do nothing.

Types are added from @types/microsoft__typescript-etw so that we can have type checking even if the actual package is not installed.

}
finally {
if (etwLogger && result && result.resolvedModule) etwLogger.logInfoEvent(`Module "${moduleName}" resolved to "${result.resolvedModule.resolvedFileName}"`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, maybe in our PerfLogger wrapper, we could have a logInfoEventIf<T extends any[]>(condition: boolean, logText: (...args: T) => string, ...args: T), so we can avoid introducing control flow statements for logging into code not related to the logging?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand this suggestion.

Copy link
Member

@weswigham weswigham Jul 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than

if (etwLogger && result && result.resolvedModule) etwLogger.logInfoEvent(`Module "${moduleName}" resolved to "${result.resolvedModule.resolvedFileName}"`)

write

logger.logInfoEventIf(result && result.resolvedModule, (moduleName, result) => `Module "${moduleName}" resolved to "${result.resolvedModule.resolvedFileName}"`, moduleName, result);

like how our ts.Debug.assert signature is setup. (This way the control flow for the logger is within the logger and if the message isn't logged, the string isn't calculated)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is that preferable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It moves the control flow for the conditional-ness of the logging into the logger, so anyone reading the implementation doesn't need to reason over the effects of the control flow that's only there for the logger. The function-instead-of-string bit is just an optimization to avoid doing expensive string building (like typeToString) if the branch isn't hit - it's not strictly needed, and is why ts.Debug.assert takes both a string and a function argument.

@mrcrane
Copy link
Contributor Author

mrcrane commented Aug 1, 2019

@weswigham I added a PerfLogger and NullLogger class which addresses your first concern and makes things cleaner. Let me know if it should be organized differently (e.g. is that the correct file and namespace?)

I considered the second suggestion (moving conditional logic inside the logger class), but that is very unwieldy given the strong typing that is currently in place. We would need logInfoEventIf(), logStartCommandIf(), logStopCommandIf(), etc... It seems like overkill for this problem.

@weswigham
Copy link
Member

We'll also need to add "@microsoft/typescript-etw": false to the browser table in our package.json

etwModule = undefined;
}

export const perfLogger: PerfLogger = etwModule ? etwModule : new NullLogger();
Copy link
Member

@weswigham weswigham Aug 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered the second suggestion (moving conditional logic inside the logger class), but that is very unwieldy given the strong typing that is currently in place. We would need logInfoEventIf(), logStartCommandIf(), logStopCommandIf(), etc... It seems like overkill for this problem.

Here's a version using a fluent API to avoid remaking conditional methods with similar definitions (you could construct the passthru instance reflectively, but eh):

Suggested change
export const perfLogger: PerfLogger = etwModule ? etwModule : new NullLogger();
const nullLogger = new NullLogger();
class PassThruLogger implements PerfLogger {
constructor(protected base: PerfLogger) {}
logEvent(msg: string): void {
return this.base.logEvent(msg);
}
logErrEvent(msg: string): void {
return this.base.logErrEvent(msg);
}
logPerfEvent(msg: string): void {
return this.base.logPerfEvent(msg);
}
logInfoEvent(msg: string): void {
return this.base.logInfoEvent(msg);
}
logStartCommand(command: string, msg: string): void {
return this.base.logStartCommand(command, msg);
}
logStopCommand(command: string, msg: string): void {
return this.base.logStopCommand(command, msg);
}
logStartUpdateProgram(msg: string): void {
return this.base.startUpdateProgram(msg);
}
logStopUpdateProgram(msg: string): void {
return this.base.logStopUpdateProgram(msg);
}
logStartUpdateGraph(): void {
return this.base.logStartUpdateGraph();
}
logStopUpdateGraph(): void {
return this.base.logStopUpdateGraph();
}
logStartResolveModule(name: string): void {
return this.base.logStartResolveModule(name);
}
logStopResolveModule(success: string): void {
return this.base.logStopResolveModule(success);
}
logStartParseSourceFile(filename: string): void {
return this.base.logStartParseSourceFile(filename);
}
logStopParseSourceFile(): void {
return this.base.logStopParseSourceFile();
}
logStartReadFile(filename: string): void {
return this.base.logStartReadFile(filename);
}
logStopReadFile(): void {
return this.base.logStopReadFile();
}
logStartBindFile(filename: string): void {
return this.base.logStartBindFile(filename);
}
logStopBindFile(): void {
return this.base.logStopBindFile();
}
logStartScheduledOperation(operationId: string): void {
return this.base.logStartScheduledOperation(operationId);
}
logStopScheduledOperation(): void {
return this.base.logStopScheduledOperation();
}
}
class ConditionalPerfLogger extends PassThruLogger {
constructor(base: PerfLogger) {
super(base);
}
if(condition: boolean): PerfLogger {
if (condition) {
return this.base;
}
return nullLogger;
}
}
export const perfLogger: ConditionalPerfLogger = new ConditionalPerfLogger(etwModule ? etwModule : nullLogger);

usage:

perfLogger.if(moduleResolution).logEvent("message");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this out but it's still missing the piece where the message is a function instead of a string. Without that the string interpolation could fail with a null reference.

At this point I'd like to move forward with the code that I have which is pretty straightforward and has addressed all of the other comments. What do you think @sheetalkamat ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can redefine if to be

        if(condition: boolean, cb: (logger: PerfLogger) => void): void {
          if (condition) {
            return cb(this.base);
          }
        }

with usage

perfLogger.if(moduleResolution, log => log.logEvent("message"));

if that's the concern.

@RyanCavanaugh
Copy link
Member

@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Aug 2, 2019

Heya @RyanCavanaugh, I've started to run the perf test suite on this PR at 5e19753. You can monitor the build here. It should now contribute to this PR's status checks.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

@RyanCavanaugh
The results of the perf run you requested are in!

Here they are:

Comparison Report - master..32612

Metric master 32612 Delta Best Worst
Angular - node (v12.1.0, x64)
Memory used 325,420k (± 0.01%) 325,409k (± 0.03%) -11k (- 0.00%) 325,297k 325,597k
Parse Time 1.44s (± 0.67%) 1.43s (± 0.82%) -0.01s (- 0.83%) 1.41s 1.47s
Bind Time 0.76s (± 1.16%) 0.76s (± 0.77%) -0.01s (- 0.79%) 0.74s 0.77s
Check Time 4.22s (± 0.34%) 4.20s (± 0.53%) -0.02s (- 0.40%) 4.15s 4.26s
Emit Time 5.22s (± 0.68%) 5.23s (± 0.56%) +0.01s (+ 0.17%) 5.17s 5.29s
Total Time 11.65s (± 0.31%) 11.62s (± 0.40%) -0.02s (- 0.21%) 11.52s 11.71s
Monaco - node (v12.1.0, x64)
Memory used 345,858k (± 0.02%) 345,802k (± 0.02%) -57k (- 0.02%) 345,649k 345,912k
Parse Time 1.19s (± 0.56%) 1.17s (± 0.57%) -0.02s (- 1.68%) 1.15s 1.18s
Bind Time 0.68s (± 0.73%) 0.67s (± 0.83%) -0.00s (- 0.59%) 0.66s 0.69s
Check Time 4.28s (± 0.41%) 4.24s (± 0.43%) -0.04s (- 0.84%) 4.20s 4.28s
Emit Time 2.84s (± 0.60%) 2.85s (± 0.49%) +0.02s (+ 0.53%) 2.82s 2.88s
Total Time 8.97s (± 0.28%) 8.93s (± 0.33%) -0.05s (- 0.51%) 8.86s 8.98s
TFS - node (v12.1.0, x64)
Memory used 301,396k (± 0.02%) 301,333k (± 0.02%) -63k (- 0.02%) 301,205k 301,495k
Parse Time 0.92s (± 0.49%) 0.91s (± 0.80%) -0.01s (- 1.09%) 0.90s 0.93s
Bind Time 0.62s (± 0.90%) 0.62s (± 0.59%) -0.00s (- 0.32%) 0.61s 0.62s
Check Time 3.84s (± 0.32%) 3.82s (± 0.31%) -0.02s (- 0.47%) 3.79s 3.84s
Emit Time 2.96s (± 0.67%) 2.95s (± 0.43%) -0.01s (- 0.24%) 2.93s 2.98s
Total Time 8.33s (± 0.29%) 8.30s (± 0.26%) -0.03s (- 0.37%) 8.26s 8.34s
Angular - node (v8.9.0, x64)
Memory used 343,970k (± 0.02%) 343,994k (± 0.02%) +24k (+ 0.01%) 343,825k 344,100k
Parse Time 1.93s (± 0.31%) 1.83s (± 0.94%) -0.09s (- 4.83%) 1.82s 1.90s
Bind Time 0.81s (± 0.76%) 0.81s (± 0.82%) +0.00s (+ 0.25%) 0.80s 0.83s
Check Time 5.05s (± 0.53%) 5.03s (± 0.28%) -0.02s (- 0.38%) 5.00s 5.06s
Emit Time 6.08s (± 0.45%) 6.02s (± 1.55%) -0.06s (- 0.99%) 5.75s 6.25s
Total Time 13.87s (± 0.26%) 13.70s (± 0.76%) -0.17s (- 1.20%) 13.41s 13.92s
Monaco - node (v8.9.0, x64)
Memory used 363,279k (± 0.01%) 363,140k (± 0.01%) -139k (- 0.04%) 363,049k 363,188k
Parse Time 1.52s (± 0.51%) 1.43s (± 0.46%) -0.09s (- 5.98%) 1.42s 1.45s
Bind Time 0.88s (± 0.85%) 0.89s (± 1.40%) +0.02s (+ 1.94%) 0.87s 0.92s
Check Time 5.27s (± 0.55%) 5.13s (± 1.78%) -0.14s (- 2.60%) 4.97s 5.28s
Emit Time 2.92s (± 0.90%) 3.19s (± 6.45%) +0.27s (+ 9.37%) 2.88s 3.50s
Total Time 10.58s (± 0.30%) 10.64s (± 1.33%) +0.06s (+ 0.58%) 10.36s 10.90s
TFS - node (v8.9.0, x64)
Memory used 317,234k (± 0.01%) 317,123k (± 0.01%) -111k (- 0.03%) 317,039k 317,212k
Parse Time 1.23s (± 0.50%) 1.13s (± 0.43%) -0.10s (- 7.73%) 1.12s 1.14s
Bind Time 0.67s (± 0.67%) 0.67s (± 0.75%) -0.00s (- 0.45%) 0.65s 0.67s
Check Time 4.46s (± 0.73%) 4.44s (± 0.40%) -0.02s (- 0.49%) 4.41s 4.48s
Emit Time 3.07s (± 0.43%) 3.21s (± 2.11%) +0.14s (+ 4.62%) 3.04s 3.31s
Total Time 9.43s (± 0.41%) 9.45s (± 0.59%) +0.02s (+ 0.25%) 9.31s 9.54s
Angular - node (v8.9.0, x86)
Memory used 194,797k (± 0.02%) 194,766k (± 0.02%) -31k (- 0.02%) 194,665k 194,810k
Parse Time 1.87s (± 0.57%) 1.77s (± 0.62%) -0.10s (- 5.40%) 1.74s 1.79s
Bind Time 0.95s (± 0.91%) 0.95s (± 0.49%) -0.00s (- 0.21%) 0.94s 0.96s
Check Time 4.61s (± 0.75%) 4.60s (± 0.72%) -0.00s (- 0.11%) 4.54s 4.69s
Emit Time 5.79s (± 0.92%) 5.79s (± 0.71%) -0.00s (- 0.02%) 5.67s 5.87s
Total Time 13.22s (± 0.44%) 13.10s (± 0.60%) -0.11s (- 0.85%) 12.92s 13.30s
Monaco - node (v8.9.0, x86)
Memory used 202,927k (± 0.01%) 202,809k (± 0.02%) -117k (- 0.06%) 202,741k 202,884k
Parse Time 1.59s (± 0.72%) 1.49s (± 0.55%) -0.10s (- 6.36%) 1.47s 1.51s
Bind Time 0.72s (± 2.62%) 0.71s (± 0.95%) -0.00s (- 0.42%) 0.70s 0.73s
Check Time 4.88s (± 0.58%) 4.85s (± 0.59%) -0.03s (- 0.61%) 4.80s 4.92s
Emit Time 3.15s (± 0.54%) 3.17s (± 0.41%) +0.01s (+ 0.35%) 3.14s 3.19s
Total Time 10.34s (± 0.45%) 10.22s (± 0.36%) -0.12s (- 1.18%) 10.14s 10.30s
TFS - node (v8.9.0, x86)
Memory used 178,226k (± 0.02%) 178,147k (± 0.02%) -79k (- 0.04%) 178,083k 178,216k
Parse Time 1.30s (± 0.99%) 1.18s (± 0.64%) -0.12s (- 9.06%) 1.17s 1.20s
Bind Time 0.62s (± 1.70%) 0.63s (± 0.83%) +0.00s (+ 0.80%) 0.62s 0.64s
Check Time 4.28s (± 0.65%) 4.27s (± 0.95%) -0.00s (- 0.12%) 4.18s 4.34s
Emit Time 2.85s (± 1.01%) 2.87s (± 1.48%) +0.02s (+ 0.56%) 2.82s 3.03s
Total Time 9.05s (± 0.52%) 8.95s (± 0.64%) -0.11s (- 1.18%) 8.84s 9.08s
Angular - node (v9.0.0, x64)
Memory used 343,605k (± 0.01%) 343,571k (± 0.01%) -34k (- 0.01%) 343,482k 343,670k
Parse Time 1.68s (± 0.82%) 1.67s (± 0.74%) -0.01s (- 0.65%) 1.65s 1.71s
Bind Time 0.76s (± 0.64%) 0.76s (± 0.53%) -0.00s (- 0.39%) 0.75s 0.77s
Check Time 4.79s (± 0.38%) 4.79s (± 0.42%) -0.00s (- 0.06%) 4.75s 4.83s
Emit Time 5.74s (± 1.14%) 5.67s (± 1.83%) -0.06s (- 1.08%) 5.43s 5.89s
Total Time 12.97s (± 0.50%) 12.90s (± 0.86%) -0.08s (- 0.60%) 12.62s 13.10s
Monaco - node (v9.0.0, x64)
Memory used 363,035k (± 0.03%) 363,066k (± 0.02%) +31k (+ 0.01%) 362,822k 363,190k
Parse Time 1.29s (± 0.46%) 1.28s (± 0.51%) -0.00s (- 0.31%) 1.27s 1.30s
Bind Time 0.85s (± 0.58%) 0.86s (± 0.69%) +0.00s (+ 0.23%) 0.84s 0.87s
Check Time 4.90s (± 0.57%) 4.88s (± 0.51%) -0.02s (- 0.31%) 4.82s 4.93s
Emit Time 3.35s (± 0.57%) 3.37s (± 0.57%) +0.02s (+ 0.54%) 3.33s 3.42s
Total Time 10.39s (± 0.39%) 10.39s (± 0.35%) +0.00s (+ 0.02%) 10.29s 10.48s
TFS - node (v9.0.0, x64)
Memory used 317,045k (± 0.02%) 316,945k (± 0.01%) -101k (- 0.03%) 316,869k 317,099k
Parse Time 1.02s (± 0.33%) 1.01s (± 0.57%) -0.01s (- 0.69%) 1.00s 1.02s
Bind Time 0.62s (± 0.48%) 0.62s (± 0.48%) 0.00s ( 0.00%) 0.61s 0.62s
Check Time 4.36s (± 0.47%) 4.36s (± 0.43%) -0.00s (- 0.00%) 4.31s 4.39s
Emit Time 3.19s (± 0.78%) 3.18s (± 1.89%) -0.00s (- 0.16%) 2.95s 3.26s
Total Time 9.18s (± 0.46%) 9.17s (± 0.70%) -0.02s (- 0.16%) 8.93s 9.26s
Angular - node (v9.0.0, x86)
Memory used 194,898k (± 0.02%) 194,814k (± 0.03%) -84k (- 0.04%) 194,725k 194,958k
Parse Time 1.59s (± 0.76%) 1.59s (± 0.69%) -0.00s (- 0.19%) 1.56s 1.61s
Bind Time 0.88s (± 0.86%) 0.88s (± 0.54%) -0.00s (- 0.11%) 0.87s 0.89s
Check Time 4.29s (± 0.55%) 4.26s (± 0.54%) -0.03s (- 0.65%) 4.21s 4.30s
Emit Time 5.51s (± 0.71%) 5.48s (± 0.77%) -0.03s (- 0.53%) 5.38s 5.58s
Total Time 12.28s (± 0.49%) 12.21s (± 0.48%) -0.07s (- 0.54%) 12.05s 12.33s
Monaco - node (v9.0.0, x86)
Memory used 202,926k (± 0.02%) 202,818k (± 0.02%) -108k (- 0.05%) 202,733k 202,879k
Parse Time 1.32s (± 0.75%) 1.30s (± 0.52%) -0.02s (- 1.59%) 1.28s 1.31s
Bind Time 0.65s (± 1.06%) 0.64s (± 1.21%) -0.00s (- 0.62%) 0.63s 0.67s
Check Time 4.73s (± 1.24%) 4.68s (± 1.03%) -0.05s (- 1.14%) 4.61s 4.85s
Emit Time 3.05s (± 2.56%) 3.06s (± 2.01%) +0.01s (+ 0.33%) 2.82s 3.13s
Total Time 9.75s (± 0.31%) 9.68s (± 0.34%) -0.06s (- 0.66%) 9.59s 9.74s
TFS - node (v9.0.0, x86)
Memory used 178,223k (± 0.02%) 178,154k (± 0.02%) -69k (- 0.04%) 178,057k 178,250k
Parse Time 1.04s (± 0.80%) 1.03s (± 0.80%) -0.01s (- 0.87%) 1.01s 1.04s
Bind Time 0.57s (± 0.70%) 0.57s (± 0.83%) +0.00s (+ 0.70%) 0.56s 0.58s
Check Time 4.12s (± 0.40%) 4.12s (± 0.57%) +0.00s (+ 0.02%) 4.08s 4.17s
Emit Time 2.79s (± 0.95%) 2.77s (± 0.62%) -0.01s (- 0.47%) 2.71s 2.80s
Total Time 8.51s (± 0.42%) 8.49s (± 0.28%) -0.02s (- 0.23%) 8.45s 8.56s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-142-generic
Architecturex64
Available Memory16 GB
Available Memory1 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v12.1.0, x64)
  • node (v8.9.0, x64)
  • node (v8.9.0, x86)
  • node (v9.0.0, x64)
  • node (v9.0.0, x86)
Scenarios
  • Angular - node (v12.1.0, x64)
  • Angular - node (v8.9.0, x64)
  • Angular - node (v8.9.0, x86)
  • Angular - node (v9.0.0, x64)
  • Angular - node (v9.0.0, x86)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v8.9.0, x64)
  • Monaco - node (v8.9.0, x86)
  • Monaco - node (v9.0.0, x64)
  • Monaco - node (v9.0.0, x86)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v8.9.0, x64)
  • TFS - node (v8.9.0, x86)
  • TFS - node (v9.0.0, x64)
  • TFS - node (v9.0.0, x86)
Benchmark Name Iterations
Current 32612 10
Baseline master 10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants