UNPKG

23.5 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright 2021 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 */
18Object.defineProperty(exports, "__esModule", { value: true });
19exports.setup = exports.getChannelzServiceDefinition = exports.getChannelzHandlers = exports.unregisterChannelzRef = exports.registerChannelzSocket = exports.registerChannelzServer = exports.registerChannelzSubchannel = exports.registerChannelzChannel = exports.ChannelzCallTracker = exports.ChannelzChildrenTracker = exports.ChannelzTrace = void 0;
20const net_1 = require("net");
21const connectivity_state_1 = require("./connectivity-state");
22const constants_1 = require("./constants");
23const subchannel_address_1 = require("./subchannel-address");
24const admin_1 = require("./admin");
25const make_client_1 = require("./make-client");
26function channelRefToMessage(ref) {
27 return {
28 channel_id: ref.id,
29 name: ref.name,
30 };
31}
32function subchannelRefToMessage(ref) {
33 return {
34 subchannel_id: ref.id,
35 name: ref.name,
36 };
37}
38function serverRefToMessage(ref) {
39 return {
40 server_id: ref.id,
41 };
42}
43function socketRefToMessage(ref) {
44 return {
45 socket_id: ref.id,
46 name: ref.name,
47 };
48}
49/**
50 * The loose upper bound on the number of events that should be retained in a
51 * trace. This may be exceeded by up to a factor of 2. Arbitrarily chosen as a
52 * number that should be large enough to contain the recent relevant
53 * information, but small enough to not use excessive memory.
54 */
55const TARGET_RETAINED_TRACES = 32;
56class ChannelzTrace {
57 constructor() {
58 this.events = [];
59 this.eventsLogged = 0;
60 this.creationTimestamp = new Date();
61 }
62 addTrace(severity, description, child) {
63 const timestamp = new Date();
64 this.events.push({
65 description: description,
66 severity: severity,
67 timestamp: timestamp,
68 childChannel: (child === null || child === void 0 ? void 0 : child.kind) === 'channel' ? child : undefined,
69 childSubchannel: (child === null || child === void 0 ? void 0 : child.kind) === 'subchannel' ? child : undefined,
70 });
71 // Whenever the trace array gets too large, discard the first half
72 if (this.events.length >= TARGET_RETAINED_TRACES * 2) {
73 this.events = this.events.slice(TARGET_RETAINED_TRACES);
74 }
75 this.eventsLogged += 1;
76 }
77 getTraceMessage() {
78 return {
79 creation_timestamp: dateToProtoTimestamp(this.creationTimestamp),
80 num_events_logged: this.eventsLogged,
81 events: this.events.map(event => {
82 return {
83 description: event.description,
84 severity: event.severity,
85 timestamp: dateToProtoTimestamp(event.timestamp),
86 channel_ref: event.childChannel
87 ? channelRefToMessage(event.childChannel)
88 : null,
89 subchannel_ref: event.childSubchannel
90 ? subchannelRefToMessage(event.childSubchannel)
91 : null,
92 };
93 }),
94 };
95 }
96}
97exports.ChannelzTrace = ChannelzTrace;
98class ChannelzChildrenTracker {
99 constructor() {
100 this.channelChildren = new Map();
101 this.subchannelChildren = new Map();
102 this.socketChildren = new Map();
103 }
104 refChild(child) {
105 var _a, _b, _c;
106 switch (child.kind) {
107 case 'channel': {
108 const trackedChild = (_a = this.channelChildren.get(child.id)) !== null && _a !== void 0 ? _a : {
109 ref: child,
110 count: 0,
111 };
112 trackedChild.count += 1;
113 this.channelChildren.set(child.id, trackedChild);
114 break;
115 }
116 case 'subchannel': {
117 const trackedChild = (_b = this.subchannelChildren.get(child.id)) !== null && _b !== void 0 ? _b : {
118 ref: child,
119 count: 0,
120 };
121 trackedChild.count += 1;
122 this.subchannelChildren.set(child.id, trackedChild);
123 break;
124 }
125 case 'socket': {
126 const trackedChild = (_c = this.socketChildren.get(child.id)) !== null && _c !== void 0 ? _c : {
127 ref: child,
128 count: 0,
129 };
130 trackedChild.count += 1;
131 this.socketChildren.set(child.id, trackedChild);
132 break;
133 }
134 }
135 }
136 unrefChild(child) {
137 switch (child.kind) {
138 case 'channel': {
139 const trackedChild = this.channelChildren.get(child.id);
140 if (trackedChild !== undefined) {
141 trackedChild.count -= 1;
142 if (trackedChild.count === 0) {
143 this.channelChildren.delete(child.id);
144 }
145 else {
146 this.channelChildren.set(child.id, trackedChild);
147 }
148 }
149 break;
150 }
151 case 'subchannel': {
152 const trackedChild = this.subchannelChildren.get(child.id);
153 if (trackedChild !== undefined) {
154 trackedChild.count -= 1;
155 if (trackedChild.count === 0) {
156 this.subchannelChildren.delete(child.id);
157 }
158 else {
159 this.subchannelChildren.set(child.id, trackedChild);
160 }
161 }
162 break;
163 }
164 case 'socket': {
165 const trackedChild = this.socketChildren.get(child.id);
166 if (trackedChild !== undefined) {
167 trackedChild.count -= 1;
168 if (trackedChild.count === 0) {
169 this.socketChildren.delete(child.id);
170 }
171 else {
172 this.socketChildren.set(child.id, trackedChild);
173 }
174 }
175 break;
176 }
177 }
178 }
179 getChildLists() {
180 const channels = [];
181 for (const { ref } of this.channelChildren.values()) {
182 channels.push(ref);
183 }
184 const subchannels = [];
185 for (const { ref } of this.subchannelChildren.values()) {
186 subchannels.push(ref);
187 }
188 const sockets = [];
189 for (const { ref } of this.socketChildren.values()) {
190 sockets.push(ref);
191 }
192 return { channels, subchannels, sockets };
193 }
194}
195exports.ChannelzChildrenTracker = ChannelzChildrenTracker;
196class ChannelzCallTracker {
197 constructor() {
198 this.callsStarted = 0;
199 this.callsSucceeded = 0;
200 this.callsFailed = 0;
201 this.lastCallStartedTimestamp = null;
202 }
203 addCallStarted() {
204 this.callsStarted += 1;
205 this.lastCallStartedTimestamp = new Date();
206 }
207 addCallSucceeded() {
208 this.callsSucceeded += 1;
209 }
210 addCallFailed() {
211 this.callsFailed += 1;
212 }
213}
214exports.ChannelzCallTracker = ChannelzCallTracker;
215let nextId = 1;
216function getNextId() {
217 return nextId++;
218}
219const channels = [];
220const subchannels = [];
221const servers = [];
222const sockets = [];
223function registerChannelzChannel(name, getInfo, channelzEnabled) {
224 const id = getNextId();
225 const ref = { id, name, kind: 'channel' };
226 if (channelzEnabled) {
227 channels[id] = { ref, getInfo };
228 }
229 return ref;
230}
231exports.registerChannelzChannel = registerChannelzChannel;
232function registerChannelzSubchannel(name, getInfo, channelzEnabled) {
233 const id = getNextId();
234 const ref = { id, name, kind: 'subchannel' };
235 if (channelzEnabled) {
236 subchannels[id] = { ref, getInfo };
237 }
238 return ref;
239}
240exports.registerChannelzSubchannel = registerChannelzSubchannel;
241function registerChannelzServer(getInfo, channelzEnabled) {
242 const id = getNextId();
243 const ref = { id, kind: 'server' };
244 if (channelzEnabled) {
245 servers[id] = { ref, getInfo };
246 }
247 return ref;
248}
249exports.registerChannelzServer = registerChannelzServer;
250function registerChannelzSocket(name, getInfo, channelzEnabled) {
251 const id = getNextId();
252 const ref = { id, name, kind: 'socket' };
253 if (channelzEnabled) {
254 sockets[id] = { ref, getInfo };
255 }
256 return ref;
257}
258exports.registerChannelzSocket = registerChannelzSocket;
259function unregisterChannelzRef(ref) {
260 switch (ref.kind) {
261 case 'channel':
262 delete channels[ref.id];
263 return;
264 case 'subchannel':
265 delete subchannels[ref.id];
266 return;
267 case 'server':
268 delete servers[ref.id];
269 return;
270 case 'socket':
271 delete sockets[ref.id];
272 return;
273 }
274}
275exports.unregisterChannelzRef = unregisterChannelzRef;
276/**
277 * Parse a single section of an IPv6 address as two bytes
278 * @param addressSection A hexadecimal string of length up to 4
279 * @returns The pair of bytes representing this address section
280 */
281function parseIPv6Section(addressSection) {
282 const numberValue = Number.parseInt(addressSection, 16);
283 return [(numberValue / 256) | 0, numberValue % 256];
284}
285/**
286 * Parse a chunk of an IPv6 address string to some number of bytes
287 * @param addressChunk Some number of segments of up to 4 hexadecimal
288 * characters each, joined by colons.
289 * @returns The list of bytes representing this address chunk
290 */
291function parseIPv6Chunk(addressChunk) {
292 if (addressChunk === '') {
293 return [];
294 }
295 const bytePairs = addressChunk
296 .split(':')
297 .map(section => parseIPv6Section(section));
298 const result = [];
299 return result.concat(...bytePairs);
300}
301/**
302 * Converts an IPv4 or IPv6 address from string representation to binary
303 * representation
304 * @param ipAddress an IP address in standard IPv4 or IPv6 text format
305 * @returns
306 */
307function ipAddressStringToBuffer(ipAddress) {
308 if ((0, net_1.isIPv4)(ipAddress)) {
309 return Buffer.from(Uint8Array.from(ipAddress.split('.').map(segment => Number.parseInt(segment))));
310 }
311 else if ((0, net_1.isIPv6)(ipAddress)) {
312 let leftSection;
313 let rightSection;
314 const doubleColonIndex = ipAddress.indexOf('::');
315 if (doubleColonIndex === -1) {
316 leftSection = ipAddress;
317 rightSection = '';
318 }
319 else {
320 leftSection = ipAddress.substring(0, doubleColonIndex);
321 rightSection = ipAddress.substring(doubleColonIndex + 2);
322 }
323 const leftBuffer = Buffer.from(parseIPv6Chunk(leftSection));
324 const rightBuffer = Buffer.from(parseIPv6Chunk(rightSection));
325 const middleBuffer = Buffer.alloc(16 - leftBuffer.length - rightBuffer.length, 0);
326 return Buffer.concat([leftBuffer, middleBuffer, rightBuffer]);
327 }
328 else {
329 return null;
330 }
331}
332function connectivityStateToMessage(state) {
333 switch (state) {
334 case connectivity_state_1.ConnectivityState.CONNECTING:
335 return {
336 state: 'CONNECTING',
337 };
338 case connectivity_state_1.ConnectivityState.IDLE:
339 return {
340 state: 'IDLE',
341 };
342 case connectivity_state_1.ConnectivityState.READY:
343 return {
344 state: 'READY',
345 };
346 case connectivity_state_1.ConnectivityState.SHUTDOWN:
347 return {
348 state: 'SHUTDOWN',
349 };
350 case connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE:
351 return {
352 state: 'TRANSIENT_FAILURE',
353 };
354 default:
355 return {
356 state: 'UNKNOWN',
357 };
358 }
359}
360function dateToProtoTimestamp(date) {
361 if (!date) {
362 return null;
363 }
364 const millisSinceEpoch = date.getTime();
365 return {
366 seconds: (millisSinceEpoch / 1000) | 0,
367 nanos: (millisSinceEpoch % 1000) * 1000000,
368 };
369}
370function getChannelMessage(channelEntry) {
371 const resolvedInfo = channelEntry.getInfo();
372 return {
373 ref: channelRefToMessage(channelEntry.ref),
374 data: {
375 target: resolvedInfo.target,
376 state: connectivityStateToMessage(resolvedInfo.state),
377 calls_started: resolvedInfo.callTracker.callsStarted,
378 calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
379 calls_failed: resolvedInfo.callTracker.callsFailed,
380 last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
381 trace: resolvedInfo.trace.getTraceMessage(),
382 },
383 channel_ref: resolvedInfo.children.channels.map(ref => channelRefToMessage(ref)),
384 subchannel_ref: resolvedInfo.children.subchannels.map(ref => subchannelRefToMessage(ref)),
385 };
386}
387function GetChannel(call, callback) {
388 const channelId = Number.parseInt(call.request.channel_id);
389 const channelEntry = channels[channelId];
390 if (channelEntry === undefined) {
391 callback({
392 code: constants_1.Status.NOT_FOUND,
393 details: 'No channel data found for id ' + channelId,
394 });
395 return;
396 }
397 callback(null, { channel: getChannelMessage(channelEntry) });
398}
399function GetTopChannels(call, callback) {
400 const maxResults = Number.parseInt(call.request.max_results);
401 const resultList = [];
402 let i = Number.parseInt(call.request.start_channel_id);
403 for (; i < channels.length; i++) {
404 const channelEntry = channels[i];
405 if (channelEntry === undefined) {
406 continue;
407 }
408 resultList.push(getChannelMessage(channelEntry));
409 if (resultList.length >= maxResults) {
410 break;
411 }
412 }
413 callback(null, {
414 channel: resultList,
415 end: i >= servers.length,
416 });
417}
418function getServerMessage(serverEntry) {
419 const resolvedInfo = serverEntry.getInfo();
420 return {
421 ref: serverRefToMessage(serverEntry.ref),
422 data: {
423 calls_started: resolvedInfo.callTracker.callsStarted,
424 calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
425 calls_failed: resolvedInfo.callTracker.callsFailed,
426 last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
427 trace: resolvedInfo.trace.getTraceMessage(),
428 },
429 listen_socket: resolvedInfo.listenerChildren.sockets.map(ref => socketRefToMessage(ref)),
430 };
431}
432function GetServer(call, callback) {
433 const serverId = Number.parseInt(call.request.server_id);
434 const serverEntry = servers[serverId];
435 if (serverEntry === undefined) {
436 callback({
437 code: constants_1.Status.NOT_FOUND,
438 details: 'No server data found for id ' + serverId,
439 });
440 return;
441 }
442 callback(null, { server: getServerMessage(serverEntry) });
443}
444function GetServers(call, callback) {
445 const maxResults = Number.parseInt(call.request.max_results);
446 const resultList = [];
447 let i = Number.parseInt(call.request.start_server_id);
448 for (; i < servers.length; i++) {
449 const serverEntry = servers[i];
450 if (serverEntry === undefined) {
451 continue;
452 }
453 resultList.push(getServerMessage(serverEntry));
454 if (resultList.length >= maxResults) {
455 break;
456 }
457 }
458 callback(null, {
459 server: resultList,
460 end: i >= servers.length,
461 });
462}
463function GetSubchannel(call, callback) {
464 const subchannelId = Number.parseInt(call.request.subchannel_id);
465 const subchannelEntry = subchannels[subchannelId];
466 if (subchannelEntry === undefined) {
467 callback({
468 code: constants_1.Status.NOT_FOUND,
469 details: 'No subchannel data found for id ' + subchannelId,
470 });
471 return;
472 }
473 const resolvedInfo = subchannelEntry.getInfo();
474 const subchannelMessage = {
475 ref: subchannelRefToMessage(subchannelEntry.ref),
476 data: {
477 target: resolvedInfo.target,
478 state: connectivityStateToMessage(resolvedInfo.state),
479 calls_started: resolvedInfo.callTracker.callsStarted,
480 calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
481 calls_failed: resolvedInfo.callTracker.callsFailed,
482 last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
483 trace: resolvedInfo.trace.getTraceMessage(),
484 },
485 socket_ref: resolvedInfo.children.sockets.map(ref => socketRefToMessage(ref)),
486 };
487 callback(null, { subchannel: subchannelMessage });
488}
489function subchannelAddressToAddressMessage(subchannelAddress) {
490 var _a;
491 if ((0, subchannel_address_1.isTcpSubchannelAddress)(subchannelAddress)) {
492 return {
493 address: 'tcpip_address',
494 tcpip_address: {
495 ip_address: (_a = ipAddressStringToBuffer(subchannelAddress.host)) !== null && _a !== void 0 ? _a : undefined,
496 port: subchannelAddress.port,
497 },
498 };
499 }
500 else {
501 return {
502 address: 'uds_address',
503 uds_address: {
504 filename: subchannelAddress.path,
505 },
506 };
507 }
508}
509function GetSocket(call, callback) {
510 var _a, _b, _c, _d, _e;
511 const socketId = Number.parseInt(call.request.socket_id);
512 const socketEntry = sockets[socketId];
513 if (socketEntry === undefined) {
514 callback({
515 code: constants_1.Status.NOT_FOUND,
516 details: 'No socket data found for id ' + socketId,
517 });
518 return;
519 }
520 const resolvedInfo = socketEntry.getInfo();
521 const securityMessage = resolvedInfo.security
522 ? {
523 model: 'tls',
524 tls: {
525 cipher_suite: resolvedInfo.security.cipherSuiteStandardName
526 ? 'standard_name'
527 : 'other_name',
528 standard_name: (_a = resolvedInfo.security.cipherSuiteStandardName) !== null && _a !== void 0 ? _a : undefined,
529 other_name: (_b = resolvedInfo.security.cipherSuiteOtherName) !== null && _b !== void 0 ? _b : undefined,
530 local_certificate: (_c = resolvedInfo.security.localCertificate) !== null && _c !== void 0 ? _c : undefined,
531 remote_certificate: (_d = resolvedInfo.security.remoteCertificate) !== null && _d !== void 0 ? _d : undefined,
532 },
533 }
534 : null;
535 const socketMessage = {
536 ref: socketRefToMessage(socketEntry.ref),
537 local: resolvedInfo.localAddress
538 ? subchannelAddressToAddressMessage(resolvedInfo.localAddress)
539 : null,
540 remote: resolvedInfo.remoteAddress
541 ? subchannelAddressToAddressMessage(resolvedInfo.remoteAddress)
542 : null,
543 remote_name: (_e = resolvedInfo.remoteName) !== null && _e !== void 0 ? _e : undefined,
544 security: securityMessage,
545 data: {
546 keep_alives_sent: resolvedInfo.keepAlivesSent,
547 streams_started: resolvedInfo.streamsStarted,
548 streams_succeeded: resolvedInfo.streamsSucceeded,
549 streams_failed: resolvedInfo.streamsFailed,
550 last_local_stream_created_timestamp: dateToProtoTimestamp(resolvedInfo.lastLocalStreamCreatedTimestamp),
551 last_remote_stream_created_timestamp: dateToProtoTimestamp(resolvedInfo.lastRemoteStreamCreatedTimestamp),
552 messages_received: resolvedInfo.messagesReceived,
553 messages_sent: resolvedInfo.messagesSent,
554 last_message_received_timestamp: dateToProtoTimestamp(resolvedInfo.lastMessageReceivedTimestamp),
555 last_message_sent_timestamp: dateToProtoTimestamp(resolvedInfo.lastMessageSentTimestamp),
556 local_flow_control_window: resolvedInfo.localFlowControlWindow
557 ? { value: resolvedInfo.localFlowControlWindow }
558 : null,
559 remote_flow_control_window: resolvedInfo.remoteFlowControlWindow
560 ? { value: resolvedInfo.remoteFlowControlWindow }
561 : null,
562 },
563 };
564 callback(null, { socket: socketMessage });
565}
566function GetServerSockets(call, callback) {
567 const serverId = Number.parseInt(call.request.server_id);
568 const serverEntry = servers[serverId];
569 if (serverEntry === undefined) {
570 callback({
571 code: constants_1.Status.NOT_FOUND,
572 details: 'No server data found for id ' + serverId,
573 });
574 return;
575 }
576 const startId = Number.parseInt(call.request.start_socket_id);
577 const maxResults = Number.parseInt(call.request.max_results);
578 const resolvedInfo = serverEntry.getInfo();
579 // If we wanted to include listener sockets in the result, this line would
580 // instead say
581 // const allSockets = resolvedInfo.listenerChildren.sockets.concat(resolvedInfo.sessionChildren.sockets).sort((ref1, ref2) => ref1.id - ref2.id);
582 const allSockets = resolvedInfo.sessionChildren.sockets.sort((ref1, ref2) => ref1.id - ref2.id);
583 const resultList = [];
584 let i = 0;
585 for (; i < allSockets.length; i++) {
586 if (allSockets[i].id >= startId) {
587 resultList.push(socketRefToMessage(allSockets[i]));
588 if (resultList.length >= maxResults) {
589 break;
590 }
591 }
592 }
593 callback(null, {
594 socket_ref: resultList,
595 end: i >= allSockets.length,
596 });
597}
598function getChannelzHandlers() {
599 return {
600 GetChannel,
601 GetTopChannels,
602 GetServer,
603 GetServers,
604 GetSubchannel,
605 GetSocket,
606 GetServerSockets,
607 };
608}
609exports.getChannelzHandlers = getChannelzHandlers;
610let loadedChannelzDefinition = null;
611function getChannelzServiceDefinition() {
612 if (loadedChannelzDefinition) {
613 return loadedChannelzDefinition;
614 }
615 /* The purpose of this complexity is to avoid loading @grpc/proto-loader at
616 * runtime for users who will not use/enable channelz. */
617 const loaderLoadSync = require('@grpc/proto-loader')
618 .loadSync;
619 const loadedProto = loaderLoadSync('channelz.proto', {
620 keepCase: true,
621 longs: String,
622 enums: String,
623 defaults: true,
624 oneofs: true,
625 includeDirs: [`${__dirname}/../../proto`],
626 });
627 const channelzGrpcObject = (0, make_client_1.loadPackageDefinition)(loadedProto);
628 loadedChannelzDefinition =
629 channelzGrpcObject.grpc.channelz.v1.Channelz.service;
630 return loadedChannelzDefinition;
631}
632exports.getChannelzServiceDefinition = getChannelzServiceDefinition;
633function setup() {
634 (0, admin_1.registerAdminService)(getChannelzServiceDefinition, getChannelzHandlers);
635}
636exports.setup = setup;
637//# sourceMappingURL=channelz.js.map
\No newline at end of file