3.2 KB105 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8export type Level = 'log' | 'info' | 'warn' | 'error';
9
10const tzOptions: Intl.DateTimeFormatOptions & {
11 // This is an ES2021 feature which is not properly reflected in the types but is available. TODO: update target to ES2021 to fix this.
12 // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#fractionalseconddigits
13 fractionalSecondDigits: 1 | 2 | 3;
14} = {
15 timeZoneName: 'short',
16 // Show millisecond resolution for extra timing information in the logs
17 fractionalSecondDigits: 3,
18 // After setting fractionalSecondDigits, it requires setting all fields we want printed to 'numeric'.
19 year: 'numeric',
20 month: 'numeric',
21 day: 'numeric',
22 hour: 'numeric',
23 minute: 'numeric',
24 second: 'numeric',
25};
26
27/** Standardized logging interface for the server.
28 * Format: [YYY-MM-DD HH:MM:SS,MILLISECONDS TIMEZONE] [LEVEL] your message here
29 * Example: [2025-01-14 17:54:55,092 GMT−8] [INFO] Setup analytics
30 */
31export abstract class Logger {
32 abstract write(level: Level, timeStr: string, ...args: Parameters<typeof console.log>): void;
33
34 private writeLog(level: Level, ...args: Parameters<typeof console.info>): void {
35 const timeStr = `[${new Date().toLocaleString('sv', tzOptions)}]`;
36 this.write(level, timeStr, ...args);
37 }
38
39 /**
40 * @deprecated use .info instead
41 * TODO: we should just use info everywhere, I don't know the distinction between log and info,
42 * this was just for compatibility with console.log which isn't particularly important.
43 */
44 log(...args: Parameters<typeof console.info>): void {
45 this.writeLog('log', ...args);
46 }
47
48 info(...args: Parameters<typeof console.info>): void {
49 this.writeLog('info', ...args);
50 }
51
52 warn(...args: Parameters<typeof console.info>): void {
53 this.writeLog('warn', ...args);
54 }
55
56 error(...args: Parameters<typeof console.info>): void {
57 this.writeLog('error', ...args);
58 }
59
60 /** Get all previously logged contents, usually for filing a bug report. */
61 getLogFileContents?(): Promise<string>;
62
63 levelToString(level: Level): string {
64 switch (level) {
65 case 'log':
66 return ' [LOG]';
67 case 'info':
68 return ' [INFO]';
69 case 'warn':
70 return ' [WARN]';
71 case 'error':
72 return '[ERROR]';
73 }
74 }
75}
76
77const GREY = '\x1b[38;5;8m';
78const RED = '\x1b[38;5;9m';
79const YELLOW = '\x1b[38;5;11m';
80const CLEAR = '\x1b[0m';
81
82/**
83 * Logger that prints to stdout via `console`, with ANSI escape coloring for easy reading.
84 * Typically used in dev mode.
85 */
86export class StdoutLogger extends Logger {
87 write(level: Level, timeStr: string, ...args: Parameters<typeof console.log>): void {
88 // eslint-disable-next-line no-console
89 console[level]('%s%s%s%s', GREY, timeStr, this.levelToString(level), CLEAR, ...args);
90 }
91
92 levelToString(level: Level): string {
93 switch (level) {
94 case 'log':
95 return GREY + ' [LOG]';
96 case 'info':
97 return GREY + ' [INFO]';
98 case 'warn':
99 return YELLOW + ' [WARN]';
100 case 'error':
101 return RED + '[ERROR]';
102 }
103 }
104}
105