1+ < html >
2+ < head >
3+ < script >
4+ function cssClean ( str )
5+ {
6+ return str . replaceAll ( / [ ^ A - Z a - z 0 - 9 ] / g, "_" )
7+ }
8+ function firstPubkey ( str )
9+ {
10+ if ( ! str )
11+ return null ;
12+
13+ var res = str . match ( / 0 [ 2 3 ] [ 0 - 9 a - f A - F ] { 64 } / )
14+
15+ return res ? res . at ( 0 ) : null ;
16+ }
17+ function parseDaemon ( daemon )
18+ {
19+ var res = { } ;
20+
21+ var items = daemon . split ( '-' ) ;
22+
23+ if ( daemon == "plugin-manager" )
24+ items = [ "plugin manager" ] ;
25+
26+ res . str = daemon ;
27+ res . name = items . pop ( ) ;
28+ res . peer = firstPubkey ( items [ 0 ] ) ;
29+ res . short_peer = null ;
30+
31+ if ( res . peer ) {
32+ items . shift ( ) ;
33+ res . short_peer = res . peer . substring ( 0 , 4 ) ;
34+ }
35+
36+ res . parents = items ;
37+
38+ if ( res . name . startsWith ( "chan#" ) )
39+ if ( res . parents . length )
40+ res . name = res . parents . pop ( ) + " " + res . name ;
41+ else
42+ res . name = "lightningd " + res . name ;
43+
44+ return res ;
45+ }
46+ function parseLogLine ( line )
47+ {
48+ var res = { } ;
49+
50+ function eat_upto ( re )
51+ {
52+ details = line . match ( re ) ;
53+ if ( ! details )
54+ return null ;
55+ value = line . substring ( 0 , details . index ) ;
56+ line = line . substring ( details . index + details [ 0 ] . length ) ;
57+ return value ;
58+ }
59+
60+ res . date = new Date ( eat_upto ( / \s + / ) ) ;
61+ res . type = eat_upto ( / \s + / ) ;
62+ res . daemon = parseDaemon ( eat_upto ( / : / ) ) ;
63+ res . msg = line ;
64+
65+ return res ;
66+ }
67+ function parseUs ( lineInfo )
68+ {
69+ return {
70+ pubkey : firstPubkey ( lineInfo . msg ) ,
71+ alias : ( lineInfo . msg . match ( / , a l i a s ( .* ) \( c o l o r / ) || [ ] ) . at ( 1 ) ,
72+ color : ( lineInfo . msg . match ( / \( c o l o r ( [ # ] [ 0 - 9 a - f A - F ] { 6 } ) \) / ) || [ ] ) . at ( 1 ) ,
73+ lightngind : ( lineInfo . msg . match ( / \) a n d l i g h t n i n g d ( [ 0 - 9 a - f A - F ] + ) / ) || [ ] ) . at ( 1 ) ,
74+ }
75+ }
76+ function toggle_type ( )
77+ {
78+ var sheet = document . getElementById ( 'logStyleSheet' ) . sheet ;
79+ var rule = "" ;
80+ let selector = ".type-" + cssClean ( this . type_str ) ;
81+ if ( this . className == 'type_on' ) {
82+ this . className = 'type_off' ;
83+ rule = "display:none" ;
84+ }
85+ else {
86+ this . className = 'type_on' ;
87+ }
88+
89+ for ( let i = 0 ; i < sheet . cssRules . length ; i ++ ) {
90+ if ( sheet . cssRules [ i ] . selectorText != selector )
91+ continue ;
92+ sheet . deleteRule ( i ) ;
93+ sheet . insertRule ( selector + " {" + rule + "}" , i ) ;
94+ break ;
95+ }
96+ }
97+ function togggle_all_type ( )
98+ {
99+ for ( b of this . buttons )
100+ if ( b . className == this . className )
101+ b . click ( ) ;
102+
103+ if ( this . className == 'type_on' )
104+ this . className = 'type_off' ;
105+ else
106+ this . className = 'type_on' ;
107+ }
108+ function toggle_daemon ( )
109+ {
110+ var sheet = document . getElementById ( 'logStyleSheet' ) . sheet ;
111+ var rule = "" ;
112+ let selector = ".daemon-" + cssClean ( this . daemon . name ) ;
113+ if ( this . className == 'daemon_on' ) {
114+ this . className = 'daemon_off' ;
115+ rule = "display:none" ;
116+ }
117+ else {
118+ this . className = 'daemon_on' ;
119+ }
120+
121+ for ( let i = 0 ; i < sheet . cssRules . length ; i ++ ) {
122+ if ( sheet . cssRules [ i ] . selectorText != selector )
123+ continue ;
124+ sheet . deleteRule ( i ) ;
125+ sheet . insertRule ( selector + " {" + rule + "}" , i ) ;
126+ break ;
127+ }
128+ }
129+ function togggle_all_daemon ( )
130+ {
131+ for ( b of this . buttons )
132+ if ( b . className == this . className )
133+ b . click ( ) ;
134+
135+ if ( this . className == 'daemon_on' )
136+ this . className = 'daemon_off' ;
137+ else
138+ this . className = 'daemon_on' ;
139+ }
140+ function filter_messages ( )
141+ {
142+ lines = document . getElementsByClassName ( "logLine" ) ;
143+
144+ try {
145+ for ( line of lines ) {
146+ if ( line . info . msg . match ( this . value ) )
147+ line . style . display = "" ;
148+ else
149+ line . style . display = "none" ;
150+ }
151+ document . getElementById ( 'filter_error' ) . innerText = "" ;
152+ }
153+ catch ( error ) {
154+ document . getElementById ( 'filter_error' ) . innerText = error . message ;
155+ }
156+ }
157+ function do_render ( logs , area )
158+ {
159+ var d = document ;
160+ var sheet = d . getElementById ( 'logStyleSheet' ) . sheet ;
161+ var types = { } ;
162+ var daemons = { } ;
163+ var us = null ;
164+
165+ while ( area . firstChild )
166+ area . removeChild ( area . firstChild ) ;
167+
168+ while ( sheet . cssRules . length )
169+ sheet . deleteRule ( 0 ) ;
170+
171+ for ( line of logs . split ( "\n" ) ) {
172+ line = line . trim ( )
173+ if ( ! line . length )
174+ continue ;
175+
176+ info = parseLogLine ( line ) ;
177+
178+ if ( info . msg . startsWith ( 'Server started with public key' ) )
179+ us = parseUs ( info ) ;
180+
181+ p = d . createElement ( 'p' ) ;
182+ p . info = info ;
183+ p . className = "logLine type-" + cssClean ( info . type ) + " daemon-" + cssClean ( info . daemon . name ) ;
184+
185+ if ( info . daemon . peer )
186+ p . className += " peer-" + info . daemon . peer ;
187+
188+ types [ info . type ] = ( types [ info . type ] || 0 ) + 1
189+ daemons [ info . daemon . str ] = ( daemons [ info . daemon . str ] || 0 ) + 1
190+
191+ var s = d . createElement ( 'span' ) ;
192+ s . className = "daemon"
193+ s . title = info . daemon . parents ;
194+ s . innerText = info . daemon . name ;
195+ if ( info . daemon . short_peer )
196+ s . innerText += " " + info . daemon . short_peer ;
197+ s . innerText += " " ;
198+ p . appendChild ( s ) ;
199+
200+ var s = d . createElement ( 'span' ) ;
201+ s . className = "logMsg" ;
202+ s . title = info . date ;
203+ s . innerText = info . msg ;
204+ p . appendChild ( s ) ;
205+
206+ area . appendChild ( p ) ;
207+ }
208+
209+ types = Object . fromEntries (
210+ Object . entries ( types ) . sort ( ( [ , a ] , [ , b ] ) => b - a )
211+ ) ;
212+
213+ daemons = Object . fromEntries (
214+ Object . entries ( daemons ) . sort ( ( [ , a ] , [ , b ] ) => b - a )
215+ ) ;
216+
217+ var controls = d . createElement ( 'p' ) ;
218+ controls . className = "controls" ;
219+ var type_buttons = [ ] ;
220+ var daemon_buttons = [ ] ;
221+
222+ for ( type in types ) {
223+ var b = d . createElement ( 'button' ) ;
224+ b . className = 'type_on' ;
225+ b . type_str = type ;
226+ b . innerText = type + " " + types [ type ] ;
227+ b . onclick = toggle_type ;
228+ type_buttons . push ( b ) ;
229+ controls . appendChild ( b ) ;
230+
231+ sheet . insertRule ( ".type-" + cssClean ( type ) + "{}" , 0 ) ;
232+ }
233+
234+ var b = d . createElement ( 'button' ) ;
235+ b . className = 'type_on' ;
236+ b . innerText = "All"
237+ b . onclick = togggle_all_type ;
238+ b . buttons = type_buttons ;
239+ controls . appendChild ( b ) ;
240+
241+ controls . appendChild ( d . createElement ( 'hr' ) ) ;
242+
243+ for ( daemon_str in daemons ) {
244+ var daemon = parseDaemon ( daemon_str )
245+ var b = d . createElement ( 'button' ) ;
246+ b . className = 'daemon_on' ;
247+ b . daemon = daemon ;
248+ b . innerText = ( daemon . short_peer || "" ) + " " + daemon . name + " " + daemons [ daemon_str ] ;
249+ b . onclick = toggle_daemon ;
250+ daemon_buttons . push ( b ) ;
251+ controls . appendChild ( b ) ;
252+
253+ sheet . insertRule ( ".daemon-" + cssClean ( daemon . name ) + "{}" , 0 ) ;
254+ }
255+
256+ var b = d . createElement ( 'button' ) ;
257+ b . className = 'daemon_on' ;
258+ b . innerText = "All"
259+ b . onclick = togggle_all_daemon ;
260+ b . buttons = daemon_buttons ;
261+ controls . appendChild ( b ) ;
262+
263+ controls . appendChild ( d . createElement ( 'hr' ) ) ;
264+
265+
266+ var t = d . createElement ( 'input' ) ;
267+ t . type = 'text' ;
268+ t . placeholder = 'message filter regex' ;
269+ t . onchange = filter_messages ;
270+ t . onkeyup = filter_messages ;
271+
272+ controls . appendChild ( t ) ;
273+ controls . appendChild ( document . createTextNode ( " " ) ) ;
274+
275+ var s = d . createElement ( 'span' ) ;
276+ s . id = 'filter_error' ;
277+ controls . appendChild ( s ) ;
278+
279+ area . prepend ( controls ) ;
280+ }
281+ </ script >
282+ < style id ="logStyleSheet ">
283+ </ style >
284+ < style >
285+ .logLine {
286+ font-family : Cascadia, Hack, monospace;
287+ }
288+ .type_on : after , .daemon_on : after {
289+ content : " (on)" ;
290+ }
291+ .type_off : after , .daemon_off : after {
292+ content : " (off)" ;
293+ }
294+ .type_on , .daemon_on {
295+ }
296+ .type_off , .daemon_off {
297+ background-color : # FF6961 ;
298+ }
299+ .controls {
300+ position : sticky;
301+ top : 0 ;
302+ background-color : # aaa9 ;
303+ padding : 1em ;
304+ }
305+ .logLine .daemon {
306+ color : # AEC6CF ;
307+ }
308+ .type-IO : before {
309+ content : "io " ;
310+ }
311+ .type-TRACE : before {
312+ content : "trace " ;
313+ }
314+ .type-DEBUG : before {
315+ content : "debug " ;
316+ color : # Ffd1dc ;
317+ }
318+ .type-INFO : before {
319+ content : "info " ;
320+ color : # AEC6CF ;
321+ }
322+ .type-UNUSUAL : before {
323+ content : "unusual " ;
324+ color : # E9D502 ;
325+ }
326+ .type-BROKEN : before {
327+ content : "broken " ;
328+ color : # FF6961 ;
329+ }
330+ </ style >
331+ </ head >
332+ < body >
333+ < div id ="area ">
334+ < h3 > Enter Logs:</ h3 >
335+ < textarea id ="logs "> </ textarea >
336+ < button onclick ="do_render(document.getElementById('logs').value, document.getElementById('area')) "> Render</ button >
337+ </ div >
338+ </ body >
339+ </ html >
0 commit comments