Skip to content

Commit 179dbfa

Browse files
authored
Merge pull request #2969 from murgatroid99/debugging_example
Add debugging example
2 parents 9877359 + d22becc commit 179dbfa

File tree

3 files changed

+203
-0
lines changed

3 files changed

+203
-0
lines changed

examples/debugging/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Debugging
2+
3+
Currently, grpc provides two major tools to help user debug issues, which are logging and channelz.
4+
5+
## Logs
6+
7+
gRPC has put substantial logging instruments on critical paths of gRPC to help users debug issues. The [Environment Variables](https://github.com/grpc/grpc-node/blob/master/doc/environment_variables.md) doc describes the environment variables that control debug logging.
8+
9+
To enable full debug logging, run the code with the following environment variables: `GRPC_TRACE=all GRPC_VERBOSITY=DEBUG`.
10+
11+
## Channelz
12+
13+
We also provide a runtime debugging tool, Channelz, to help users with live debugging.
14+
15+
See the channelz blog post here ([link](https://grpc.io/blog/a-short-introduction-to-channelz/)) for details about how to use channelz service to debug live program.
16+
17+
## Try it
18+
19+
The example is able to showcase how logging and channelz can help with debugging. See the channelz blog post linked above for full explanation.
20+
21+
```
22+
node server.js
23+
```
24+
25+
```
26+
node client.js
27+
```

examples/debugging/client.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
*
3+
* Copyright 2025 gRPC authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
const grpc = require('@grpc/grpc-js');
20+
const protoLoader = require('@grpc/proto-loader');
21+
const parseArgs = require('minimist');
22+
23+
var PROTO_PATH = __dirname + '/../protos/helloworld.proto';
24+
25+
const packageDefinition = protoLoader.loadSync(
26+
PROTO_PATH,
27+
{keepCase: true,
28+
longs: String,
29+
enums: String,
30+
defaults: true,
31+
oneofs: true
32+
});
33+
var helloProto = grpc.loadPackageDefinition(packageDefinition).helloworld;
34+
35+
function serverBindPort(server, port) {
36+
return new Promise((resolve, reject) => {
37+
server.bindAsync(port, grpc.ServerCredentials.createInsecure(), (error, port) => {
38+
if (error) {
39+
reject(error);
40+
} else {
41+
resolve(port);
42+
}
43+
})
44+
});
45+
}
46+
47+
const addressString = 'ipv4:///127.0.0.1:10001,127.0.0.1:10002,127.0.0.1:10003';
48+
49+
function callSayHello(client, name) {
50+
return new Promise((resolve, reject) => {
51+
const deadline = new Date();
52+
deadline.setMilliseconds(deadline.getMilliseconds() + 150);
53+
client.sayHello({name}, {deadline}, (error, response) => {
54+
if (error) {
55+
reject(error);
56+
} else {
57+
resolve(response);
58+
}
59+
});
60+
});
61+
}
62+
63+
async function main() {
64+
const argv = parseArgs(process.argv.slice(2), {
65+
string: ['addr', 'name'],
66+
default: {addr: 'localhost:50051', name: 'world'}
67+
});
68+
69+
// Set up the server serving channelz service.
70+
const channelzServer = new grpc.Server();
71+
grpc.addAdminServicesToServer(channelzServer);
72+
await serverBindPort(channelzServer, argv.addr);
73+
74+
const roundRobinServiceConfig = {
75+
methodConfig: [],
76+
loadBalancingConfig: [{ round_robin: {} }]
77+
};
78+
const client = new helloProto.Greeter(addressString, grpc.credentials.createInsecure(), {'grpc.service_config': JSON.stringify(roundRobinServiceConfig)});
79+
80+
// Contact the server and print out its response
81+
82+
// Make 100 SayHello RPCs
83+
for (let i = 0; i < 100; i++) {
84+
try {
85+
const response = await callSayHello(client, argv.name);
86+
console.log(`Greeting: ${response.message}`);
87+
} catch (e) {
88+
console.log(`could not greet: ${e.message}`);
89+
}
90+
}
91+
92+
// Unless you exit the program (e.g. CTRL+C), channelz data will be available for querying.
93+
// Users can take time to examine and learn about the info provided by channelz.
94+
setInterval(() => {}, 10000);
95+
}
96+
97+
main();

examples/debugging/server.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
*
3+
* Copyright 2025 gRPC authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
const grpc = require('@grpc/grpc-js');
20+
const protoLoader = require('@grpc/proto-loader');
21+
22+
var PROTO_PATH = __dirname + '/../protos/helloworld.proto';
23+
24+
const packageDefinition = protoLoader.loadSync(
25+
PROTO_PATH,
26+
{keepCase: true,
27+
longs: String,
28+
enums: String,
29+
defaults: true,
30+
oneofs: true
31+
});
32+
var helloProto = grpc.loadPackageDefinition(packageDefinition).helloworld;
33+
34+
const greeterImplementation = {
35+
sayHello: (call, callback) => {
36+
callback(null, { message: `Hello ${call.request.name}`});
37+
}
38+
};
39+
40+
const slowGreeterImplementation = {
41+
sayHello: (call, callback) => {
42+
const waitTimeMs = 100 + (Math.random() * 100)|0;
43+
setTimeout(() => {
44+
callback(null, { message: `Hello ${call.request.name}`});
45+
}, waitTimeMs);
46+
}
47+
}
48+
49+
function serverBindPort(server, port) {
50+
return new Promise((resolve, reject) => {
51+
server.bindAsync(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure(), (error, port) => {
52+
if (error) {
53+
reject(error);
54+
} else {
55+
resolve(port);
56+
}
57+
})
58+
});
59+
}
60+
61+
async function main() {
62+
const channelzServer = new grpc.Server();
63+
grpc.addAdminServicesToServer(channelzServer);
64+
await serverBindPort(channelzServer, 50052);
65+
66+
const server1 = new grpc.Server();
67+
server1.addService(helloProto.Greeter.service, greeterImplementation);
68+
await serverBindPort(server1, 10001);
69+
70+
const server2 = new grpc.Server();
71+
server2.addService(helloProto.Greeter.service, greeterImplementation);
72+
await serverBindPort(server2, 10002);
73+
74+
const server3 = new grpc.Server();
75+
server3.addService(helloProto.Greeter.service, slowGreeterImplementation);
76+
await serverBindPort(server3, 10003);
77+
}
78+
79+
main();

0 commit comments

Comments
 (0)