Repository navigation
Expand file tree
/
Copy pathlogger.ts
More file actions
153 lines (130 loc) · 5.13 KB
/
Copy pathlogger.ts
File metadata and controls
153 lines (130 loc) · 5.13 KB
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import log from 'loglevel';
export enum LogLevel {
trace = 0,
debug = 1,
info = 2,
warn = 3,
error = 4,
silent = 5,
}
export enum LoggerNames {
Default = 'livekit',
Room = 'livekit-room',
TokenSource = 'livekit-token-source',
Participant = 'livekit-participant',
Track = 'livekit-track',
Publication = 'livekit-track-publication',
Engine = 'livekit-engine',
Signal = 'livekit-signal',
PCManager = 'livekit-pc-manager',
PCTransport = 'livekit-pc-transport',
E2EE = 'lk-e2ee',
DataTracks = 'livekit-data-tracks',
Region = 'livekit-region',
ICE = 'livekit-ice',
Stats = 'livekit-stats',
}
type LogLevelString = keyof typeof LogLevel;
export type StructuredLogger = log.Logger & {
trace: (msg: string, context?: object) => void;
debug: (msg: string, context?: object) => void;
info: (msg: string, context?: object) => void;
warn: (msg: string, context?: object) => void;
error: (msg: string, context?: object) => void;
setDefaultLevel: (level: log.LogLevelDesc) => void;
setLevel: (level: log.LogLevelDesc) => void;
getLevel: () => number;
};
export type ContextProvider = () => object | undefined;
let livekitLogger = log.getLogger(LoggerNames.Default);
const livekitLoggers = Object.values(LoggerNames).map((name) => log.getLogger(name));
livekitLogger.setDefaultLevel(LogLevel.info);
export default livekitLogger as StructuredLogger;
/**
* Get a named logger. When `ctxFn` is supplied, every log call
* automatically:
* 1. prepends a `[key=value ...]` prefix derived from `ctxFn()` to the
* message string, so identifiers are visible in browser devtools
* without expanding the structured context object, and
* 2. merges `ctxFn()` into the structured context passed to any
* `setLogExtension` consumer, so ingestion pipelines continue to
* receive the full metadata unchanged.
*/
export function getLogger(name: string, ctxFn?: ContextProvider) {
const logger = log.getLogger(name);
logger.setDefaultLevel(livekitLogger.getLevel());
if (!ctxFn) {
return logger as StructuredLogger;
}
return wrapWithContext(logger as StructuredLogger, ctxFn);
}
function wrapWithContext(base: StructuredLogger, ctxFn: ContextProvider): StructuredLogger {
type LogMethod = 'trace' | 'debug' | 'info' | 'warn' | 'error';
// Resolve the underlying method on every call so that later
// setLogExtension installations (which replace the base logger's
// methods via loglevel's methodFactory) are picked up.
const wrap = (method: LogMethod) => (msg: string, extra?: object) => {
const ctx = ctxFn();
const merged = ctx || extra ? { ...ctx, ...extra } : undefined;
base[method](msg, merged);
};
const proxy = Object.create(base) as StructuredLogger;
proxy.trace = wrap('trace');
proxy.debug = wrap('debug');
proxy.info = wrap('info');
proxy.warn = wrap('warn');
proxy.error = wrap('error');
return proxy;
}
export function setLogLevel(level: LogLevel | LogLevelString, loggerName?: LoggerNames) {
if (loggerName) {
log.getLogger(loggerName).setLevel(level);
} else {
for (const logger of livekitLoggers) {
logger.setLevel(level);
}
}
}
export type LogExtension = (level: LogLevel, msg: string, context?: object) => void;
/**
* use this to hook into the logging function to allow sending internal livekit logs to third party services
* if set, the browser logs will lose their stacktrace information (see https://github.com/pimterry/loglevel#writing-plugins)
*/
export function setLogExtension(extension: LogExtension, logger?: StructuredLogger) {
const loggers = logger ? [logger] : livekitLoggers;
loggers.forEach((logR) => {
const originalFactory = logR.methodFactory;
logR.methodFactory = (methodName, configLevel, loggerName) => {
const rawMethod = originalFactory(methodName, configLevel, loggerName);
const logLevel = LogLevel[methodName as LogLevelString];
const needLog = logLevel >= configLevel && logLevel < LogLevel.silent;
return (msg, context?: [msg: string, context: object]) => {
if (context) rawMethod(msg, context);
else rawMethod(msg);
if (needLog) {
extension(logLevel, msg, context);
}
};
};
logR.setLevel(logR.getLevel());
});
}
export const workerLogger = log.getLogger(LoggerNames.E2EE) as StructuredLogger;
const workerLogLevelListeners = new Set<(level: LogLevel) => void>();
const originalWorkerSetLevel = workerLogger.setLevel.bind(workerLogger);
workerLogger.setLevel = ((level: log.LogLevelDesc, persist?: boolean) => {
originalWorkerSetLevel(level, persist);
const numeric = workerLogger.getLevel() as LogLevel;
workerLogLevelListeners.forEach((cb) => cb(numeric));
}) as typeof workerLogger.setLevel;
/** @internal Subscribe to workerLogger level changes (so E2EE workers can be kept in sync). */
export function onWorkerLogLevelChanged(cb: (level: LogLevel) => void): () => void {
workerLogLevelListeners.add(cb);
return () => {
workerLogLevelListeners.delete(cb);
};
}
/** @internal Test-only accessor: current number of workerLogger level listeners. */
export function getWorkerLogLevelListenerCount(): number {
return workerLogLevelListeners.size;
}