1 import { ClientLogCreate } from '@shared/models/server'
2 import { peertubeLocalStorage } from './peertube-web-storage'
3 import { OAuthUserTokens } from './users'
5 export type LoggerHook = (message: LoggerMessage, meta?: LoggerMeta) => void
6 export type LoggerLevel = 'info' | 'warn' | 'error'
8 export type LoggerMessage = string | Error | object
9 export type LoggerMeta = Error | { [ id: string ]: any, err?: Error }
18 private readonly hooks: { level: LoggerLevel, hook: LoggerHook }[] = []
20 info (message: LoggerMessage, meta?: LoggerMeta) {
21 this.runHooks('info', message, meta)
23 if (meta) console.log(message, meta)
24 else console.log(message)
27 warn (message: LoggerMessage, meta?: LoggerMeta) {
28 this.runHooks('warn', message, meta)
30 if (meta) console.warn(message, meta)
31 else console.warn(message)
34 error (message: LoggerMessage, meta?: LoggerMeta) {
35 this.runHooks('error', message, meta)
37 if (meta) console.error(message, meta)
38 else console.error(message)
41 addHook (level: LoggerLevel, hook: LoggerHook) {
42 this.hooks.push({ level, hook })
45 registerServerSending (serverUrl: string) {
46 this.addHook('warn', (message, meta) => this.sendClientLog(serverUrl, this.buildServerLogPayload('warn', message, meta)))
47 this.addHook('error', (message, meta) => this.sendClientLog(serverUrl, this.buildServerLogPayload('error', message, meta)))
50 sendClientLog (serverUrl: string, payload: ClientLogCreate | null) {
53 const headers = new Headers({
54 Accept: 'application/json',
55 'Content-Type': 'application/json'
59 const tokens = OAuthUserTokens.getUserTokens(peertubeLocalStorage)
61 if (tokens) headers.set('Authorization', `${tokens.tokenType} ${tokens.accessToken}`)
63 console.error('Cannot set tokens to client log sender.', { err })
67 fetch(serverUrl + '/api/v1/server/logs/client', {
70 body: JSON.stringify(payload)
73 console.error('Cannot send client warn/error to server.', err)
77 private buildServerLogPayload (level: Extract<LoggerLevel, 'warn' | 'error'>, message: LoggerMessage, meta?: LoggerMeta) {
78 if (!message) return null
81 message: this.buildMessageServerLogPayload(message),
82 userAgent: navigator.userAgent,
83 url: window.location.href,
85 stackTrace: this.buildStackServerLogPayload(message, meta),
86 meta: this.buildMetaServerLogPayload(meta)
90 private buildMessageServerLogPayload (message: LoggerMessage) {
91 if (typeof message === 'string') return message
92 if (message instanceof Error) return message.message
94 return JSON.stringify(message)
97 private buildStackServerLogPayload (message: LoggerMessage, meta?: LoggerMeta) {
98 if (message instanceof Error) return this.buildStack(message)
99 if (meta instanceof Error) return this.buildStack(meta)
100 if (meta?.err instanceof Error) return this.buildStack(meta.err)
105 private buildMetaServerLogPayload (meta?: LoggerMeta) {
106 if (!meta) return undefined
107 if (meta instanceof Error) return undefined
112 result = JSON.stringify(meta, (key, value) => {
113 if (key === 'err') return undefined
118 console.error('Cannot stringify meta.', err)
124 private runHooks (level: LoggerLevel, message: LoggerMessage, meta?: LoggerMeta) {
125 for (const hookObj of this.hooks) {
126 if (hookObj.level !== level) continue
128 hookObj.hook(message, meta)
132 private buildStack (err: Error) {
133 return `${err.message}\n${err.stack || ''}`
137 const logger = window.logger || new Logger()
138 window.logger = logger