44#include < ydb/core/base/appdata_fwd.h>
55#include < ydb/core/base/feature_flags.h>
66#include < ydb/core/base/feature_flags_service.h>
7+ #include < ydb/core/mon/mon.h>
8+ #include < ydb/core/protos/feature_flags.pb.h>
79#include < ydb/library/actors/core/actor_bootstrapped.h>
10+ #include < ydb/library/actors/core/mon.h>
11+ #include < google/protobuf/descriptor.pb.h>
812
913namespace NKikimr ::NConsole {
1014
@@ -25,6 +29,13 @@ namespace NKikimr::NConsole {
2529 ui32 item = (ui32)NKikimrConsole::TConfigItem::FeatureFlagsItem;
2630 Send (MakeConfigsDispatcherID (SelfId ().NodeId ()),
2731 new TEvConfigsDispatcher::TEvSetConfigSubscriptionRequest (item));
32+
33+ if (auto * mon = AppData ()->Mon ) {
34+ auto * actorsMonPage = mon->RegisterIndexPage (" actors" , " Actors" );
35+ mon->RegisterActorPage (
36+ actorsMonPage, " feature_flags" , " Feature Flags" ,
37+ false , TActivationContext::ActorSystem (), SelfId ());
38+ }
2839 }
2940
3041 void Handle (TEvConsole::TEvConfigNotificationRequest::TPtr& ev) {
@@ -42,12 +53,14 @@ namespace NKikimr::NConsole {
4253 for (auto & pr : Subscribers) {
4354 // Since feature flags are relaxed this event also establishes
4455 // release-acquire ordering on repeat accesses.
45- Send (pr.first , new TEvFeatureFlags::TEvChanged, 0 , pr.second );
56+ Send (pr.first , new TEvFeatureFlags::TEvChanged, 0 , pr.second . Cookie );
4657 }
4758 }
4859
4960 void Handle (TEvFeatureFlags::TEvSubscribe::TPtr& ev) {
50- Subscribers[ev->Sender ] = ev->Cookie ;
61+ auto & subscriber = Subscribers[ev->Sender ];
62+ subscriber.Cookie = ev->Cookie ;
63+ subscriber.Description = std::move (ev->Get ()->Description );
5164
5265 // Since feature flags are relaxed client may have observed outdated
5366 // values just before the subscription, which may have actually
@@ -62,19 +75,127 @@ namespace NKikimr::NConsole {
6275 Subscribers.erase (ev->Sender );
6376 }
6477
78+ void Handle (NMon::TEvHttpInfo::TPtr& ev) {
79+ auto cgi = ev->Get ()->Request .GetParams ();
80+ bool showAll = false ;
81+ if (const auto & text = cgi.Get (" all" )) {
82+ int value;
83+ if (TryFromString (text, value)) {
84+ showAll = value;
85+ }
86+ }
87+
88+ NKikimrConfig::TFeatureFlags flags = AppData ()->FeatureFlags ;
89+ const auto * d = NKikimrConfig::TFeatureFlags::descriptor ();
90+ const auto * r = flags.GetReflection ();
91+
92+ TStringStream html;
93+ HTML (html) {
94+ TAG (TH4) {
95+ if (showAll) {
96+ html << " <a href=\" feature_flags?all=0\" >" ;
97+ }
98+ html << " Modified" ;
99+ if (showAll) {
100+ html << " </a>" ;
101+ }
102+ html << " | " ;
103+ if (!showAll) {
104+ html << " <a href=\" feature_flags?all=1\" >" ;
105+ }
106+ html << " All" ;
107+ if (!showAll) {
108+ html << " </a>" ;
109+ }
110+ }
111+
112+ TABLE_SORTABLE_CLASS (" table" ) {
113+ TABLEHEAD () {
114+ TABLER () {
115+ TABLEH () { html << " Flag" ; }
116+ TABLEH () { html << " Value" ; }
117+ }
118+ }
119+ TABLEBODY () {
120+ for (int fieldIndex = 0 ; fieldIndex < d->field_count (); ++fieldIndex) {
121+ const auto * protoField = d->field (fieldIndex);
122+ if (protoField->type () != google::protobuf::FieldDescriptor::TYPE_BOOL) {
123+ continue ;
124+ }
125+ bool hasValue = r->HasField (flags, protoField);
126+ if (!hasValue && !showAll) {
127+ continue ;
128+ }
129+ bool value = r->GetBool (flags, protoField);
130+ TABLER () {
131+ TABLED () {
132+ if (hasValue) {
133+ html << " <b>" ;
134+ }
135+ html << protoField->name ();
136+ if (hasValue) {
137+ html << " </b>" ;
138+ }
139+ }
140+ TABLED () {
141+ if (hasValue) {
142+ html << " <b>" ;
143+ }
144+ html << (value ? " true" : " false" );
145+ if (hasValue) {
146+ html << " </b>" ;
147+ }
148+ }
149+ }
150+ }
151+ }
152+ }
153+
154+ TAG (TH4) {
155+ html << " Subscribers" ;
156+ }
157+
158+ TABLE_SORTABLE_CLASS (" table" ) {
159+ TABLEHEAD () {
160+ TABLER () {
161+ TABLEH () { html << " ActorId" ; }
162+ TABLEH () { html << " Description" ; }
163+ }
164+ }
165+ TABLEBODY () {
166+ for (const auto & pr : Subscribers) {
167+ TABLER () {
168+ TABLED () { html << pr.first ; }
169+ TABLED () { html << pr.second .Description ; }
170+ }
171+ }
172+ }
173+ }
174+ }
175+
176+ Send (ev->Sender , new NMon::TEvHttpInfoRes (html.Str ()));
177+ }
178+
65179 private:
66180 STFUNC (StateWork) {
67181 switch (ev->GetTypeRewrite ()) {
68182 hFunc (TEvConsole::TEvConfigNotificationRequest, Handle);
69183 IgnoreFunc (TEvConfigsDispatcher::TEvSetConfigSubscriptionResponse);
70184 hFunc (TEvFeatureFlags::TEvSubscribe, Handle);
71185 hFunc (TEvFeatureFlags::TEvUnsubscribe, Handle);
186+ hFunc (NMon::TEvHttpInfo, Handle);
72187 }
73188 }
74189
190+ private:
191+ struct TSubscriber {
192+ ui64 Cookie;
193+ TString Description;
194+ };
195+
75196 private:
76197 // ActorId -> Cookie
77- THashMap<TActorId, ui64 > Subscribers;
198+ THashMap<TActorId, TSubscriber > Subscribers;
78199 // Tracks when we updated feature flags at least once
79200 bool Updated = false ;
80201 };
0 commit comments