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/produce-log.sh DELETED
@@ -1,3 +0,0 @@
1
- #!/bin/fish
2
-
3
- echo "{ timestamp: '"(date -Ins)"', level: 'INFO', message: 'Finished job execution5'}"
package/src/config.ts DELETED
@@ -1,28 +0,0 @@
1
- import { Command } from 'commander'
2
- import { extractIndexTokens as extractIndexTokensFn } from './log-index'
3
-
4
- const program = new Command('web-log-viewer')
5
- program.version('0.0.1')
6
- program.option('--port <port>', 'specify the port where the server will run', '8000')
7
- program.option(
8
- '-p, --parser <filename>',
9
- 'specify the script used for parsing lines of text from stdin and converting them to JSON. Defaults to parse using `json5` (https://www.npmjs.com/package/json5)',
10
- )
11
- program.option(
12
- '-k, --index-keys',
13
- 'allow searching by the log object keys, instead of just its values',
14
- false,
15
- )
16
- program.option('-s, --stdout', 'log every message coming in to stdout', false)
17
-
18
- program.parse(process.argv)
19
- const options = program.opts()
20
-
21
- const config = {
22
- port: options.port || 8000,
23
- stdout: options.stdout || false,
24
- parseRawMessage: options.parser ? require(options.parser) : require('./defaultMessageParser'),
25
- extractIndexTokens: extractIndexTokensFn({ includeObjectKeys: options.indexKeys }),
26
- }
27
-
28
- export { config }
@@ -1,16 +0,0 @@
1
- import json5 from 'json5'
2
-
3
- /**
4
- * Default parser implementation, that transforms a string (usually a single
5
- * line of text coming in from **stdin**) into a JSON object containing the data
6
- * present in a log message
7
- */
8
-
9
- module.exports = (rawMessage: string) => {
10
- try {
11
- return json5.parse(rawMessage)
12
- } catch (err) {
13
- const msg = { message: rawMessage }
14
- return msg
15
- }
16
- }
package/src/log-index.ts DELETED
@@ -1,44 +0,0 @@
1
- type Tokens = string[]
2
-
3
- interface Config {
4
- includeObjectKeys: boolean
5
- }
6
-
7
- const extractIndexTokens = (cfg: Config) => (obj: Object, tokens: Tokens = []): Tokens => {
8
- let newTokens = [...tokens]
9
- if (cfg.includeObjectKeys) {
10
- newTokens = [...newTokens, ...Object.keys(obj)]
11
- }
12
- return Object.values(obj).reduce((acc, val) => {
13
- if (!val) {
14
- return acc
15
- } else if (typeof val !== 'object') {
16
- return [...acc, removeDiacritics(val.toString())]
17
- } else {
18
- return extractIndexTokens(cfg)(val, acc)
19
- }
20
- }, newTokens)
21
- }
22
-
23
- // see: https://github.com/esamattis/underscore.string
24
- const removeDiacritics = (str: string) => {
25
- const accents = 'ąàáäâãåæăćčĉęèéëêĝĥìíïîĵłľńňòóöőôõðøśșşšŝťțţŭùúüűûñÿýçżźž'
26
- const result = 'aaaaaaaaaccceeeeeghiiiijllnnoooooooossssstttuuuuuunyyczzz'
27
-
28
- return str.toLowerCase().replace(/.{1}/g, function (c: string) {
29
- var index = accents.indexOf(c)
30
- return index === -1 ? c : result[index]
31
- })
32
- }
33
-
34
- const isIndexMatch = (filter: string) => {
35
- if (!filter) {
36
- return () => true
37
- }
38
- const tokens = removeDiacritics(filter).split(/\s+/)
39
- return (index: Tokens) => tokens.every(token => index.find(isSingleMatch(token)))
40
- }
41
-
42
- const isSingleMatch = (tokenToSearch: string) => (data: string) => data.indexOf(tokenToSearch) != -1
43
-
44
- export { extractIndexTokens, isIndexMatch }
package/src/server.ts DELETED
@@ -1,135 +0,0 @@
1
- import { map, runEffects, tap } from '@most/core'
2
- import { newDefaultScheduler } from '@most/scheduler'
3
- import express from 'express'
4
- import http from 'http'
5
- import { pipe } from 'ramda'
6
- import WebSocket from 'ws'
7
- import { createReadlineStream } from './stream-utils'
8
- import { ClientMessage, LogMessage, ServerMessage } from './types'
9
- import { config } from './config'
10
- import { isIndexMatch } from './log-index'
11
- import path from 'path'
12
-
13
- const LOG_WINDOW_SIZE = 100
14
-
15
- type ClientStatus =
16
- | { mode: 'tail' }
17
- | {
18
- mode: 'static'
19
- // the `seq` of the first message the client is locked on
20
- start: number
21
- }
22
- const clients: ClientStatus[] = []
23
-
24
- /**Contains all the logs that came into the server up until now */
25
- const logs: LogMessage[] = []
26
-
27
- // setup the server
28
- const app = express()
29
- app.use(express.static(path.join(__dirname, 'public')))
30
- const server = http.createServer(app)
31
- const wss = new WebSocket.Server({ server })
32
-
33
- //start our server
34
- server.listen(config.port, () => {
35
- const address = server.address()
36
- if (address) {
37
- console.log(
38
- `Started on ${
39
- typeof address == 'string' ? address : `${address.port}`
40
- }. Waiting for log messages on stdin...`,
41
- )
42
-
43
- // setup the log stream from stdin to clients
44
- const logStream = setupLogStream()()
45
- runEffects(logStream, newDefaultScheduler())
46
-
47
- // waits for WS clients to connect
48
- wss.on('connection', setupNewClient)
49
- }
50
- })
51
-
52
- /**
53
- * Sets up the stream that receives messages from **stdin** and
54
- * pipes them to all registered clients using websockets.
55
- */
56
- const setupLogStream = () =>
57
- pipe(
58
- () => createReadlineStream(),
59
- tap(rawMessage => {
60
- if (config.stdout) {
61
- console.log(rawMessage)
62
- }
63
- }),
64
- map(rawMessage => {
65
- const data = config.parseRawMessage(rawMessage)
66
- const msg: LogMessage = {
67
- seq: logs.length + 1,
68
- data,
69
- index: config.extractIndexTokens(data),
70
- }
71
- logs.push(msg)
72
- return msg
73
- }),
74
- tap((rawLog: LogMessage) => {
75
- const updateMsg: ServerMessage = {
76
- type: 'update',
77
- size: logs.length,
78
- message: rawLog,
79
- }
80
- wss.clients.forEach(ws => {
81
- ws.send(encode(updateMsg))
82
- })
83
- }),
84
- )
85
-
86
- /**
87
- * Function executed every time a new WS client connects to the server.
88
- * This will send a `InitMessage` to that client, so it can immediately start showing to the user.
89
- */
90
- function setupNewClient(ws: WebSocket) {
91
- const clientId = clients.length
92
- clients.push({ mode: 'tail' })
93
-
94
- // send a window with the last logs to newly registered clients
95
- ws.send(encode(buildTailMessage('')))
96
-
97
- ws.on('message', encodedMsg => {
98
- const msg = decode(encodedMsg) as ClientMessage
99
- if (msg.mode == 'tail') {
100
- ws.send(encode(buildTailMessage(msg.filter)))
101
- } else if (msg.mode == 'static') {
102
- ws.send(encode(buildStaticMessage(msg.filter, msg.offsetSeq)))
103
- }
104
- })
105
-
106
- function buildTailMessage(filter: string): ServerMessage {
107
- const matcher = isIndexMatch(filter)
108
- const filteredLogs = logs.filter(l => matcher(l.index)).map((l, i) => ({ ...l, seq: i + 1 }))
109
- return {
110
- type: 'init',
111
- mode: 'tail',
112
- size: filteredLogs.length,
113
- offsetSeq: -1,
114
- window: filteredLogs.slice(-LOG_WINDOW_SIZE),
115
- }
116
- }
117
-
118
- function buildStaticMessage(filter: string, offsetSeq: number): ServerMessage {
119
- const matcher = isIndexMatch(filter)
120
- const filteredLogs = logs.filter(l => matcher(l.index)).map((l, i) => ({ ...l, seq: i + 1 }))
121
- return {
122
- type: 'init',
123
- mode: 'static',
124
- size: filteredLogs.length,
125
- offsetSeq,
126
- window: filteredLogs.slice(
127
- Math.max(offsetSeq - LOG_WINDOW_SIZE / 2, 0),
128
- offsetSeq + LOG_WINDOW_SIZE / 2,
129
- ),
130
- }
131
- }
132
- }
133
-
134
- const encode = (data: ServerMessage) => JSON.stringify(data)
135
- const decode = (data: WebSocket.Data): ClientMessage => JSON.parse(data.toString('utf-8'))
@@ -1,28 +0,0 @@
1
- import { createAdapter } from "@most/adapter";
2
- import * as readline from "readline";
3
- import json5 from "json5";
4
-
5
- const memoizedFromJson = (s: string) => {
6
- try {
7
- return json5.parse(s);
8
- } catch (err) {
9
- return {};
10
- }
11
- };
12
-
13
- const createReadlineStream = () => {
14
- const [induce, readlineStream] = createAdapter<string>();
15
-
16
- const rl = readline.createInterface({
17
- input: process.stdin,
18
- output: process.stdout,
19
- terminal: false,
20
- });
21
- rl.on("line", (line) => {
22
- induce(line);
23
- });
24
-
25
- return readlineStream;
26
- };
27
-
28
- export { memoizedFromJson, createReadlineStream };
package/src/types.ts DELETED
@@ -1,51 +0,0 @@
1
- type OperationMode = 'static' | 'tail'
2
-
3
- /**A log message has sent by the server to the clients */
4
- type LogMessage = {
5
- seq: number
6
- data: Record<string, any>
7
- index: string[]
8
- }
9
-
10
- /**A log message that's already been formatted and ready to be displayed */
11
- type FormattedMessage = {
12
- seq: number
13
- rawMessage: Record<string, any>
14
- formattedMessage: Record<string, any>
15
- }
16
-
17
- /**A function that formats a log message */
18
- type LogFormatter = Record<string, LogColumnFormatter>
19
- type LogColumnFormatter = (message: LogMessage['data'], seq: number) => any
20
-
21
- // ***
22
- // SERVER MESSAGES
23
- // ***
24
-
25
- // the message sent to clients that either just connected or changed operation model (tail <-> static)
26
- type InitMessage = {
27
- type: 'init'
28
- mode: OperationMode
29
- size: number
30
- offsetSeq: number
31
- window: LogMessage[]
32
- }
33
- // the message sent to all clients once a new message comes in from stdin
34
- type UpdateMessage = { type: 'update'; size: number; message: LogMessage }
35
- // all messages in the direction Server -> Client
36
- type ServerMessage = InitMessage | UpdateMessage
37
-
38
- // CLIENT MESSAGES
39
- type StaticClientMessage = { mode: 'static'; filter: string; offsetSeq: number }
40
- type TailClientMessage = { mode: 'tail'; filter: string }
41
- /**Messages sent from the client to the server, basically to signal it want to swith operation mode */
42
- type ClientMessage = StaticClientMessage | TailClientMessage
43
-
44
- export type {
45
- LogMessage,
46
- FormattedMessage,
47
- LogFormatter,
48
- LogColumnFormatter,
49
- ServerMessage,
50
- ClientMessage,
51
- }
@@ -1,3 +0,0 @@
1
- {
2
- "recommendations": ["svelte.svelte-vscode"]
3
- }