1 //
2 //
3 // Copyright 2017 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 #include <grpc/support/port_platform.h>
20
21 #include "src/core/lib/channel/channelz.h"
22
23 #include <algorithm>
24 #include <atomic>
25 #include <cstdint>
26
27 #include "absl/status/statusor.h"
28 #include "absl/strings/escaping.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/strip.h"
31
32 #include <grpc/support/json.h>
33 #include <grpc/support/log.h>
34 #include <grpc/support/time.h>
35
36 #include "src/core/lib/address_utils/parse_address.h"
37 #include "src/core/lib/address_utils/sockaddr_utils.h"
38 #include "src/core/lib/channel/channel_args.h"
39 #include "src/core/lib/channel/channelz_registry.h"
40 #include "src/core/lib/gpr/string.h"
41 #include "src/core/lib/gpr/useful.h"
42 #include "src/core/lib/iomgr/resolved_address.h"
43 #include "src/core/lib/json/json_writer.h"
44 #include "src/core/lib/transport/connectivity_state.h"
45 #include "src/core/lib/uri/uri_parser.h"
46
47 namespace grpc_core {
48 namespace channelz {
49
50 //
51 // BaseNode
52 //
53
BaseNode(EntityType type,std::string name)54 BaseNode::BaseNode(EntityType type, std::string name)
55 : type_(type), uuid_(-1), name_(std::move(name)) {
56 // The registry will set uuid_ under its lock.
57 ChannelzRegistry::Register(this);
58 }
59
~BaseNode()60 BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
61
RenderJsonString()62 std::string BaseNode::RenderJsonString() {
63 Json json = RenderJson();
64 return JsonDump(json);
65 }
66
67 //
68 // CallCountingHelper
69 //
70
RecordCallStarted()71 void CallCountingHelper::RecordCallStarted() {
72 calls_started_.fetch_add(1, std::memory_order_relaxed);
73 last_call_started_cycle_.store(gpr_get_cycle_counter(),
74 std::memory_order_relaxed);
75 }
76
RecordCallFailed()77 void CallCountingHelper::RecordCallFailed() {
78 calls_failed_.fetch_add(1, std::memory_order_relaxed);
79 }
80
RecordCallSucceeded()81 void CallCountingHelper::RecordCallSucceeded() {
82 calls_succeeded_.fetch_add(1, std::memory_order_relaxed);
83 }
84
PopulateCallCounts(Json::Object * json)85 void CallCountingHelper::PopulateCallCounts(Json::Object* json) {
86 auto calls_started = calls_started_.load(std::memory_order_relaxed);
87 auto calls_succeeded = calls_succeeded_.load(std::memory_order_relaxed);
88 auto calls_failed = calls_failed_.load(std::memory_order_relaxed);
89 auto last_call_started_cycle =
90 last_call_started_cycle_.load(std::memory_order_relaxed);
91 if (calls_started != 0) {
92 (*json)["callsStarted"] = Json::FromString(absl::StrCat(calls_started));
93 gpr_timespec ts = gpr_convert_clock_type(
94 gpr_cycle_counter_to_time(last_call_started_cycle), GPR_CLOCK_REALTIME);
95 (*json)["lastCallStartedTimestamp"] =
96 Json::FromString(gpr_format_timespec(ts));
97 }
98 if (calls_succeeded != 0) {
99 (*json)["callsSucceeded"] = Json::FromString(absl::StrCat(calls_succeeded));
100 }
101 if (calls_failed != 0) {
102 (*json)["callsFailed"] = Json::FromString(absl::StrCat(calls_failed));
103 }
104 }
105
106 //
107 // PerCpuCallCountingHelper
108 //
109
RecordCallStarted()110 void PerCpuCallCountingHelper::RecordCallStarted() {
111 auto& data = per_cpu_data_.this_cpu();
112 data.calls_started.fetch_add(1, std::memory_order_relaxed);
113 data.last_call_started_cycle.store(gpr_get_cycle_counter(),
114 std::memory_order_relaxed);
115 }
116
RecordCallFailed()117 void PerCpuCallCountingHelper::RecordCallFailed() {
118 per_cpu_data_.this_cpu().calls_failed.fetch_add(1, std::memory_order_relaxed);
119 }
120
RecordCallSucceeded()121 void PerCpuCallCountingHelper::RecordCallSucceeded() {
122 per_cpu_data_.this_cpu().calls_succeeded.fetch_add(1,
123 std::memory_order_relaxed);
124 }
125
PopulateCallCounts(Json::Object * json)126 void PerCpuCallCountingHelper::PopulateCallCounts(Json::Object* json) {
127 int64_t calls_started = 0;
128 int64_t calls_succeeded = 0;
129 int64_t calls_failed = 0;
130 gpr_cycle_counter last_call_started_cycle = 0;
131 for (const auto& cpu : per_cpu_data_) {
132 calls_started += cpu.calls_started.load(std::memory_order_relaxed);
133 calls_succeeded += cpu.calls_succeeded.load(std::memory_order_relaxed);
134 calls_failed += cpu.calls_failed.load(std::memory_order_relaxed);
135 last_call_started_cycle =
136 std::max(last_call_started_cycle,
137 cpu.last_call_started_cycle.load(std::memory_order_relaxed));
138 }
139
140 if (calls_started != 0) {
141 (*json)["callsStarted"] = Json::FromString(absl::StrCat(calls_started));
142 gpr_timespec ts = gpr_convert_clock_type(
143 gpr_cycle_counter_to_time(last_call_started_cycle), GPR_CLOCK_REALTIME);
144 (*json)["lastCallStartedTimestamp"] =
145 Json::FromString(gpr_format_timespec(ts));
146 }
147 if (calls_succeeded != 0) {
148 (*json)["callsSucceeded"] = Json::FromString(absl::StrCat(calls_succeeded));
149 }
150 if (calls_failed != 0) {
151 (*json)["callsFailed"] = Json::FromString(absl::StrCat(calls_failed));
152 }
153 }
154
155 //
156 // ChannelNode
157 //
158
ChannelNode(std::string target,size_t channel_tracer_max_nodes,bool is_internal_channel)159 ChannelNode::ChannelNode(std::string target, size_t channel_tracer_max_nodes,
160 bool is_internal_channel)
161 : BaseNode(is_internal_channel ? EntityType::kInternalChannel
162 : EntityType::kTopLevelChannel,
163 target),
164 target_(std::move(target)),
165 trace_(channel_tracer_max_nodes) {}
166
GetChannelConnectivityStateChangeString(grpc_connectivity_state state)167 const char* ChannelNode::GetChannelConnectivityStateChangeString(
168 grpc_connectivity_state state) {
169 switch (state) {
170 case GRPC_CHANNEL_IDLE:
171 return "Channel state change to IDLE";
172 case GRPC_CHANNEL_CONNECTING:
173 return "Channel state change to CONNECTING";
174 case GRPC_CHANNEL_READY:
175 return "Channel state change to READY";
176 case GRPC_CHANNEL_TRANSIENT_FAILURE:
177 return "Channel state change to TRANSIENT_FAILURE";
178 case GRPC_CHANNEL_SHUTDOWN:
179 return "Channel state change to SHUTDOWN";
180 }
181 GPR_UNREACHABLE_CODE(return "UNKNOWN");
182 }
183
RenderJson()184 Json ChannelNode::RenderJson() {
185 Json::Object data = {
186 {"target", Json::FromString(target_)},
187 };
188 // Connectivity state.
189 // If low-order bit is on, then the field is set.
190 int state_field = connectivity_state_.load(std::memory_order_relaxed);
191 if ((state_field & 1) != 0) {
192 grpc_connectivity_state state =
193 static_cast<grpc_connectivity_state>(state_field >> 1);
194 data["state"] = Json::FromObject({
195 {"state", Json::FromString(ConnectivityStateName(state))},
196 });
197 }
198 // Fill in the channel trace if applicable.
199 Json trace_json = trace_.RenderJson();
200 if (trace_json.type() != Json::Type::kNull) {
201 data["trace"] = std::move(trace_json);
202 }
203 // Ask CallCountingHelper to populate call count data.
204 call_counter_.PopulateCallCounts(&data);
205 // Construct outer object.
206 Json::Object json = {
207 {"ref", Json::FromObject({
208 {"channelId", Json::FromString(absl::StrCat(uuid()))},
209 })},
210 {"data", Json::FromObject(std::move(data))},
211 };
212 // Template method. Child classes may override this to add their specific
213 // functionality.
214 PopulateChildRefs(&json);
215 return Json::FromObject(std::move(json));
216 }
217
PopulateChildRefs(Json::Object * json)218 void ChannelNode::PopulateChildRefs(Json::Object* json) {
219 MutexLock lock(&child_mu_);
220 if (!child_subchannels_.empty()) {
221 Json::Array array;
222 for (intptr_t subchannel_uuid : child_subchannels_) {
223 array.emplace_back(Json::FromObject({
224 {"subchannelId", Json::FromString(absl::StrCat(subchannel_uuid))},
225 }));
226 }
227 (*json)["subchannelRef"] = Json::FromArray(std::move(array));
228 }
229 if (!child_channels_.empty()) {
230 Json::Array array;
231 for (intptr_t channel_uuid : child_channels_) {
232 array.emplace_back(Json::FromObject({
233 {"channelId", Json::FromString(absl::StrCat(channel_uuid))},
234 }));
235 }
236 (*json)["channelRef"] = Json::FromArray(std::move(array));
237 }
238 }
239
SetConnectivityState(grpc_connectivity_state state)240 void ChannelNode::SetConnectivityState(grpc_connectivity_state state) {
241 // Store with low-order bit set to indicate that the field is set.
242 int state_field = (state << 1) + 1;
243 connectivity_state_.store(state_field, std::memory_order_relaxed);
244 }
245
AddChildChannel(intptr_t child_uuid)246 void ChannelNode::AddChildChannel(intptr_t child_uuid) {
247 MutexLock lock(&child_mu_);
248 child_channels_.insert(child_uuid);
249 }
250
RemoveChildChannel(intptr_t child_uuid)251 void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
252 MutexLock lock(&child_mu_);
253 child_channels_.erase(child_uuid);
254 }
255
AddChildSubchannel(intptr_t child_uuid)256 void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
257 MutexLock lock(&child_mu_);
258 child_subchannels_.insert(child_uuid);
259 }
260
RemoveChildSubchannel(intptr_t child_uuid)261 void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
262 MutexLock lock(&child_mu_);
263 child_subchannels_.erase(child_uuid);
264 }
265
266 //
267 // ServerNode
268 //
269
ServerNode(size_t channel_tracer_max_nodes)270 ServerNode::ServerNode(size_t channel_tracer_max_nodes)
271 : BaseNode(EntityType::kServer, ""), trace_(channel_tracer_max_nodes) {}
272
~ServerNode()273 ServerNode::~ServerNode() {}
274
AddChildSocket(RefCountedPtr<SocketNode> node)275 void ServerNode::AddChildSocket(RefCountedPtr<SocketNode> node) {
276 MutexLock lock(&child_mu_);
277 child_sockets_.insert(std::make_pair(node->uuid(), std::move(node)));
278 }
279
RemoveChildSocket(intptr_t child_uuid)280 void ServerNode::RemoveChildSocket(intptr_t child_uuid) {
281 MutexLock lock(&child_mu_);
282 child_sockets_.erase(child_uuid);
283 }
284
AddChildListenSocket(RefCountedPtr<ListenSocketNode> node)285 void ServerNode::AddChildListenSocket(RefCountedPtr<ListenSocketNode> node) {
286 MutexLock lock(&child_mu_);
287 child_listen_sockets_.insert(std::make_pair(node->uuid(), std::move(node)));
288 }
289
RemoveChildListenSocket(intptr_t child_uuid)290 void ServerNode::RemoveChildListenSocket(intptr_t child_uuid) {
291 MutexLock lock(&child_mu_);
292 child_listen_sockets_.erase(child_uuid);
293 }
294
RenderServerSockets(intptr_t start_socket_id,intptr_t max_results)295 std::string ServerNode::RenderServerSockets(intptr_t start_socket_id,
296 intptr_t max_results) {
297 GPR_ASSERT(start_socket_id >= 0);
298 GPR_ASSERT(max_results >= 0);
299 // If user does not set max_results, we choose 500.
300 size_t pagination_limit = max_results == 0 ? 500 : max_results;
301 Json::Object object;
302 {
303 MutexLock lock(&child_mu_);
304 size_t sockets_rendered = 0;
305 // Create list of socket refs.
306 Json::Array array;
307 auto it = child_sockets_.lower_bound(start_socket_id);
308 for (; it != child_sockets_.end() && sockets_rendered < pagination_limit;
309 ++it, ++sockets_rendered) {
310 array.emplace_back(Json::FromObject({
311 {"socketId", Json::FromString(absl::StrCat(it->first))},
312 {"name", Json::FromString(it->second->name())},
313 }));
314 }
315 object["socketRef"] = Json::FromArray(std::move(array));
316 if (it == child_sockets_.end()) {
317 object["end"] = Json::FromBool(true);
318 }
319 }
320 return JsonDump(Json::FromObject(std::move(object)));
321 }
322
RenderJson()323 Json ServerNode::RenderJson() {
324 Json::Object data;
325 // Fill in the channel trace if applicable.
326 Json trace_json = trace_.RenderJson();
327 if (trace_json.type() != Json::Type::kNull) {
328 data["trace"] = std::move(trace_json);
329 }
330 // Ask CallCountingHelper to populate call count data.
331 call_counter_.PopulateCallCounts(&data);
332 // Construct top-level object.
333 Json::Object object = {
334 {"ref", Json::FromObject({
335 {"serverId", Json::FromString(absl::StrCat(uuid()))},
336 })},
337 {"data", Json::FromObject(std::move(data))},
338 };
339 // Render listen sockets.
340 {
341 MutexLock lock(&child_mu_);
342 if (!child_listen_sockets_.empty()) {
343 Json::Array array;
344 for (const auto& it : child_listen_sockets_) {
345 array.emplace_back(Json::FromObject({
346 {"socketId", Json::FromString(absl::StrCat(it.first))},
347 {"name", Json::FromString(it.second->name())},
348 }));
349 }
350 object["listenSocket"] = Json::FromArray(std::move(array));
351 }
352 }
353 return Json::FromObject(std::move(object));
354 }
355
356 //
357 // SocketNode::Security::Tls
358 //
359
RenderJson()360 Json SocketNode::Security::Tls::RenderJson() {
361 Json::Object data;
362 if (type == NameType::kStandardName) {
363 data["standard_name"] = Json::FromString(name);
364 } else if (type == NameType::kOtherName) {
365 data["other_name"] = Json::FromString(name);
366 }
367 if (!local_certificate.empty()) {
368 data["local_certificate"] =
369 Json::FromString(absl::Base64Escape(local_certificate));
370 }
371 if (!remote_certificate.empty()) {
372 data["remote_certificate"] =
373 Json::FromString(absl::Base64Escape(remote_certificate));
374 }
375 return Json::FromObject(std::move(data));
376 }
377
378 //
379 // SocketNode::Security
380 //
381
RenderJson()382 Json SocketNode::Security::RenderJson() {
383 Json::Object data;
384 switch (type) {
385 case ModelType::kUnset:
386 break;
387 case ModelType::kTls:
388 if (tls) {
389 data["tls"] = tls->RenderJson();
390 }
391 break;
392 case ModelType::kOther:
393 if (other.has_value()) {
394 data["other"] = *other;
395 }
396 break;
397 }
398 return Json::FromObject(std::move(data));
399 }
400
401 namespace {
402
SecurityArgCopy(void * p)403 void* SecurityArgCopy(void* p) {
404 SocketNode::Security* xds_certificate_provider =
405 static_cast<SocketNode::Security*>(p);
406 return xds_certificate_provider->Ref().release();
407 }
408
SecurityArgDestroy(void * p)409 void SecurityArgDestroy(void* p) {
410 SocketNode::Security* xds_certificate_provider =
411 static_cast<SocketNode::Security*>(p);
412 xds_certificate_provider->Unref();
413 }
414
SecurityArgCmp(void * p,void * q)415 int SecurityArgCmp(void* p, void* q) { return QsortCompare(p, q); }
416
417 const grpc_arg_pointer_vtable kChannelArgVtable = {
418 SecurityArgCopy, SecurityArgDestroy, SecurityArgCmp};
419
420 } // namespace
421
MakeChannelArg() const422 grpc_arg SocketNode::Security::MakeChannelArg() const {
423 return grpc_channel_arg_pointer_create(
424 const_cast<char*>(GRPC_ARG_CHANNELZ_SECURITY),
425 const_cast<SocketNode::Security*>(this), &kChannelArgVtable);
426 }
427
GetFromChannelArgs(const grpc_channel_args * args)428 RefCountedPtr<SocketNode::Security> SocketNode::Security::GetFromChannelArgs(
429 const grpc_channel_args* args) {
430 Security* security = grpc_channel_args_find_pointer<Security>(
431 args, GRPC_ARG_CHANNELZ_SECURITY);
432 return security != nullptr ? security->Ref() : nullptr;
433 }
434
435 //
436 // SocketNode
437 //
438
439 namespace {
440
PopulateSocketAddressJson(Json::Object * json,const char * name,const char * addr_str)441 void PopulateSocketAddressJson(Json::Object* json, const char* name,
442 const char* addr_str) {
443 if (addr_str == nullptr) return;
444 absl::StatusOr<URI> uri = URI::Parse(addr_str);
445 if (uri.ok()) {
446 if (uri->scheme() == "ipv4" || uri->scheme() == "ipv6") {
447 auto address = StringToSockaddr(absl::StripPrefix(uri->path(), "/"));
448 if (address.ok()) {
449 std::string packed_host = grpc_sockaddr_get_packed_host(&*address);
450 (*json)[name] = Json::FromObject({
451 {"tcpip_address",
452 Json::FromObject({
453 {"port", Json::FromString(
454 absl::StrCat(grpc_sockaddr_get_port(&*address)))},
455 {"ip_address",
456 Json::FromString(absl::Base64Escape(packed_host))},
457 })},
458 });
459 return;
460 }
461 } else if (uri->scheme() == "unix") {
462 (*json)[name] = Json::FromObject({
463 {"uds_address", Json::FromObject({
464 {"filename", Json::FromString(uri->path())},
465 })},
466 });
467 return;
468 }
469 }
470 // Unknown address type.
471 (*json)[name] = Json::FromObject({
472 {"other_address", Json::FromObject({
473 {"name", Json::FromString(addr_str)},
474 })},
475 });
476 }
477
478 } // namespace
479
SocketNode(std::string local,std::string remote,std::string name,RefCountedPtr<Security> security)480 SocketNode::SocketNode(std::string local, std::string remote, std::string name,
481 RefCountedPtr<Security> security)
482 : BaseNode(EntityType::kSocket, std::move(name)),
483 local_(std::move(local)),
484 remote_(std::move(remote)),
485 security_(std::move(security)) {}
486
RecordStreamStartedFromLocal()487 void SocketNode::RecordStreamStartedFromLocal() {
488 streams_started_.fetch_add(1, std::memory_order_relaxed);
489 last_local_stream_created_cycle_.store(gpr_get_cycle_counter(),
490 std::memory_order_relaxed);
491 }
492
RecordStreamStartedFromRemote()493 void SocketNode::RecordStreamStartedFromRemote() {
494 streams_started_.fetch_add(1, std::memory_order_relaxed);
495 last_remote_stream_created_cycle_.store(gpr_get_cycle_counter(),
496 std::memory_order_relaxed);
497 }
498
RecordMessagesSent(uint32_t num_sent)499 void SocketNode::RecordMessagesSent(uint32_t num_sent) {
500 messages_sent_.fetch_add(num_sent, std::memory_order_relaxed);
501 last_message_sent_cycle_.store(gpr_get_cycle_counter(),
502 std::memory_order_relaxed);
503 }
504
RecordMessageReceived()505 void SocketNode::RecordMessageReceived() {
506 messages_received_.fetch_add(1, std::memory_order_relaxed);
507 last_message_received_cycle_.store(gpr_get_cycle_counter(),
508 std::memory_order_relaxed);
509 }
510
RenderJson()511 Json SocketNode::RenderJson() {
512 // Create and fill the data child.
513 Json::Object data;
514 gpr_timespec ts;
515 int64_t streams_started = streams_started_.load(std::memory_order_relaxed);
516 if (streams_started != 0) {
517 data["streamsStarted"] = Json::FromString(absl::StrCat(streams_started));
518 gpr_cycle_counter last_local_stream_created_cycle =
519 last_local_stream_created_cycle_.load(std::memory_order_relaxed);
520 if (last_local_stream_created_cycle != 0) {
521 ts = gpr_convert_clock_type(
522 gpr_cycle_counter_to_time(last_local_stream_created_cycle),
523 GPR_CLOCK_REALTIME);
524 data["lastLocalStreamCreatedTimestamp"] =
525 Json::FromString(gpr_format_timespec(ts));
526 }
527 gpr_cycle_counter last_remote_stream_created_cycle =
528 last_remote_stream_created_cycle_.load(std::memory_order_relaxed);
529 if (last_remote_stream_created_cycle != 0) {
530 ts = gpr_convert_clock_type(
531 gpr_cycle_counter_to_time(last_remote_stream_created_cycle),
532 GPR_CLOCK_REALTIME);
533 data["lastRemoteStreamCreatedTimestamp"] =
534 Json::FromString(gpr_format_timespec(ts));
535 }
536 }
537 int64_t streams_succeeded =
538 streams_succeeded_.load(std::memory_order_relaxed);
539 if (streams_succeeded != 0) {
540 data["streamsSucceeded"] =
541 Json::FromString(absl::StrCat(streams_succeeded));
542 }
543 int64_t streams_failed = streams_failed_.load(std::memory_order_relaxed);
544 if (streams_failed != 0) {
545 data["streamsFailed"] = Json::FromString(absl::StrCat(streams_failed));
546 }
547 int64_t messages_sent = messages_sent_.load(std::memory_order_relaxed);
548 if (messages_sent != 0) {
549 data["messagesSent"] = Json::FromString(absl::StrCat(messages_sent));
550 ts = gpr_convert_clock_type(
551 gpr_cycle_counter_to_time(
552 last_message_sent_cycle_.load(std::memory_order_relaxed)),
553 GPR_CLOCK_REALTIME);
554 data["lastMessageSentTimestamp"] =
555 Json::FromString(gpr_format_timespec(ts));
556 }
557 int64_t messages_received =
558 messages_received_.load(std::memory_order_relaxed);
559 if (messages_received != 0) {
560 data["messagesReceived"] =
561 Json::FromString(absl::StrCat(messages_received));
562 ts = gpr_convert_clock_type(
563 gpr_cycle_counter_to_time(
564 last_message_received_cycle_.load(std::memory_order_relaxed)),
565 GPR_CLOCK_REALTIME);
566 data["lastMessageReceivedTimestamp"] =
567 Json::FromString(gpr_format_timespec(ts));
568 }
569 int64_t keepalives_sent = keepalives_sent_.load(std::memory_order_relaxed);
570 if (keepalives_sent != 0) {
571 data["keepAlivesSent"] = Json::FromString(absl::StrCat(keepalives_sent));
572 }
573 // Create and fill the parent object.
574 Json::Object object = {
575 {"ref", Json::FromObject({
576 {"socketId", Json::FromString(absl::StrCat(uuid()))},
577 {"name", Json::FromString(name())},
578 })},
579 {"data", Json::FromObject(std::move(data))},
580 };
581 if (security_ != nullptr &&
582 security_->type != SocketNode::Security::ModelType::kUnset) {
583 object["security"] = security_->RenderJson();
584 }
585 PopulateSocketAddressJson(&object, "remote", remote_.c_str());
586 PopulateSocketAddressJson(&object, "local", local_.c_str());
587 return Json::FromObject(std::move(object));
588 }
589
590 //
591 // ListenSocketNode
592 //
593
ListenSocketNode(std::string local_addr,std::string name)594 ListenSocketNode::ListenSocketNode(std::string local_addr, std::string name)
595 : BaseNode(EntityType::kSocket, std::move(name)),
596 local_addr_(std::move(local_addr)) {}
597
RenderJson()598 Json ListenSocketNode::RenderJson() {
599 Json::Object object = {
600 {"ref", Json::FromObject({
601 {"socketId", Json::FromString(absl::StrCat(uuid()))},
602 {"name", Json::FromString(name())},
603 })},
604 };
605 PopulateSocketAddressJson(&object, "local", local_addr_.c_str());
606 return Json::FromObject(std::move(object));
607 }
608
609 } // namespace channelz
610 } // namespace grpc_core
611