1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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<LoggerLevel, 'warn' | 'error'>, 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
}
|