UNPKG

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