rtail 0.2.1 → 2.0.0-rc.3
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/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +633 -82
- package/bin/rtail.js +97 -0
- package/package.json +15 -67
- package/.jscsrc +0 -28
- package/.jshintrc +0 -11
- package/.npmignore +0 -4
- package/.scss-lint.yml +0 -11
- package/cli/lib/webapp.js +0 -54
- package/cli/rtail-client.js +0 -156
- package/cli/rtail-server.js +0 -144
- package/dist/bundle.min.js +0 -18
- package/dist/css/bundle.min.css +0 -199
- package/dist/images/common/btn-fav-off.svg +0 -16
- package/dist/images/common/btn-fav-on.svg +0 -16
- package/dist/images/common/favicon-16x16.png +0 -0
- package/dist/images/common/favicon-32x32.png +0 -0
- package/dist/images/common/favicon-96x96.png +0 -0
- package/dist/images/common/icn-filter.svg +0 -16
- package/dist/images/common/icn-resume.svg +0 -16
- package/dist/images/common/logo-rtail.svg +0 -26
- package/dist/images/dark/btn-github.svg +0 -16
- package/dist/images/dark/btn-info.svg +0 -20
- package/dist/images/dark/btn-json-collapse.svg +0 -16
- package/dist/images/dark/btn-json-expand.svg +0 -16
- package/dist/images/dark/btn-settings.svg +0 -16
- package/dist/images/dark/icn-close-date.svg +0 -16
- package/dist/images/dark/icn-font-decrease.svg +0 -19
- package/dist/images/dark/icn-font-increase.svg +0 -19
- package/dist/images/dark/icn-font-reset.svg +0 -18
- package/dist/images/dark/icn-search.svg +0 -16
- package/dist/images/dark/icn-sort-asc.svg +0 -18
- package/dist/images/dark/icn-sort-desc.svg +0 -18
- package/dist/images/dark/icn-theme-dark.svg +0 -18
- package/dist/images/dark/icn-theme-light.svg +0 -26
- package/dist/images/dark/logo-lukibear.svg +0 -74
- package/dist/images/dark/logo-rtail-info.svg +0 -26
- package/dist/images/light/btn-github.svg +0 -16
- package/dist/images/light/btn-info.svg +0 -20
- package/dist/images/light/btn-json-collapse.svg +0 -16
- package/dist/images/light/btn-json-expand.svg +0 -16
- package/dist/images/light/btn-settings.svg +0 -16
- package/dist/images/light/icn-close-date.svg +0 -16
- package/dist/images/light/icn-font-decrease.svg +0 -19
- package/dist/images/light/icn-font-increase.svg +0 -19
- package/dist/images/light/icn-font-reset.svg +0 -18
- package/dist/images/light/icn-search.svg +0 -16
- package/dist/images/light/icn-sort-asc.svg +0 -18
- package/dist/images/light/icn-sort-desc.svg +0 -18
- package/dist/images/light/icn-theme-dark.svg +0 -18
- package/dist/images/light/icn-theme-light.svg +0 -26
- package/dist/images/light/logo-lukibear.svg +0 -74
- package/dist/images/light/logo-rtail-info.svg +0 -26
- package/dist/index.html +0 -1
- package/scripts/npm-publish.sh +0 -21
- package/scripts/set-version.sh +0 -7
- package/test/rtail-client.test.js +0 -131
- package/test/rtail-server.test.js +0 -138
- package/test/runner.js +0 -31
- package/test/util.js +0 -65
package/bin/rtail.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
/*!
|
|
5
|
+
* The npm entry point: find this machine's binary and become it.
|
|
6
|
+
*
|
|
7
|
+
* rtail is a Go program. npm is one of the ways to get it, because the people
|
|
8
|
+
* who pipe logs into it are often already standing in a project with a
|
|
9
|
+
* package.json — but nothing here implements the client, and this file is the
|
|
10
|
+
* only JavaScript left in the install.
|
|
11
|
+
*
|
|
12
|
+
* ** Why a launcher at all. **
|
|
13
|
+
*
|
|
14
|
+
* The binaries ship as one package per platform (`@rtail/darwin-arm64` and
|
|
15
|
+
* friends), each declaring `os` and `cpu`, listed here as optional
|
|
16
|
+
* dependencies. npm installs the one that matches and silently skips the
|
|
17
|
+
* rest, so a Linux box downloads a Linux binary and nothing else. That is the
|
|
18
|
+
* arrangement esbuild uses, and it is the only one that needs no install-time
|
|
19
|
+
* download: no postinstall script, no curl, nothing that breaks behind a
|
|
20
|
+
* proxy or under `--ignore-scripts`.
|
|
21
|
+
*
|
|
22
|
+
* The cost is this file. A platform package can declare its own `bin`, and
|
|
23
|
+
* for a local install npm would link it — but `npm i -g rtail` links the bins
|
|
24
|
+
* of the named package only, never a transitive dependency's, so a global
|
|
25
|
+
* install would leave no `rtail` on PATH. Hence a launcher in the package the
|
|
26
|
+
* user actually named.
|
|
27
|
+
*
|
|
28
|
+
* Anyone who would rather not have Node in the path at all can take the
|
|
29
|
+
* binary straight from a release tarball, `go install`, or the container.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const { spawnSync } = require('node:child_process')
|
|
33
|
+
const { constants } = require('node:os')
|
|
34
|
+
|
|
35
|
+
// Go and Node spell the same architectures differently.
|
|
36
|
+
const PLATFORMS = {
|
|
37
|
+
'darwin-arm64': '@rtail/darwin-arm64',
|
|
38
|
+
'darwin-x64': '@rtail/darwin-x64',
|
|
39
|
+
'linux-arm64': '@rtail/linux-arm64',
|
|
40
|
+
'linux-x64': '@rtail/linux-x64',
|
|
41
|
+
'win32-x64': '@rtail/win32-x64'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const key = `${process.platform}-${process.arch}`
|
|
45
|
+
const pkg = PLATFORMS[key]
|
|
46
|
+
|
|
47
|
+
if (!pkg) {
|
|
48
|
+
console.error(
|
|
49
|
+
`rtail: no prebuilt binary for ${key}.\n` +
|
|
50
|
+
'Build one with `go install github.com/kilianc/rtail/v2/cmd/rtail@latest`,\n' +
|
|
51
|
+
'or open an issue at https://github.com/kilianc/rtail/issues if this\n' +
|
|
52
|
+
'platform should be published.'
|
|
53
|
+
)
|
|
54
|
+
process.exit(1)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const exe = 'win32' === process.platform ? 'rtail.exe' : 'rtail'
|
|
58
|
+
|
|
59
|
+
let binary
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
binary = require.resolve(`${pkg}/bin/${exe}`)
|
|
63
|
+
} catch {
|
|
64
|
+
// The optional dependency was skipped — almost always --no-optional, or a
|
|
65
|
+
// lockfile copied from a machine of a different shape.
|
|
66
|
+
console.error(
|
|
67
|
+
`rtail: ${pkg} is not installed.\n` +
|
|
68
|
+
'It is an optional dependency carrying the binary for this platform.\n' +
|
|
69
|
+
`Reinstall without --no-optional, or \`npm install ${pkg}\` directly.`
|
|
70
|
+
)
|
|
71
|
+
process.exit(1)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*!
|
|
75
|
+
* stdio is inherited, which is the whole point.
|
|
76
|
+
*
|
|
77
|
+
* rtail is a pipe. Its stdin is the producer's stdout and its own stdout is
|
|
78
|
+
* whatever came next in the shell, so the file descriptors are handed over
|
|
79
|
+
* untouched rather than copied through this process — which also means no
|
|
80
|
+
* buffering, no mangling of a partial line, and a SIGINT at the terminal
|
|
81
|
+
* reaching the binary directly.
|
|
82
|
+
*/
|
|
83
|
+
const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' })
|
|
84
|
+
|
|
85
|
+
if (result.error) {
|
|
86
|
+
console.error(`rtail: ${result.error.message}`)
|
|
87
|
+
process.exit(1)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// A child killed by a signal has a null status; 128 + signal is the shell's
|
|
91
|
+
// convention for reporting that, and exiting 0 would claim it succeeded.
|
|
92
|
+
if (null === result.status) {
|
|
93
|
+
const number = constants.signals[result.signal]
|
|
94
|
+
process.exit(number ? 128 + number : 1)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
process.exit(result.status)
|
package/package.json
CHANGED
|
@@ -1,76 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rtail",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0-rc.3",
|
|
4
4
|
"description": "Terminal output to the browser in seconds, using UNIX pipes.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"rtail": "
|
|
15
|
-
"rtail-server": "./cli/rtail-server.js"
|
|
5
|
+
"keywords": ["log", "logging", "logio", "logs", "tail", "udp"],
|
|
6
|
+
"bin": { "rtail": "bin/rtail.js" },
|
|
7
|
+
"files": ["bin", "NOTICE"],
|
|
8
|
+
"engines": { "node": ">=20" },
|
|
9
|
+
"optionalDependencies": {
|
|
10
|
+
"@rtail/linux-x64": "2.0.0-rc.3",
|
|
11
|
+
"@rtail/linux-arm64": "2.0.0-rc.3",
|
|
12
|
+
"@rtail/darwin-x64": "2.0.0-rc.3",
|
|
13
|
+
"@rtail/darwin-arm64": "2.0.0-rc.3",
|
|
14
|
+
"@rtail/win32-x64": "2.0.0-rc.3"
|
|
16
15
|
},
|
|
17
16
|
"repository": {
|
|
18
17
|
"type": "git",
|
|
19
|
-
"url": "git
|
|
20
|
-
},
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"chrono-node": "^1.0.6",
|
|
23
|
-
"debug": "^2.1.0",
|
|
24
|
-
"express": "^4.10.0",
|
|
25
|
-
"json5": "^0.4.0",
|
|
26
|
-
"moniker": "^0.1.2",
|
|
27
|
-
"request": "^2.58.0",
|
|
28
|
-
"socket.io": "^1.1.0",
|
|
29
|
-
"split": "^1.0.0",
|
|
30
|
-
"strip-ansi": "^3.0.0",
|
|
31
|
-
"through2-map": "^2.0.0",
|
|
32
|
-
"update-notifier": "^0.5.0",
|
|
33
|
-
"yargs": "^3.14.0"
|
|
34
|
-
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"angular": "^1.3.15",
|
|
37
|
-
"angular-animate": "^1.4.0",
|
|
38
|
-
"angular-localforage": "^1.2.2",
|
|
39
|
-
"angular-moment": "^0.10.1",
|
|
40
|
-
"angular-rt-popup": "^1.0.5",
|
|
41
|
-
"angular-ui-router": "^0.2.15",
|
|
42
|
-
"ansi_up": "^1.2.1",
|
|
43
|
-
"autoprefixer-core": "^5.1.11",
|
|
44
|
-
"chai": "^3.0.0",
|
|
45
|
-
"del": "^1.1.1",
|
|
46
|
-
"gulp": "^3.9.0",
|
|
47
|
-
"gulp-ejs": "^1.1.0",
|
|
48
|
-
"gulp-livereload": "^3.8.0",
|
|
49
|
-
"gulp-load-plugins": "^0.10.0",
|
|
50
|
-
"gulp-minify-css": "^1.1.1",
|
|
51
|
-
"gulp-minify-html": "^1.0.2",
|
|
52
|
-
"gulp-ng-annotate": "^1.0.0",
|
|
53
|
-
"gulp-postcss": "^5.1.6",
|
|
54
|
-
"gulp-sass": "^2.0.1",
|
|
55
|
-
"gulp-sourcemaps": "^1.5.2",
|
|
56
|
-
"gulp-uglify": "^1.2.0",
|
|
57
|
-
"gulp-useref": "^1.1.2",
|
|
58
|
-
"gulp-util": "^3.0.4",
|
|
59
|
-
"highlight.js": "isagalaev/highlight.js",
|
|
60
|
-
"istanbul": "^0.3.17",
|
|
61
|
-
"istanbul-harmony": "^0.3.16",
|
|
62
|
-
"jquery": "^2.1.4",
|
|
63
|
-
"localforage": "^1.2.2",
|
|
64
|
-
"mocha": "^2.2.5",
|
|
65
|
-
"moment": "^2.10.3",
|
|
66
|
-
"node-sass": "^3.1.1",
|
|
67
|
-
"run-sequence": "^1.1.0"
|
|
68
|
-
},
|
|
69
|
-
"scripts": {
|
|
70
|
-
"app": "DEBUG=rtail:*,api:* gulp app",
|
|
71
|
-
"test": "NODE_ENV=test node --harmony test/runner.js",
|
|
72
|
-
"test:watch": "NODE_ENV=test nodemon --harmony test/runner.js"
|
|
18
|
+
"url": "git+https://github.com/kilianc/rtail.git"
|
|
73
19
|
},
|
|
20
|
+
"homepage": "https://github.com/kilianc/rtail#readme",
|
|
21
|
+
"bugs": "https://github.com/kilianc/rtail/issues",
|
|
74
22
|
"author": "Kilian Ciuffolo <me@nailik.org>",
|
|
75
|
-
"license": "
|
|
23
|
+
"license": "Apache-2.0"
|
|
76
24
|
}
|
package/.jscsrc
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"preset": "airbnb",
|
|
3
|
-
"esnext": true,
|
|
4
|
-
"verbose": true,
|
|
5
|
-
"disallowCapitalizedComments": true,
|
|
6
|
-
"disallowMultipleLineBreaks": true,
|
|
7
|
-
"disallowMultipleSpaces": true,
|
|
8
|
-
"disallowSemicolons": true,
|
|
9
|
-
"disallowSpacesInAnonymousFunctionExpression": null,
|
|
10
|
-
"disallowSpacesInFunctionExpression": null,
|
|
11
|
-
"disallowYodaConditions": null,
|
|
12
|
-
"requireCamelCaseOrUpperCaseIdentifiers": null,
|
|
13
|
-
"requirePaddingNewLinesAfterUseStrict": true,
|
|
14
|
-
"requireSpacesInAnonymousFunctionExpression": {
|
|
15
|
-
"beforeOpeningRoundBrace": true,
|
|
16
|
-
"beforeOpeningCurlyBrace": true
|
|
17
|
-
},
|
|
18
|
-
"safeContextKeyword": ["self", "doc", "ctx"],
|
|
19
|
-
"validateIndentation": 2,
|
|
20
|
-
"plugins": [
|
|
21
|
-
"jscs-jsdoc"
|
|
22
|
-
],
|
|
23
|
-
"jsDoc": {
|
|
24
|
-
"checkAnnotations": "closurecompiler",
|
|
25
|
-
"checkTypes": "strictNativeCase",
|
|
26
|
-
"enforceExistence": "exceptExports"
|
|
27
|
-
}
|
|
28
|
-
}
|
package/.jshintrc
DELETED
package/.npmignore
DELETED
package/.scss-lint.yml
DELETED
package/cli/lib/webapp.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* webapp.js
|
|
3
|
-
* Created by Kilian Ciuffolo on Nov 11, 2014
|
|
4
|
-
* (c) 2014-2015
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use strict'
|
|
8
|
-
|
|
9
|
-
const debug = require('debug')('rtail:webapp')
|
|
10
|
-
const get = require('request').defaults({ encoding: null })
|
|
11
|
-
|
|
12
|
-
// serve frontend from s3
|
|
13
|
-
module.exports = function webapp(opts) {
|
|
14
|
-
let cache = Object.create(null)
|
|
15
|
-
let cacheTTL = opts.ttl
|
|
16
|
-
let s3 = opts.s3
|
|
17
|
-
|
|
18
|
-
/*!
|
|
19
|
-
* wipes out cache every cacheTTL ms
|
|
20
|
-
*/
|
|
21
|
-
setInterval(function () {
|
|
22
|
-
cache = Object.create(null)
|
|
23
|
-
debug('cleared cache')
|
|
24
|
-
}, cacheTTL)
|
|
25
|
-
|
|
26
|
-
/*!
|
|
27
|
-
* middleware
|
|
28
|
-
*/
|
|
29
|
-
return function (req, res) {
|
|
30
|
-
if (cache[req.path]) {
|
|
31
|
-
return serveCache(req, res)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
debug('caching %s', req.path)
|
|
35
|
-
|
|
36
|
-
get(s3 + req.path, function (err, s3res, body) {
|
|
37
|
-
cache[req.path] = {
|
|
38
|
-
headers: s3res.headers,
|
|
39
|
-
body: body
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
serveCache(req, res)
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/*!
|
|
47
|
-
* serves req from cache
|
|
48
|
-
*/
|
|
49
|
-
function serveCache(req, res) {
|
|
50
|
-
debug('serving from cache %s', req.path)
|
|
51
|
-
res.writeHead(200, cache[req.path].headers)
|
|
52
|
-
res.end(cache[req.path].body)
|
|
53
|
-
}
|
|
54
|
-
}
|
package/cli/rtail-client.js
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
":" //# comment; exec /usr/bin/env node --harmony "$0" "$@"
|
|
3
|
-
|
|
4
|
-
/*!
|
|
5
|
-
* rtail-client.js
|
|
6
|
-
* Created by Kilian Ciuffolo on Oct 26, 2014
|
|
7
|
-
* (c) 2014-2015
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
'use strict'
|
|
11
|
-
|
|
12
|
-
const dgram = require('dgram')
|
|
13
|
-
const split = require('split')
|
|
14
|
-
const chrono = require('chrono-node')
|
|
15
|
-
const JSON5 = require('json5')
|
|
16
|
-
const yargs = require('yargs')
|
|
17
|
-
const map = require('through2-map')
|
|
18
|
-
const stripAnsi = require('strip-ansi')
|
|
19
|
-
const moniker_ = require('moniker').choose
|
|
20
|
-
const updateNotifier = require('update-notifier')
|
|
21
|
-
const pkg = require('../package')
|
|
22
|
-
|
|
23
|
-
/*!
|
|
24
|
-
* inform the user of updates
|
|
25
|
-
*/
|
|
26
|
-
updateNotifier({
|
|
27
|
-
packageName: pkg.name,
|
|
28
|
-
packageVersion: pkg.version
|
|
29
|
-
}).notify()
|
|
30
|
-
|
|
31
|
-
/*!
|
|
32
|
-
* parsing argv
|
|
33
|
-
*/
|
|
34
|
-
let argv = yargs
|
|
35
|
-
.usage('Usage: cmd | rtail [OPTIONS]')
|
|
36
|
-
.example('server | rtail > server.log', 'localhost + file')
|
|
37
|
-
.example('server | rtail --id api.domain.com', 'Name the log stream')
|
|
38
|
-
.example('server | rtail --host example.com', 'Sends to example.com')
|
|
39
|
-
.example('server | rtail --port 43567', 'Uses custom port')
|
|
40
|
-
.example('server | rtail --mute', 'No stdout')
|
|
41
|
-
.example('server | rtail --no-tty', 'Strips ansi colors')
|
|
42
|
-
.example('server | rtail --no-date-parse', 'Disable date parsing/stripping')
|
|
43
|
-
.option('host', {
|
|
44
|
-
alias: 'h',
|
|
45
|
-
type: 'string',
|
|
46
|
-
default: '127.0.0.1',
|
|
47
|
-
describe: 'The server host'
|
|
48
|
-
})
|
|
49
|
-
.option('port', {
|
|
50
|
-
alias: 'p',
|
|
51
|
-
type: 'string',
|
|
52
|
-
default: 9999,
|
|
53
|
-
describe: 'The server port'
|
|
54
|
-
})
|
|
55
|
-
.option('id', {
|
|
56
|
-
alias: 'name',
|
|
57
|
-
type: 'string',
|
|
58
|
-
default: function moniker() { return moniker_() } ,
|
|
59
|
-
describe: 'The log stream id'
|
|
60
|
-
})
|
|
61
|
-
.option('mute', {
|
|
62
|
-
alias: 'm',
|
|
63
|
-
type: 'boolean',
|
|
64
|
-
describe: 'Don\'t pipe stdin with stdout'
|
|
65
|
-
})
|
|
66
|
-
.option('tty', {
|
|
67
|
-
type: 'boolean',
|
|
68
|
-
default: true,
|
|
69
|
-
describe: 'Keeps ansi colors'
|
|
70
|
-
})
|
|
71
|
-
.option('parse-date', {
|
|
72
|
-
type: 'boolean',
|
|
73
|
-
default: true,
|
|
74
|
-
describe: 'Looks for dates to use as timestamp'
|
|
75
|
-
})
|
|
76
|
-
.help('help')
|
|
77
|
-
.version(pkg.version, 'version')
|
|
78
|
-
.alias('version', 'v')
|
|
79
|
-
.strict()
|
|
80
|
-
.argv
|
|
81
|
-
|
|
82
|
-
/*!
|
|
83
|
-
* setup pipes
|
|
84
|
-
*/
|
|
85
|
-
if (!argv.mute) {
|
|
86
|
-
if (!process.stdout.isTTY || !argv.tty) {
|
|
87
|
-
process.stdin
|
|
88
|
-
.pipe(map(function (chunk) {
|
|
89
|
-
return stripAnsi(chunk.toString('utf8'))
|
|
90
|
-
}))
|
|
91
|
-
.pipe(process.stdout)
|
|
92
|
-
} else {
|
|
93
|
-
process.stdin.pipe(process.stdout)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/*!
|
|
98
|
-
* initialize socket
|
|
99
|
-
*/
|
|
100
|
-
let isClosed = false
|
|
101
|
-
let isSending = 0
|
|
102
|
-
let socket = dgram.createSocket('udp4')
|
|
103
|
-
let baseMessage = { id: argv.id }
|
|
104
|
-
|
|
105
|
-
socket.bind(function () {
|
|
106
|
-
socket.setBroadcast(true)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
/*!
|
|
110
|
-
* broadcast lines to browser
|
|
111
|
-
*/
|
|
112
|
-
process.stdin
|
|
113
|
-
.pipe(split(null, null, { trailing: false }))
|
|
114
|
-
.on('data', function (line) {
|
|
115
|
-
let timestamp = null
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
// try to JSON parse
|
|
119
|
-
line = JSON5.parse(line)
|
|
120
|
-
} catch (err) {
|
|
121
|
-
// look for timestamps if not an object
|
|
122
|
-
timestamp = argv.parseDate ? chrono.parse(line)[0] : null
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (timestamp) {
|
|
126
|
-
// escape for regexp and remove from line
|
|
127
|
-
timestamp.text = timestamp.text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
|
|
128
|
-
line = line.replace(new RegExp(' *[^ ]?' + timestamp.text + '[^ ]? *'), '')
|
|
129
|
-
// use timestamp as line timestamp
|
|
130
|
-
baseMessage.timestamp = Date.parse(timestamp.start.date())
|
|
131
|
-
} else {
|
|
132
|
-
baseMessage.timestamp = Date.now()
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// update default message
|
|
136
|
-
baseMessage.content = line
|
|
137
|
-
|
|
138
|
-
// prepare binary message
|
|
139
|
-
let buffer = new Buffer(JSON.stringify(baseMessage))
|
|
140
|
-
|
|
141
|
-
// set semaphore
|
|
142
|
-
isSending ++
|
|
143
|
-
|
|
144
|
-
socket.send(buffer, 0, buffer.length, argv.port, argv.host, function () {
|
|
145
|
-
isSending --
|
|
146
|
-
if (isClosed && !isSending) socket.close()
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
/*!
|
|
151
|
-
* drain pipe and exit
|
|
152
|
-
*/
|
|
153
|
-
process.stdin.on('end', function () {
|
|
154
|
-
isClosed = true
|
|
155
|
-
if (!isSending) socket.close()
|
|
156
|
-
})
|
package/cli/rtail-server.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
":" //# comment; exec /usr/bin/env node --harmony "$0" "$@"
|
|
3
|
-
|
|
4
|
-
/*!
|
|
5
|
-
* server.js
|
|
6
|
-
* Created by Kilian Ciuffolo on Oct 26, 2014
|
|
7
|
-
* (c) 2014-2015
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
'use strict'
|
|
11
|
-
|
|
12
|
-
const dgram = require('dgram')
|
|
13
|
-
const app = require('express')()
|
|
14
|
-
const serve = require('express').static
|
|
15
|
-
const http = require('http').Server(app)
|
|
16
|
-
const io = require('socket.io')()
|
|
17
|
-
const yargs = require('yargs')
|
|
18
|
-
const debug = require('debug')('rtail:server')
|
|
19
|
-
const webapp = require('./lib/webapp')
|
|
20
|
-
const updateNotifier = require('update-notifier')
|
|
21
|
-
const pkg = require('../package')
|
|
22
|
-
|
|
23
|
-
/*!
|
|
24
|
-
* inform the user of updates
|
|
25
|
-
*/
|
|
26
|
-
updateNotifier({
|
|
27
|
-
packageName: pkg.name,
|
|
28
|
-
packageVersion: pkg.version
|
|
29
|
-
}).notify()
|
|
30
|
-
|
|
31
|
-
/*!
|
|
32
|
-
* parsing argv
|
|
33
|
-
*/
|
|
34
|
-
let argv = yargs
|
|
35
|
-
.usage('Usage: rtail-server [OPTIONS]')
|
|
36
|
-
.example('rtail-server --web-port 8080', 'Use custom HTTP port')
|
|
37
|
-
.example('rtail-server --udp-port 8080', 'Use custom UDP port')
|
|
38
|
-
.example('rtail-server --web-version stable', 'Always uses latest stable webapp')
|
|
39
|
-
.example('rtail-server --web-version unstable', 'Always uses latest develop webapp')
|
|
40
|
-
.example('rtail-server --web-version 0.1.3', 'Use webapp v0.1.3')
|
|
41
|
-
.option('udp-host', {
|
|
42
|
-
alias: 'uh',
|
|
43
|
-
default: '127.0.0.1',
|
|
44
|
-
describe: 'The listening UDP hostname'
|
|
45
|
-
})
|
|
46
|
-
.option('udp-port', {
|
|
47
|
-
alias: 'up',
|
|
48
|
-
default: 9999,
|
|
49
|
-
describe: 'The listening UDP port'
|
|
50
|
-
})
|
|
51
|
-
.option('web-host', {
|
|
52
|
-
alias: 'wh',
|
|
53
|
-
default: '127.0.0.1',
|
|
54
|
-
describe: 'The listening HTTP hostname'
|
|
55
|
-
})
|
|
56
|
-
.option('web-port', {
|
|
57
|
-
alias: 'wp',
|
|
58
|
-
default: 8888,
|
|
59
|
-
describe: 'The listening HTTP port'
|
|
60
|
-
})
|
|
61
|
-
.option('web-version', {
|
|
62
|
-
type: 'string',
|
|
63
|
-
describe: 'Define web app version to serve'
|
|
64
|
-
})
|
|
65
|
-
.help('help')
|
|
66
|
-
.alias('help', 'h')
|
|
67
|
-
.version(pkg.version, 'version')
|
|
68
|
-
.alias('version', 'v')
|
|
69
|
-
.strict()
|
|
70
|
-
.argv
|
|
71
|
-
|
|
72
|
-
/*!
|
|
73
|
-
* UDP sockets setup
|
|
74
|
-
*/
|
|
75
|
-
let streams = {}
|
|
76
|
-
let socket = dgram.createSocket('udp4')
|
|
77
|
-
|
|
78
|
-
socket.on('message', function (data, remote) {
|
|
79
|
-
// try to decode JSON
|
|
80
|
-
try { data = JSON.parse(data) }
|
|
81
|
-
catch (err) { return debug('invalid data sent') }
|
|
82
|
-
|
|
83
|
-
if (!streams[data.id]) {
|
|
84
|
-
streams[data.id] = []
|
|
85
|
-
io.sockets.emit('streams', Object.keys(streams))
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
let message = {
|
|
89
|
-
timestamp: data.timestamp,
|
|
90
|
-
streamid: data.id,
|
|
91
|
-
host: remote.address,
|
|
92
|
-
port: remote.port,
|
|
93
|
-
content: data.content,
|
|
94
|
-
type: typeof data.content
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// limit backlog to 100 lines
|
|
98
|
-
streams[data.id].length >= 100 && streams[data.id].shift()
|
|
99
|
-
streams[data.id].push(message)
|
|
100
|
-
|
|
101
|
-
debug(JSON.stringify(message))
|
|
102
|
-
io.sockets.to(data.id).emit('line', message)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
/*!
|
|
106
|
-
* socket.io
|
|
107
|
-
*/
|
|
108
|
-
io.on('connection', function (socket) {
|
|
109
|
-
socket.emit('streams', Object.keys(streams))
|
|
110
|
-
socket.on('select stream', function (stream) {
|
|
111
|
-
socket.leave(socket.rooms[0])
|
|
112
|
-
if (!stream) return
|
|
113
|
-
socket.join(stream)
|
|
114
|
-
socket.emit('backlog', streams[stream])
|
|
115
|
-
})
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
/*!
|
|
119
|
-
* serve static webapp from S3
|
|
120
|
-
*/
|
|
121
|
-
if (!argv.webVersion) {
|
|
122
|
-
app.use(serve(__dirname + '/../dist'))
|
|
123
|
-
} else if ('development' === argv.webVersion) {
|
|
124
|
-
app.use('/app', serve(__dirname + '/../app'))
|
|
125
|
-
app.use('/node_modules', serve(__dirname + '/../node_modules'))
|
|
126
|
-
io.path('/app/socket.io')
|
|
127
|
-
} else {
|
|
128
|
-
app.use(webapp({
|
|
129
|
-
s3: 'http://rtail.s3-website-us-east-1.amazonaws.com/' + argv.webVersion,
|
|
130
|
-
ttl: 1000 * 60 * 60 * 6 // 6H
|
|
131
|
-
}))
|
|
132
|
-
|
|
133
|
-
debug('serving webapp from: http://rtail.s3-website-us-east-1.amazonaws.com/%s', argv.webVersion)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/*!
|
|
137
|
-
* listen!
|
|
138
|
-
*/
|
|
139
|
-
io.attach(http, { serveClient: false })
|
|
140
|
-
socket.bind(argv.udpPort, argv.udpHost)
|
|
141
|
-
http.listen(argv.webPort, argv.webHost)
|
|
142
|
-
|
|
143
|
-
debug('UDP server listening: %s:%s', argv.udpHost, argv.udpPort)
|
|
144
|
-
debug('HTTP server listening: http://%s:%s', argv.webHost, argv.webPort)
|