Skip to content
This repository was archived by the owner on Oct 16, 2020. It is now read-only.

Commit 8a04861

Browse files
committed
feat(tracing): add Jaeger for easier OpenTracing
Adds a command line flag and docs to easily set up a Jaeger instance for debugging performance issues.
1 parent 99eff46 commit 8a04861

File tree

5 files changed

+36
-13
lines changed

5 files changed

+36
-13
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ npm test
6969
-c, --cluster [num] number of concurrent cluster workers (defaults to number of CPUs, 8)
7070
-t, --trace print all requests and responses
7171
-l, --logfile [file] log to this file
72+
-j, --enable-jaeger enable OpenTracing through Jaeger
7273
```
7374

7475
## Extensions
@@ -90,3 +91,18 @@ This language server implements some LSP extensions, prefixed with an `x`.
9091

9192
This project follows [semver](http://semver.org/) for command line arguments and standard LSP methods.
9293
Any change to command line arguments, Node version or protocol breaking changes will result in a major version increase.
94+
95+
## Debugging Performance with OpenTracing
96+
97+
The language server is fully traced through [OpenTracing](http://opentracing.io/), which allows to debug what exact operations caused method calls to take long.
98+
You can pass a span context through an optional `meta` field on the JSON RPC message object.
99+
100+
For local development, there is built-in support for the open source OpenTracing implementation [Jaeger](http://jaeger.readthedocs.io/en/latest/), which can be set up to run on localhost with just one command (you need [Docker](https://www.docker.com/) installed):
101+
102+
```
103+
docker run -d -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp \
104+
-p5778:5778 -p16686:16686 -p14268:14268 jaegertracing/all-in-one:latest
105+
```
106+
107+
After that, run the language server with the `--enable-jaeger` command line flag and do some requests from your client.
108+
Open http://localhost:16686 in your browser and you will see method calls broken down into spans.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"fast-json-patch": "^2.0.2",
4444
"glob": "^7.1.1",
4545
"iterare": "^0.0.8",
46+
"jaeger-client": "^3.5.3",
4647
"lodash": "^4.17.4",
4748
"mz": "^2.6.0",
4849
"object-hash": "^1.1.8",

src/connection.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Observable, Subscription, Symbol } from '@reactivex/rxjs';
22
import { EventEmitter } from 'events';
33
import { applyReducer, Operation } from 'fast-json-patch';
44
import { camelCase, omit } from 'lodash';
5-
import { FORMAT_TEXT_MAP, Span, Tracer } from 'opentracing';
5+
import { FORMAT_TEXT_MAP, SpanContext, Tracer } from 'opentracing';
66
import { inspect } from 'util';
77
import { ErrorCodes, Message, StreamMessageReader as VSCodeStreamMessageReader, StreamMessageWriter as VSCodeStreamMessageWriter } from 'vscode-jsonrpc';
88
import { isNotificationMessage, isRequestMessage, isResponseMessage, NotificationMessage, RequestMessage, ResponseMessage } from 'vscode-jsonrpc/lib/messages';
@@ -210,17 +210,13 @@ export function registerLanguageHandler(messageEmitter: MessageEmitter, messageW
210210
return;
211211
}
212212
const method = camelCase(message.method);
213-
let span = new Span();
214-
if (isRequestMessage(message)) {
215-
// If message is request and has tracing metadata, extract the span context and create a span for the method call
216-
if (hasMeta(message)) {
217-
const context = tracer.extract(FORMAT_TEXT_MAP, message.meta);
218-
if (context) {
219-
span = tracer.startSpan('Handle ' + message.method, { childOf: context });
220-
span.setTag('params', inspect(message.params));
221-
}
222-
}
213+
let context: SpanContext | undefined;
214+
// If message is request and has tracing metadata, extract the span context
215+
if (isRequestMessage(message) && hasMeta(message)) {
216+
context = tracer.extract(FORMAT_TEXT_MAP, message.meta) || undefined;
223217
}
218+
const span = tracer.startSpan('Handle ' + message.method, { childOf: context });
219+
span.setTag('params', inspect(message.params));
224220
if (typeof (handler as any)[method] !== 'function') {
225221
// Method not implemented
226222
if (isRequestMessage(message)) {

src/language-server-stdio.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env node
22

3+
import { Tracer } from 'opentracing';
34
import { isNotificationMessage } from 'vscode-jsonrpc/lib/messages';
45
import { MessageEmitter, MessageLogOptions, MessageWriter, registerLanguageHandler, RegisterLanguageHandlerOptions } from './connection';
56
import { RemoteLanguageClient } from './lang-handler';
@@ -8,19 +9,24 @@ import { TypeScriptService, TypeScriptServiceOptions } from './typescript-servic
89

910
const packageJson = require('../package.json');
1011
const program = require('commander');
12+
const { initTracer } = require('jaeger-client');
1113

1214
program
1315
.version(packageJson.version)
1416
.option('-s, --strict', 'enables strict mode')
1517
.option('-t, --trace', 'print all requests and responses')
1618
.option('-l, --logfile [file]', 'log to this file')
19+
.option('-j, --enable-jaeger', 'enable OpenTracing through Jaeger')
1720
.parse(process.argv);
1821

1922
const logger = program.logfile ? new FileLogger(program.logfile) : new StderrLogger();
23+
const tracer = program.enableJaeger ? initTracer({ serviceName: 'javascript-typescript-langserver', sampler: { type: 'const', param: 1 } }) : new Tracer();
24+
2025
const options: TypeScriptServiceOptions & MessageLogOptions & RegisterLanguageHandlerOptions = {
2126
strict: program.strict,
2227
logMessages: program.trace,
23-
logger
28+
logger,
29+
tracer
2430
};
2531

2632
const messageEmitter = new MessageEmitter(process.stdin, options);

src/language-server.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#!/usr/bin/env node
22

3+
import { Tracer } from 'opentracing';
34
import { FileLogger, StdioLogger } from './logging';
45
import { serve, ServeOptions } from './server';
56
import { TypeScriptService, TypeScriptServiceOptions } from './typescript-service';
67
const program = require('commander');
78
const packageJson = require('../package.json');
9+
const { initTracer } = require('jaeger-client');
810

911
const defaultLspPort = 2089;
1012
const numCPUs = require('os').cpus().length;
@@ -16,14 +18,16 @@ program
1618
.option('-c, --cluster [num]', 'number of concurrent cluster workers (defaults to number of CPUs, ' + numCPUs + ')', parseInt)
1719
.option('-t, --trace', 'print all requests and responses')
1820
.option('-l, --logfile [file]', 'log to this file')
21+
.option('-j, --enable-jaeger', 'enable OpenTracing through Jaeger')
1922
.parse(process.argv);
2023

2124
const options: ServeOptions & TypeScriptServiceOptions = {
2225
clusterSize: program.cluster || numCPUs,
2326
lspPort: program.port || defaultLspPort,
2427
strict: program.strict,
2528
logMessages: program.trace,
26-
logger: program.logfile ? new FileLogger(program.logfile) : new StdioLogger()
29+
logger: program.logfile ? new FileLogger(program.logfile) : new StdioLogger(),
30+
tracer: program.enableJaeger ? initTracer({ serviceName: 'javascript-typescript-langserver', sampler: { type: 'const', param: 1 } }) : new Tracer()
2731
};
2832

2933
serve(options, client => new TypeScriptService(client, options));

0 commit comments

Comments
 (0)