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.
@@ -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
@@ -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
- }
@@ -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>
@@ -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
- };
@@ -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>