From 42b40636991b97fe818007fab19091764fc5db73 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 15 Jul 2022 15:30:14 +0200 Subject: Add ability for client to create server logs --- client/src/root-helpers/logger.ts | 138 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 client/src/root-helpers/logger.ts (limited to 'client/src/root-helpers/logger.ts') diff --git a/client/src/root-helpers/logger.ts b/client/src/root-helpers/logger.ts new file mode 100644 index 000000000..cd559cfa7 --- /dev/null +++ b/client/src/root-helpers/logger.ts @@ -0,0 +1,138 @@ +import { ClientLogCreate } from '@shared/models/server' +import { peertubeLocalStorage } from './peertube-web-storage' +import { UserTokens } from './users' + +export type LoggerHook = (message: LoggerMessage, meta?: LoggerMeta) => void +export type LoggerLevel = 'info' | 'warn' | 'error' + +export type LoggerMessage = string | Error | object +export type LoggerMeta = Error | { [ id: string ]: any, err?: Error } + +declare global { + interface Window { + logger: Logger + } +} + +class Logger { + private readonly hooks: { level: LoggerLevel, hook: LoggerHook }[] = [] + + info (message: LoggerMessage, meta?: LoggerMeta) { + this.runHooks('info', message, meta) + + if (meta) console.log(message, meta) + else console.log(message) + } + + warn (message: LoggerMessage, meta?: LoggerMeta) { + this.runHooks('warn', message, meta) + + if (meta) console.warn(message, meta) + else console.warn(message) + } + + error (message: LoggerMessage, meta?: LoggerMeta) { + this.runHooks('error', message, meta) + + if (meta) console.error(message, meta) + else console.error(message) + } + + addHook (level: LoggerLevel, hook: LoggerHook) { + this.hooks.push({ level, hook }) + } + + registerServerSending (serverUrl: string) { + this.addHook('warn', (message, meta) => this.sendClientLog(serverUrl, this.buildServerLogPayload('warn', message, meta))) + this.addHook('error', (message, meta) => this.sendClientLog(serverUrl, this.buildServerLogPayload('error', message, meta))) + } + + sendClientLog (serverUrl: string, payload: ClientLogCreate | null) { + if (!payload) return + + const headers = new Headers({ + Accept: 'application/json', + 'Content-Type': 'application/json' + }) + + try { + const tokens = UserTokens.getUserTokens(peertubeLocalStorage) + + if (tokens) headers.set('Authorization', `${tokens.tokenType} ${tokens.accessToken}`) + } catch (err) { + console.error('Cannot set tokens to client log sender.', { err }) + } + + try { + fetch(serverUrl + '/api/v1/server/logs/client', { + headers, + method: 'POST', + body: JSON.stringify(payload) + }) + } catch (err) { + console.error('Cannot send client warn/error to server.', err) + } + } + + private buildServerLogPayload (level: Extract, message: LoggerMessage, meta?: LoggerMeta) { + if (!message) return null + + return { + message: this.buildMessageServerLogPayload(message), + userAgent: navigator.userAgent, + url: window.location.href, + level, + stackTrace: this.buildStackServerLogPayload(message, meta), + meta: this.buildMetaServerLogPayload(meta) + } + } + + private buildMessageServerLogPayload (message: LoggerMessage) { + if (typeof message === 'string') return message + if (message instanceof Error) return message.message + + return JSON.stringify(message) + } + + private buildStackServerLogPayload (message: LoggerMessage, meta?: LoggerMeta) { + if (message instanceof Error) return message.stack + if (meta instanceof Error) return meta.stack + if (meta?.err instanceof Error) return meta.err.stack + + return undefined + } + + private buildMetaServerLogPayload (meta?: LoggerMeta) { + if (!meta) return undefined + if (meta instanceof Error) return undefined + + let result: string + + try { + result = JSON.stringify(meta, (key, value) => { + if (key === 'err') return undefined + + return value + }) + } catch (err) { + console.error('Cannot stringify meta.', err) + } + + return result + } + + private runHooks (level: LoggerLevel, message: LoggerMessage, meta?: LoggerMeta) { + for (const hookObj of this.hooks) { + if (hookObj.level !== level) continue + + hookObj.hook(message, meta) + } + } +} + +const logger = window.logger || new Logger() +window.logger = logger + +export { + logger +} -- cgit v1.2.3