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