web-log-viewer 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -14
- package/build/public/build/bundle.css +1 -1
- package/build/public/build/bundle.js +1 -1
- package/build/public/build/bundle.js.map +1 -1
- package/build/public/index.html +1 -0
- package/build/server.js +68 -29
- package/package.json +15 -3
- package/.prettierrc +0 -9
- package/app-console.js +0 -28
- package/nginx.log +0 -51462
- package/produce-log.sh +0 -3
- package/src/config.ts +0 -28
- package/src/defaultMessageParser.ts +0 -16
- package/src/log-index.ts +0 -44
- package/src/server.ts +0 -135
- package/src/stream-utils.ts +0 -28
- package/src/types.ts +0 -51
- package/svelte/.vscode/extensions.json +0 -3
- package/svelte/package-lock.json +0 -998
- package/svelte/package.json +0 -33
- package/svelte/public/favicon.png +0 -0
- package/svelte/public/global.css +0 -104
- package/svelte/public/index.html +0 -18
- package/svelte/rollup.config.js +0 -88
- package/svelte/src/App.svelte +0 -285
- package/svelte/src/LogMessageDetails.svelte +0 -166
- package/svelte/src/log-formatter.ts +0 -21
- package/svelte/src/log-store.ts +0 -209
- package/svelte/src/main.ts +0 -8
- package/svelte/src/types.ts +0 -51
- package/svelte/tsconfig.json +0 -6
- package/tsconfig.json +0 -71
package/svelte/package.json
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "svelte-app",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"scripts": {
|
|
5
|
-
"build": "rollup -c",
|
|
6
|
-
"dev": "rollup -c -w",
|
|
7
|
-
"start": "sirv public",
|
|
8
|
-
"validate": "svelte-check"
|
|
9
|
-
},
|
|
10
|
-
"devDependencies": {
|
|
11
|
-
"@rollup/plugin-commonjs": "^17.0.0",
|
|
12
|
-
"@rollup/plugin-node-resolve": "^11.0.0",
|
|
13
|
-
"@rollup/plugin-replace": "^2.4.2",
|
|
14
|
-
"@rollup/plugin-typescript": "^6.0.0",
|
|
15
|
-
"@tsconfig/svelte": "^1.0.0",
|
|
16
|
-
"@types/lodash": "^4.14.168",
|
|
17
|
-
"rollup": "^2.3.4",
|
|
18
|
-
"rollup-plugin-css-only": "^3.1.0",
|
|
19
|
-
"rollup-plugin-livereload": "^2.0.0",
|
|
20
|
-
"rollup-plugin-svelte": "^7.0.0",
|
|
21
|
-
"rollup-plugin-terser": "^7.0.0",
|
|
22
|
-
"svelte": "^3.0.0",
|
|
23
|
-
"svelte-check": "^1.0.0",
|
|
24
|
-
"svelte-preprocess": "^4.0.0",
|
|
25
|
-
"tslib": "^2.0.0",
|
|
26
|
-
"typescript": "^3.9.3"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"json5": "^2.2.0",
|
|
30
|
-
"lodash": "^4.17.21",
|
|
31
|
-
"sirv-cli": "^1.0.0"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
Binary file
|
package/svelte/public/global.css
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
html, body {
|
|
2
|
-
position: relative;
|
|
3
|
-
width: 100%;
|
|
4
|
-
height: 100%;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
body {
|
|
8
|
-
color: #333;
|
|
9
|
-
margin: 0;
|
|
10
|
-
padding: 8px;
|
|
11
|
-
box-sizing: border-box;
|
|
12
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
*, *:before, *:after {
|
|
16
|
-
box-sizing: inherit;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
a {
|
|
20
|
-
color: rgb(0,100,200);
|
|
21
|
-
text-decoration: none;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
a:hover {
|
|
25
|
-
text-decoration: underline;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
a:visited {
|
|
29
|
-
color: rgb(0,80,160);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
label {
|
|
33
|
-
display: block;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
input, button, select, textarea {
|
|
37
|
-
font-family: inherit;
|
|
38
|
-
font-size: inherit;
|
|
39
|
-
-webkit-padding: 0.4em 0;
|
|
40
|
-
padding: 0.4em;
|
|
41
|
-
margin: 0 0 0.5em 0;
|
|
42
|
-
box-sizing: border-box;
|
|
43
|
-
border: 1px solid #ccc;
|
|
44
|
-
border-radius: 2px;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
input:disabled {
|
|
48
|
-
color: #ccc;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
button {
|
|
52
|
-
color: #333;
|
|
53
|
-
background-color: #f4f4f4;
|
|
54
|
-
outline: none;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
button:disabled {
|
|
58
|
-
color: #999;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
button:not(:disabled):active {
|
|
62
|
-
background-color: #ddd;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
button:focus {
|
|
66
|
-
border-color: #666;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
:root {
|
|
72
|
-
/* --primary-color: #de3b3c; */
|
|
73
|
-
--primary-color: #6FA157;
|
|
74
|
-
--gray-800: #3A435A;
|
|
75
|
-
--gray-600: #61697B;
|
|
76
|
-
--gray-400: #9CA1AC;
|
|
77
|
-
--gray-200: #E1E3E6;
|
|
78
|
-
--gray-100: #F5F6F7;
|
|
79
|
-
}
|
|
80
|
-
.button {
|
|
81
|
-
border-color: var(--primary-color);
|
|
82
|
-
color: var(--primary-color);
|
|
83
|
-
background-color: white;
|
|
84
|
-
border-radius: 3px;
|
|
85
|
-
text-transform: uppercase;
|
|
86
|
-
letter-spacing: 1px;
|
|
87
|
-
font-size: 14px;
|
|
88
|
-
font-weight: bold;
|
|
89
|
-
padding: 5px 13px;
|
|
90
|
-
margin: 0px;
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
}
|
|
93
|
-
.button--primary {
|
|
94
|
-
color: white;
|
|
95
|
-
background-color: var(--primary-color);
|
|
96
|
-
}
|
|
97
|
-
.button--small {
|
|
98
|
-
font-size: 10px;
|
|
99
|
-
padding: 3px 8px;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
h1, h2, h3, h4 {
|
|
103
|
-
color: var(--primary-color);
|
|
104
|
-
}
|
package/svelte/public/index.html
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset='utf-8'>
|
|
5
|
-
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
|
6
|
-
|
|
7
|
-
<title>JSON log viewer app</title>
|
|
8
|
-
|
|
9
|
-
<link rel='icon' type='image/png' href='/favicon.png'>
|
|
10
|
-
<link rel='stylesheet' href='/global.css'>
|
|
11
|
-
<link rel='stylesheet' href='/build/bundle.css'>
|
|
12
|
-
|
|
13
|
-
<script defer src='/build/bundle.js'></script>
|
|
14
|
-
</head>
|
|
15
|
-
|
|
16
|
-
<body>
|
|
17
|
-
</body>
|
|
18
|
-
</html>
|
package/svelte/rollup.config.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import svelte from 'rollup-plugin-svelte';
|
|
2
|
-
import commonjs from '@rollup/plugin-commonjs';
|
|
3
|
-
import resolve from '@rollup/plugin-node-resolve';
|
|
4
|
-
import livereload from 'rollup-plugin-livereload';
|
|
5
|
-
import { terser } from 'rollup-plugin-terser';
|
|
6
|
-
import sveltePreprocess from 'svelte-preprocess';
|
|
7
|
-
import typescript from '@rollup/plugin-typescript';
|
|
8
|
-
import replace from '@rollup/plugin-replace';
|
|
9
|
-
import css from 'rollup-plugin-css-only';
|
|
10
|
-
|
|
11
|
-
const production = !process.env.ROLLUP_WATCH;
|
|
12
|
-
|
|
13
|
-
function serve() {
|
|
14
|
-
let server;
|
|
15
|
-
|
|
16
|
-
function toExit() {
|
|
17
|
-
if (server) server.kill(0);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
writeBundle() {
|
|
22
|
-
if (server) return;
|
|
23
|
-
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
|
24
|
-
stdio: ['ignore', 'inherit', 'inherit'],
|
|
25
|
-
shell: true
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
process.on('SIGTERM', toExit);
|
|
29
|
-
process.on('exit', toExit);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export default {
|
|
35
|
-
input: 'src/main.ts',
|
|
36
|
-
output: {
|
|
37
|
-
sourcemap: true,
|
|
38
|
-
format: 'iife',
|
|
39
|
-
name: 'app',
|
|
40
|
-
file: 'public/build/bundle.js'
|
|
41
|
-
},
|
|
42
|
-
plugins: [
|
|
43
|
-
replace({
|
|
44
|
-
preventAssignment: true,
|
|
45
|
-
'JSL_ENVIRONMENT': JSON.stringify(process.env.NODE_ENV),
|
|
46
|
-
}),
|
|
47
|
-
svelte({
|
|
48
|
-
preprocess: sveltePreprocess({ sourceMap: !production }),
|
|
49
|
-
compilerOptions: {
|
|
50
|
-
// enable run-time checks when not in production
|
|
51
|
-
dev: !production
|
|
52
|
-
}
|
|
53
|
-
}),
|
|
54
|
-
// we'll extract any component CSS out into
|
|
55
|
-
// a separate file - better for performance
|
|
56
|
-
css({ output: 'bundle.css' }),
|
|
57
|
-
|
|
58
|
-
// If you have external dependencies installed from
|
|
59
|
-
// npm, you'll most likely need these plugins. In
|
|
60
|
-
// some cases you'll need additional configuration -
|
|
61
|
-
// consult the documentation for details:
|
|
62
|
-
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
|
63
|
-
resolve({
|
|
64
|
-
browser: true,
|
|
65
|
-
dedupe: ['svelte']
|
|
66
|
-
}),
|
|
67
|
-
commonjs(),
|
|
68
|
-
typescript({
|
|
69
|
-
sourceMap: !production,
|
|
70
|
-
inlineSources: !production
|
|
71
|
-
}),
|
|
72
|
-
|
|
73
|
-
// In dev mode, call `npm run start` once
|
|
74
|
-
// the bundle has been generated
|
|
75
|
-
!production && serve(),
|
|
76
|
-
|
|
77
|
-
// Watch the `public` directory and refresh the
|
|
78
|
-
// browser on changes when not in production
|
|
79
|
-
!production && livereload('public'),
|
|
80
|
-
|
|
81
|
-
// If we're building for production (npm run build
|
|
82
|
-
// instead of npm run dev), minify
|
|
83
|
-
production && terser()
|
|
84
|
-
],
|
|
85
|
-
watch: {
|
|
86
|
-
clearScreen: false
|
|
87
|
-
}
|
|
88
|
-
};
|
package/svelte/src/App.svelte
DELETED
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { logStore } from './log-store'
|
|
3
|
-
import LogMessageDetails from './LogMessageDetails.svelte'
|
|
4
|
-
import type { FormattedMessage, LogMessage } from './types'
|
|
5
|
-
import debounce from 'lodash/debounce'
|
|
6
|
-
|
|
7
|
-
// height of each row, in pixels
|
|
8
|
-
const ROW_HEIGHT = 30
|
|
9
|
-
|
|
10
|
-
let logMessageBeingViewed: FormattedMessage
|
|
11
|
-
|
|
12
|
-
// scroll to bottom when new logs come in while on tail mode
|
|
13
|
-
logStore.subscribe(logs => {
|
|
14
|
-
if (logs.mode == 'tail') {
|
|
15
|
-
setTimeout(() => scrollToBottom(), 0)
|
|
16
|
-
}
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
function scrollToBottom() {
|
|
20
|
-
const element = document.getElementById('windowLogs')
|
|
21
|
-
if (element) {
|
|
22
|
-
element.scrollTop = element.scrollHeight
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// TODO can be memoized
|
|
27
|
-
function calcBeforeWindowRowHeigh(logWindow: FormattedMessage[]) {
|
|
28
|
-
if (!logWindow || !logWindow.length) {
|
|
29
|
-
return 0
|
|
30
|
-
} else {
|
|
31
|
-
return (logWindow[0].seq - 1) * ROW_HEIGHT
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// TODO can be memoized
|
|
36
|
-
function calcAfterWindowRowHeight(logWindow: FormattedMessage[], logCount: number) {
|
|
37
|
-
if (!logWindow || !logWindow.length) {
|
|
38
|
-
return 0
|
|
39
|
-
} else {
|
|
40
|
-
const lastSeqInWindow = logWindow[logWindow.length - 1].seq
|
|
41
|
-
return (logCount - lastSeqInWindow) * ROW_HEIGHT
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const isScrolledToBottom = (el: Element) => el.scrollHeight - el.scrollTop - el.clientHeight < 1
|
|
46
|
-
|
|
47
|
-
const calcSeqByOffset = (offset: number, logCount: number) => Math.floor(offset / ROW_HEIGHT)
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Calculate the seq the user has offset to, and requet data from the server around that offset.
|
|
51
|
-
*/
|
|
52
|
-
const onScroll = debounce(
|
|
53
|
-
(ev: Event) => {
|
|
54
|
-
const el = ev.target as Element
|
|
55
|
-
if (isScrolledToBottom(el)) {
|
|
56
|
-
if ($logStore.mode == 'static') {
|
|
57
|
-
logStore.changeToTail()
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
const seq = calcSeqByOffset(el.scrollTop, $logStore.count) + 1
|
|
61
|
-
logStore.changeToStatic(seq)
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
100,
|
|
65
|
-
{ leading: false, trailing: true },
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Called when the user enters a new filter query. This will eventually
|
|
70
|
-
* fetch filtered data from the server
|
|
71
|
-
*/
|
|
72
|
-
const onChangeFilter = debounce(
|
|
73
|
-
(ev: Event) => {
|
|
74
|
-
const input = ev.target as HTMLInputElement
|
|
75
|
-
logStore.changeFilter(input.value)
|
|
76
|
-
},
|
|
77
|
-
100,
|
|
78
|
-
{ leading: false, trailing: true },
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
function viewRelativeLog(delta: 1 | -1) {
|
|
82
|
-
const currentIndex = $logStore.window.findIndex(l => l === logMessageBeingViewed)
|
|
83
|
-
if (currentIndex == -1) {
|
|
84
|
-
logMessageBeingViewed = undefined
|
|
85
|
-
} else {
|
|
86
|
-
logMessageBeingViewed = $logStore.window[currentIndex + delta]
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
</script>
|
|
90
|
-
|
|
91
|
-
<main>
|
|
92
|
-
<!-- show a modal with details on the currently selected message -->
|
|
93
|
-
{#if logMessageBeingViewed}
|
|
94
|
-
<LogMessageDetails
|
|
95
|
-
logMessage={logMessageBeingViewed}
|
|
96
|
-
logSize={$logStore.count}
|
|
97
|
-
formatter={$logStore.formatter}
|
|
98
|
-
on:viewPrevious={() => viewRelativeLog(-1)}
|
|
99
|
-
on:viewNext={() => viewRelativeLog(1)}
|
|
100
|
-
on:closeAndUpdateFormatter={e => {
|
|
101
|
-
if (e.detail?.newFormatter) {
|
|
102
|
-
// check if formatter has actually changed
|
|
103
|
-
if ($logStore.formatter != e.detail.newFormatter) {
|
|
104
|
-
logStore.changeFormatter(e.detail.newFormatter)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
logMessageBeingViewed = undefined
|
|
108
|
-
}}
|
|
109
|
-
/>
|
|
110
|
-
{/if}
|
|
111
|
-
|
|
112
|
-
<h1>
|
|
113
|
-
Web log viewer
|
|
114
|
-
<small><strong>{$logStore.count}</strong> messages</small>
|
|
115
|
-
{#if $logStore.mode == 'tail'}
|
|
116
|
-
<small>in <strong>follow</strong> mode</small>
|
|
117
|
-
{/if}
|
|
118
|
-
{#if $logStore.mode == 'static'}
|
|
119
|
-
<small>frozen at offset <strong>{$logStore.offsetSeq}</strong></small>
|
|
120
|
-
{/if}
|
|
121
|
-
</h1>
|
|
122
|
-
|
|
123
|
-
<section class="logFilters">
|
|
124
|
-
<input
|
|
125
|
-
class="windowLogs-filterInput"
|
|
126
|
-
type="text"
|
|
127
|
-
placeholder="enter text to filter messages"
|
|
128
|
-
on:keyup={onChangeFilter}
|
|
129
|
-
/>
|
|
130
|
-
</section>
|
|
131
|
-
|
|
132
|
-
<section id="windowLogs" class="windowLogs" on:scroll={onScroll}>
|
|
133
|
-
{#if $logStore.window.length == 0}
|
|
134
|
-
<div class="message message--info">No messages found</div>
|
|
135
|
-
{:else}
|
|
136
|
-
<table class="windowLogs-table">
|
|
137
|
-
<thead>
|
|
138
|
-
<tr style="height: {ROW_HEIGHT}px">
|
|
139
|
-
<th />
|
|
140
|
-
{#each $logStore.columns as col}
|
|
141
|
-
<th>{col}</th>
|
|
142
|
-
{/each}
|
|
143
|
-
</tr>
|
|
144
|
-
</thead>
|
|
145
|
-
<tbody>
|
|
146
|
-
<tr
|
|
147
|
-
class="windowLogs-beforeWindowRow"
|
|
148
|
-
style="height: {calcBeforeWindowRowHeigh($logStore.window)}px"
|
|
149
|
-
/>
|
|
150
|
-
{#each $logStore.window as msg (msg.seq)}
|
|
151
|
-
<tr style="height: {ROW_HEIGHT}px">
|
|
152
|
-
<td class="windowLogs-viewLogMessageButton"
|
|
153
|
-
><button
|
|
154
|
-
class="button button--small"
|
|
155
|
-
type="button"
|
|
156
|
-
on:click={() => (logMessageBeingViewed = msg)}>View</button
|
|
157
|
-
></td
|
|
158
|
-
>
|
|
159
|
-
{#each $logStore.columns as col (col)}
|
|
160
|
-
<td>{msg.formattedMessage[col] || ''}</td>
|
|
161
|
-
{/each}
|
|
162
|
-
</tr>
|
|
163
|
-
{/each}
|
|
164
|
-
<tr
|
|
165
|
-
class="windowLogs-afterWindowRow"
|
|
166
|
-
style="height: {calcAfterWindowRowHeight($logStore.window, $logStore.count)}px"
|
|
167
|
-
/>
|
|
168
|
-
</tbody>
|
|
169
|
-
</table>
|
|
170
|
-
{/if}
|
|
171
|
-
</section>
|
|
172
|
-
</main>
|
|
173
|
-
|
|
174
|
-
<style>
|
|
175
|
-
main {
|
|
176
|
-
display: flex;
|
|
177
|
-
flex-direction: column;
|
|
178
|
-
height: 100%;
|
|
179
|
-
}
|
|
180
|
-
.windowLogs {
|
|
181
|
-
height: 100%;
|
|
182
|
-
overflow: auto;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.windowLogs-filterInput {
|
|
186
|
-
border-radius: 5px;
|
|
187
|
-
font-size: 20px;
|
|
188
|
-
padding: 20px;
|
|
189
|
-
width: 100%;
|
|
190
|
-
max-width: 900px;
|
|
191
|
-
margin: 0px 0px 25px 0px;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.windowLogs-table {
|
|
195
|
-
table-layout: auto;
|
|
196
|
-
white-space: nowrap;
|
|
197
|
-
text-align: left;
|
|
198
|
-
border-collapse: separate;
|
|
199
|
-
border-spacing: 0;
|
|
200
|
-
position: relative;
|
|
201
|
-
width: 100%;
|
|
202
|
-
font-size: 14px;
|
|
203
|
-
}
|
|
204
|
-
.windowLogs-table td,
|
|
205
|
-
.windowLogs-table th {
|
|
206
|
-
padding: 2px 7px;
|
|
207
|
-
overflow: hidden;
|
|
208
|
-
text-overflow: ellipsis;
|
|
209
|
-
}
|
|
210
|
-
.windowLogs-table th {
|
|
211
|
-
position: sticky;
|
|
212
|
-
top: 0px;
|
|
213
|
-
background-color: var(--gray-100);
|
|
214
|
-
padding: 10px 7px;
|
|
215
|
-
border-top: 1px solid var(--gray-200);
|
|
216
|
-
border-bottom: 1px solid var(--gray-200);
|
|
217
|
-
color: var(--primary-color);
|
|
218
|
-
font-weight: 500;
|
|
219
|
-
letter-spacing: 0.9px;
|
|
220
|
-
font-size: 16px;
|
|
221
|
-
}
|
|
222
|
-
.windowLogs-table th:first-child {
|
|
223
|
-
border-top-left-radius: 5px;
|
|
224
|
-
border-left: 1px solid var(--gray-200);
|
|
225
|
-
}
|
|
226
|
-
.windowLogs-table th:last-child {
|
|
227
|
-
border-top-right-radius: 5px;
|
|
228
|
-
border-right: 1px solid var(--gray-200);
|
|
229
|
-
}
|
|
230
|
-
.windowLogs-table tr:hover td {
|
|
231
|
-
background-color: var(--gray-100);
|
|
232
|
-
}
|
|
233
|
-
.windowLogs-table td {
|
|
234
|
-
border-top: 1px solid var(--gray-200);
|
|
235
|
-
}
|
|
236
|
-
.windowLogs-table td:first-child {
|
|
237
|
-
border-left: 1px solid var(--gray-200);
|
|
238
|
-
}
|
|
239
|
-
.windowLogs-table td:last-child {
|
|
240
|
-
border-right: 1px solid var(--gray-200);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
main {
|
|
244
|
-
padding: 1em 1em;
|
|
245
|
-
margin: 0 auto;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
h1 {
|
|
249
|
-
text-align: left;
|
|
250
|
-
color: var(--primary-color);
|
|
251
|
-
text-transform: uppercase;
|
|
252
|
-
font-size: 30px;
|
|
253
|
-
font-weight: 800;
|
|
254
|
-
letter-spacing: 5px;
|
|
255
|
-
margin-top: 0px;
|
|
256
|
-
}
|
|
257
|
-
h1 small {
|
|
258
|
-
text-transform: lowercase;
|
|
259
|
-
font-size: 20px;
|
|
260
|
-
letter-spacing: normal;
|
|
261
|
-
font-weight: normal;
|
|
262
|
-
color: var(--gray-600);
|
|
263
|
-
}
|
|
264
|
-
h1 small:not(:last-child)::after {
|
|
265
|
-
content: '·';
|
|
266
|
-
margin-left: 10px;
|
|
267
|
-
margin-right: -5px;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.message {
|
|
271
|
-
border: 1px solid;
|
|
272
|
-
border-radius: 5px;
|
|
273
|
-
padding: 20px;
|
|
274
|
-
margin: 20px 0px;
|
|
275
|
-
border-color: var(--gray-600);
|
|
276
|
-
color: var(--gray-600);
|
|
277
|
-
background-color: var(--gray-100);
|
|
278
|
-
}
|
|
279
|
-
.message--info {
|
|
280
|
-
text-align: center;
|
|
281
|
-
border-color: #0c5460;
|
|
282
|
-
color: #0c5460;
|
|
283
|
-
background-color: #0c546022;
|
|
284
|
-
}
|
|
285
|
-
</style>
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { createEventDispatcher } from 'svelte'
|
|
3
|
-
import { formatLogMessage, parseFormatter } from './log-store'
|
|
4
|
-
import json5 from 'json5'
|
|
5
|
-
import type { FormattedMessage } from './types'
|
|
6
|
-
|
|
7
|
-
export let logMessage: FormattedMessage
|
|
8
|
-
export let logSize: number
|
|
9
|
-
export let formatter: string
|
|
10
|
-
|
|
11
|
-
const dispatch = createEventDispatcher()
|
|
12
|
-
const closeDialog = () => dispatch('closeAndUpdateFormatter', { newFormatter: formatter })
|
|
13
|
-
const formatObject = (obj: any) => {
|
|
14
|
-
if (typeof obj == 'string') {
|
|
15
|
-
return obj
|
|
16
|
-
} else {
|
|
17
|
-
return json5.stringify(obj, null, 2)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
$: isFirst = logMessage.seq == 1
|
|
22
|
-
$: isLast = logMessage.seq == logSize
|
|
23
|
-
$: formattedExample = (() => {
|
|
24
|
-
try {
|
|
25
|
-
const msg = {
|
|
26
|
-
seq: logMessage.seq,
|
|
27
|
-
data: logMessage.rawMessage,
|
|
28
|
-
index: [],
|
|
29
|
-
}
|
|
30
|
-
return formatLogMessage(formatter)(msg)
|
|
31
|
-
} catch (err) {
|
|
32
|
-
return { formattedMessage: err.message }
|
|
33
|
-
}
|
|
34
|
-
})()
|
|
35
|
-
</script>
|
|
36
|
-
|
|
37
|
-
<main class="logMessageDetails">
|
|
38
|
-
<div class="logMessageDetails-backdrop" on:click={closeDialog} />
|
|
39
|
-
<div class="logMessageDetails-container">
|
|
40
|
-
<!-- show the log message details, allow navigating to adjacent logs -->
|
|
41
|
-
<div class="logMessageDetails-actions">
|
|
42
|
-
<button class="button button--primary logMessageDetails-closeButton" on:click={closeDialog}
|
|
43
|
-
>Close</button
|
|
44
|
-
>
|
|
45
|
-
<button
|
|
46
|
-
class="button logMessageDetails-prevButton"
|
|
47
|
-
disabled={isFirst}
|
|
48
|
-
on:click={() => dispatch('viewPrevious')}>Prev</button
|
|
49
|
-
>
|
|
50
|
-
<button
|
|
51
|
-
class="button logMessageDetails-nextButton"
|
|
52
|
-
disabled={isLast}
|
|
53
|
-
on:click={() => dispatch('viewNext')}>Next</button
|
|
54
|
-
>
|
|
55
|
-
</div>
|
|
56
|
-
<h3 class="subtitle">Details of log message #{logMessage.seq}</h3>
|
|
57
|
-
<div class="logMessageDetails-value code">
|
|
58
|
-
{formatObject(logMessage.rawMessage)}
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<!-- allow the user to configure the formatter, taking the current log as example -->
|
|
62
|
-
<h3 class="subtitle">Configure the log format</h3>
|
|
63
|
-
<div class="formatterConfig">
|
|
64
|
-
<div class="formatterConfig-value">
|
|
65
|
-
<h4>Formatter</h4>
|
|
66
|
-
<textarea class="code" bind:value={formatter} />
|
|
67
|
-
</div>
|
|
68
|
-
<div class="formatterConfig-preview">
|
|
69
|
-
<h4>Example</h4>
|
|
70
|
-
<div class="code">{formatObject(formattedExample.formattedMessage)}</div>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</main>
|
|
75
|
-
|
|
76
|
-
<style>
|
|
77
|
-
.logMessageDetails {
|
|
78
|
-
position: fixed;
|
|
79
|
-
top: 0;
|
|
80
|
-
left: 0;
|
|
81
|
-
width: 100%;
|
|
82
|
-
height: 100%;
|
|
83
|
-
z-index: 10;
|
|
84
|
-
}
|
|
85
|
-
.logMessageDetails-backdrop {
|
|
86
|
-
position: fixed;
|
|
87
|
-
width: 100%;
|
|
88
|
-
height: 100%;
|
|
89
|
-
background-color: #00000066;
|
|
90
|
-
}
|
|
91
|
-
.logMessageDetails-actions {
|
|
92
|
-
position: relative;
|
|
93
|
-
margin-top: 10px;
|
|
94
|
-
display: flex;
|
|
95
|
-
justify-content: flex-end;
|
|
96
|
-
}
|
|
97
|
-
.logMessageDetails-actions > *:not(:last-child) {
|
|
98
|
-
margin-right: 10px;
|
|
99
|
-
}
|
|
100
|
-
.logMessageDetails-actions .logMessageDetails-closeButton {
|
|
101
|
-
margin-right: auto;
|
|
102
|
-
}
|
|
103
|
-
.logMessageDetails-container {
|
|
104
|
-
background-color: #f5f5f5;
|
|
105
|
-
position: relative;
|
|
106
|
-
max-width: 1200px;
|
|
107
|
-
margin-left: auto;
|
|
108
|
-
margin-right: auto;
|
|
109
|
-
margin-top: 20px;
|
|
110
|
-
margin-bottom: 20px;
|
|
111
|
-
padding: 20px 30px;
|
|
112
|
-
border-radius: 10px;
|
|
113
|
-
box-shadow: 0px 2px 5px 0px #444;
|
|
114
|
-
max-height: calc(100% - 40px);
|
|
115
|
-
overflow: auto;
|
|
116
|
-
}
|
|
117
|
-
.code {
|
|
118
|
-
white-space: pre;
|
|
119
|
-
font-family: monospace;
|
|
120
|
-
color: #555;
|
|
121
|
-
background-color: white;
|
|
122
|
-
line-height: 1.2rem;
|
|
123
|
-
text-align: left;
|
|
124
|
-
overflow: auto;
|
|
125
|
-
border: 1px solid #dedede;
|
|
126
|
-
border-radius: 3px;
|
|
127
|
-
padding: 10px;
|
|
128
|
-
width: 100%;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.formatterConfig {
|
|
132
|
-
display: flex;
|
|
133
|
-
justify-content: space-between;
|
|
134
|
-
align-items: stretch;
|
|
135
|
-
}
|
|
136
|
-
.formatterConfig > * {
|
|
137
|
-
width: 50%;
|
|
138
|
-
}
|
|
139
|
-
.formatterConfig > *:not(:last-child) {
|
|
140
|
-
margin-right: 20px;
|
|
141
|
-
}
|
|
142
|
-
.formatterConfig-value textarea {
|
|
143
|
-
height: 300px;
|
|
144
|
-
margin: 0px;
|
|
145
|
-
}
|
|
146
|
-
.formatterConfig-preview .code {
|
|
147
|
-
height: 300px;
|
|
148
|
-
}
|
|
149
|
-
h3 {
|
|
150
|
-
text-align: left;
|
|
151
|
-
margin-top: 30px;
|
|
152
|
-
margin-bottom: 14px;
|
|
153
|
-
text-transform: uppercase;
|
|
154
|
-
font-size: 17px;
|
|
155
|
-
letter-spacing: 1.3px;
|
|
156
|
-
}
|
|
157
|
-
h4 {
|
|
158
|
-
text-align: left;
|
|
159
|
-
margin-bottom: 10px;
|
|
160
|
-
text-transform: uppercase;
|
|
161
|
-
letter-spacing: 01px;
|
|
162
|
-
color: #666;
|
|
163
|
-
font-size: 12px;
|
|
164
|
-
margin-top: 0px;
|
|
165
|
-
}
|
|
166
|
-
</style>
|