feat: syslogd

This commit is contained in:
Ben Allfree 2024-01-18 22:34:15 +00:00
parent 7b9cbc3352
commit 34fe0fe43e
9 changed files with 200 additions and 19 deletions

View File

@ -20,6 +20,7 @@
"POCKETSTREAM",
"rizzdown",
"superadmin",
"syslogd",
"unzipper",
"upserting"
]

View File

@ -12,6 +12,10 @@ module.exports = {
name: `edge-ftp`,
script: 'pnpm prod:edge:ftp',
},
{
name: `edge-syslog`,
script: 'pnpm prod:edge:syslog',
},
{
name: `mothership`,
script: 'pnpm prod:mothership',

View File

@ -24,12 +24,14 @@
"dev:superadmin": "cd frontends/superadmin && pnpm dev",
"dev:edge:daemon": "tsx watch src/cli/edge-daemon.ts",
"dev:edge:ftp": "tsx watch src/cli/edge-ftp.ts",
"dev:edge:syslogd": "tsx watch src/cli/edge-syslogd.ts",
"dev:downloader": "pnpm download-versions",
"dev:mothership:maildev": "npx -y maildev",
"dev:mothership:pocketbase": "nodemon --signal SIGTERM --watch src --exec tsx ./src/cli/mothership.ts",
"prod:proxy": "dotenv tsx ./src/cli/proxy/server.ts",
"prod:edge:daemon": "tsx src/cli/edge-daemon.ts",
"prod:edge:ftp": "tsx src/cli/edge-ftp.ts",
"prod:edge:syslog": "tsx src/cli/edge-syslogd.ts",
"prod:mothership": "tsx src/cli/mothership.ts",
"plop": "plop",
"nofile": "cat /proc/sys/fs/file-nr",
@ -50,6 +52,7 @@
"type": "module",
"dependencies": {
"@s-libs/micro-dash": "^16.1.0",
"@types/winston-syslog": "^2.4.3",
"ajv": "^8.12.0",
"boolean": "^3.2.0",
"bottleneck": "^2.19.5",
@ -77,10 +80,12 @@
"pocketbase": "^0.20.1",
"semver": "^7.5.4",
"sqlite3": "^5.1.6",
"syslog-parse": "^2.0.0",
"tail": "^2.2.6",
"tmp": "^0.2.1",
"url-pattern": "^1.0.3",
"winston": "^3.11.0"
"winston": "^3.11.0",
"winston-syslog": "^2.7.0"
},
"devDependencies": {
"@swc/cli": "^0.1.62",

72
pnpm-lock.yaml generated
View File

@ -11,6 +11,9 @@ importers:
'@s-libs/micro-dash':
specifier: ^16.1.0
version: 16.1.0
'@types/winston-syslog':
specifier: ^2.4.3
version: 2.4.3
ajv:
specifier: ^8.12.0
version: 8.12.0
@ -92,6 +95,9 @@ importers:
sqlite3:
specifier: ^5.1.6
version: 5.1.6
syslog-parse:
specifier: ^2.0.0
version: 2.0.0
tail:
specifier: ^2.2.6
version: 2.2.6
@ -104,6 +110,9 @@ importers:
winston:
specifier: ^3.11.0
version: 3.11.0
winston-syslog:
specifier: ^2.7.0
version: 2.7.0(winston@3.11.0)
devDependencies:
'@swc/cli':
specifier: ^0.1.62
@ -1421,6 +1430,12 @@ packages:
resolution: {integrity: sha512-mZ0onxTS5OyfSwBNecTKT0h79e4XXHrc9RI5tQfEAf+Fp6NbBmNnc0kg59HO+97V+y3opS+sfo4k4qpYwLt6NQ==}
dev: true
/@types/glossy@0.1.3:
resolution: {integrity: sha512-CrdAR+ZgRf0MQnDAW4tUm2LpPmfC6sAWlrBwcX0O2oUKyZvseb6wlHZ0alo++DyaLckxqM4CUa+EfzyITJM7mA==}
dependencies:
'@types/node': 20.8.10
dev: false
/@types/http-cache-semantics@4.0.3:
resolution: {integrity: sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==}
dev: true
@ -1527,7 +1542,6 @@ packages:
resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==}
dependencies:
undici-types: 5.26.5
dev: true
/@types/pug@2.0.8:
resolution: {integrity: sha512-QzhsZ1dMGyJbn/D9V80zp4GIA4J4rfAjCCxc3MP+new0E8dyVdSkR735Lx+n3LIaHNFcjHL5+TbziccuT+fdoQ==}
@ -1606,6 +1620,15 @@ packages:
'@types/connect': 3.4.38
dev: true
/@types/winston-syslog@2.4.3:
resolution: {integrity: sha512-z9mO5hxDls4lSTth76sddIETonCMLguppeudk1YxBz4Y/OmdRkeKMfrOTfH74T9gN5WllLnF8XbHdiM8K6EL7A==}
dependencies:
'@types/glossy': 0.1.3
'@types/node': 20.8.10
winston: 3.11.0
winston-transport: 4.6.0
dev: false
/a-sync-waterfall@1.0.1:
resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==}
dev: true
@ -1961,6 +1984,14 @@ packages:
resolution: {integrity: sha512-v4N2l3RxL+m4zDxyxz3Ne2aTmiPn8ZUpKFpdPtO+ItW1NcTCXA7JeHG5GMBSvoKSkQZ9ycS+EouDVxYB9ufKWA==}
dev: true
/bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
requiresBuild: true
dependencies:
file-uri-to-path: 1.0.0
dev: false
optional: true
/bl@1.2.3:
resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==}
dependencies:
@ -3491,6 +3522,12 @@ packages:
engines: {node: '>=4'}
dev: false
/file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
requiresBuild: true
dev: false
optional: true
/filelist@1.0.4:
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
dependencies:
@ -3890,6 +3927,11 @@ packages:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
dev: true
/glossy@0.1.7:
resolution: {integrity: sha512-mTCC51QFadK75MvAhrL5nPVIP291NjML1guo10Sa7Yj04tJU4V++Vgm780NIddg9etQD9D8FM67hFGqM8EE2HQ==}
engines: {node: '>= 0.2.5'}
dev: false
/gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
@ -7322,6 +7364,11 @@ packages:
periscopic: 3.1.0
dev: true
/syslog-parse@2.0.0:
resolution: {integrity: sha512-FI6xGyKM9dRdNCrCWiEy1QhRZskDYkW+lUNAIXkFeht0/XCsSdZ7UsPANFLk0h8b+8Is6Ll8bllUNjME+XCANA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: false
/tail@2.2.6:
resolution: {integrity: sha512-IQ6G4wK/t8VBauYiGPLx+d3fA5XjSVagjWV5SIYzvEvglbQjwEcukeYI68JOPpdydjxhZ9sIgzRlSmwSpphHyw==}
engines: {node: '>= 6.0.0'}
@ -7627,7 +7674,6 @@ packages:
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
dev: true
/undici@5.26.5:
resolution: {integrity: sha512-cSb4bPFd5qgR7qr2jYAi0hlX9n5YKK2ONKkLFkxl+v/9BvC0sOpZjBHDBSXc5lWAf5ty9oZdRXytBIHzgUcerw==}
@ -7665,6 +7711,16 @@ packages:
'@types/unist': 3.0.2
dev: true
/unix-dgram@2.0.6:
resolution: {integrity: sha512-AURroAsb73BZ6CdAyMrTk/hYKNj3DuYYEuOaB8bYMOHGKupRNScw90Q5C71tWJc3uE7dIeXRyuwN0xLLq3vDTg==}
engines: {node: '>=0.10.48'}
requiresBuild: true
dependencies:
bindings: 1.5.0
nan: 2.18.0
dev: false
optional: true
/unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
@ -7864,6 +7920,18 @@ packages:
string-width: 5.1.2
dev: true
/winston-syslog@2.7.0(winston@3.11.0):
resolution: {integrity: sha512-w+V0lHO2W6XqcYlvVi4DrblwJShvQbAaruRvUlMPzH1Z+dYvUvo4ra2hhoF6UNTFmC9LBltcTG05ypYL6S/B8A==}
engines: {node: '>= 8'}
peerDependencies:
winston: ^3.8.2
dependencies:
glossy: 0.1.7
winston: 3.11.0
optionalDependencies:
unix-dgram: 2.0.6
dev: false
/winston-transport@4.6.0:
resolution: {integrity: sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==}
engines: {node: '>= 12.0.0'}

53
src/cli/edge-syslogd.ts Normal file
View File

@ -0,0 +1,53 @@
import {
DEBUG,
DefaultSettingsService,
SETTINGS,
SYSLOGD_PORT,
} from '$constants'
import { InstanceLogger } from '$services'
import { LogLevelName, LoggerService } from '$src/shared'
import * as dgram from 'dgram'
import parse from 'syslog-parse'
const server = dgram.createSocket('udp4')
DefaultSettingsService(SETTINGS)
const PORT = SYSLOGD_PORT()
const HOST = '0.0.0.0'
const { dbg, info, error } = LoggerService({
level: DEBUG() ? LogLevelName.Debug : LogLevelName.Info,
}).create(`edge-syslogd`)
console.log(`debug is ${DEBUG()}`)
server.on('error', (err) => {
console.log(`Server error:\n${err.stack}`)
server.close()
})
server.on('message', (msg, rinfo) => {
const raw = msg.toString()
const parsed = parse(raw)
if (!parsed) {
return
}
dbg(parsed)
const { process: instanceId, severity, message } = parsed
const logger = InstanceLogger(instanceId, `exec`)
if (severity === 'info') {
logger.info(message)
} else {
logger.error(message)
}
})
server.on('listening', () => {
const address = server.address()
console.log(`Server listening ${address.address}:${address.port}`)
})
server.bind(PORT, HOST)

View File

@ -98,6 +98,8 @@ export const SETTINGS = {
TEST_EMAIL: mkString(),
LS_WEBHOOK_SECRET: mkString(''),
SYSLOGD_PORT: mkNumber(6514),
}
;(() => {
let passed = true
@ -221,6 +223,8 @@ export const TEST_EMAIL = () => settings().TEST_EMAIL
export const LS_WEBHOOK_SECRET = () => settings().LS_WEBHOOK_SECRET
export const SYSLOGD_PORT = () => settings().SYSLOGD_PORT
/** Helpers */
export const MOTHERSHIP_DATA_ROOT = () => INSTANCE_DATA_ROOT(MOTHERSHIP_NAME())

View File

@ -25,8 +25,8 @@ export function InstanceLogger(instanceId: string, target: string) {
}
const logDirectory = mkInstanceDataPath(instanceId, `logs`)
console.log(`Creating ${logDirectory}`)
if (!fs.existsSync(logDirectory)) {
console.log(`Creating ${logDirectory}`)
fs.mkdirSync(logDirectory, { recursive: true })
}

View File

@ -1,9 +1,10 @@
import {
APEX_DOMAIN,
SYSLOGD_PORT,
mkContainerHomePath,
mkInstanceDataPath,
} from '$constants'
import { InstanceLogger, PortService } from '$services'
import { PortService } from '$services'
import {
LoggerService,
SingletonBaseConfig,
@ -19,6 +20,7 @@ import MemoryStream from 'memorystream'
import { gte } from 'semver'
import { AsyncReturnType } from 'type-fest'
import { PocketbaseReleaseVersionService } from '../PocketbaseReleaseVersionService'
import { SyslogLogger } from '../SyslogService'
export type Env = { [_: string]: string }
export type SpawnConfig = {
@ -92,7 +94,7 @@ export const createPocketbaseService = async (
} = _cfg
logger.breadcrumb(subdomain).breadcrumb(instanceId)
const iLogger = InstanceLogger(instanceId, 'exec')
const iLogger = SyslogLogger(instanceId, 'exec')
const _version = version || maxVersion // If _version is blank, we use the max version available
const realVersion = await getVersion(_version)
@ -106,20 +108,9 @@ export const createPocketbaseService = async (
const docker = new Docker()
iLogger.info(`Starting instance`)
const _stdoutData = (data: Buffer) => {
const lines = data.toString().split(/\n/)
lines.forEach((line) => {
iLogger.info(line)
})
}
const _stdoutData = (data: Buffer) => {}
stdout.on('data', _stdoutData)
const _stdErrData = (data: Buffer) => {
const lines = data.toString().split(/\n/)
lines.forEach((line) => {
error(line)
iLogger.error(line)
})
}
const _stdErrData = (data: Buffer) => {}
stderr.on('data', _stdErrData)
const Binds = [
`${mkInstanceDataPath(instanceId)}:${mkContainerHomePath()}`,
@ -154,6 +145,13 @@ export const createPocketbaseService = async (
Hard: 4096,
},
],
LogConfig: {
Type: 'syslog',
Config: {
'syslog-address': `udp://localhost:${SYSLOGD_PORT()}`,
tag: instanceId,
},
},
},
Tty: false,
ExposedPorts: {

View File

@ -0,0 +1,48 @@
import { SYSLOGD_PORT } from '$constants'
import { LoggerService } from '$shared'
import * as winston from 'winston'
import 'winston-syslog'
const loggers: {
[key: string]: {
info: (msg: string) => void
error: (msg: string) => void
}
} = {}
export function SyslogLogger(instanceId: string, target: string) {
const loggerKey = `${instanceId}_${target}`
if (loggers[loggerKey]) {
return loggers[loggerKey]!
}
const logger = winston.createLogger({
format: winston.format.printf((info) => {
return info.message
}),
transports: [
new winston.transports.Syslog({
host: `localhost`,
port: SYSLOGD_PORT(),
app_name: instanceId,
}),
],
})
const { error, warn } = LoggerService()
.create('SyslogLogger')
.breadcrumb(instanceId)
.breadcrumb(target)
const api = {
info: (msg: string) => {
logger.info(msg)
},
error: (msg: string) => {
logger.error(msg)
},
}
loggers[loggerKey] = api
return api
}