forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {nanoid} from 'nanoid/non-secure'
2
3import {logEvent} from '#/lib/statsig/statsig'
4import {add} from '#/logger/logDump'
5import {type MetricEvents} from '#/logger/metrics'
6import {consoleTransport} from '#/logger/transports/console'
7import {sentryTransport} from '#/logger/transports/sentry'
8import {
9 LogContext,
10 LogLevel,
11 type Metadata,
12 type Transport,
13} from '#/logger/types'
14import {enabledLogLevels} from '#/logger/util'
15import {ENV} from '#/env'
16
17export {type MetricEvents as Metrics} from '#/logger/metrics'
18
19const TRANSPORTS: Transport[] = (function configureTransports() {
20 switch (ENV) {
21 case 'production': {
22 return [sentryTransport].filter(Boolean)
23 }
24 case 'test': {
25 return []
26 }
27 default: {
28 return [consoleTransport]
29 }
30 }
31})()
32
33export class Logger {
34 static Level = LogLevel
35 static Context = LogContext
36
37 level: LogLevel
38 context: LogContext | undefined = undefined
39 contextFilter: string = ''
40
41 protected debugContextRegexes: RegExp[] = []
42 protected transports: Transport[] = []
43
44 static create(context?: LogContext) {
45 const logger = new Logger({
46 level: process.env.EXPO_PUBLIC_LOG_LEVEL as LogLevel,
47 context,
48 contextFilter: process.env.EXPO_PUBLIC_LOG_DEBUG || '',
49 })
50 for (const transport of TRANSPORTS) {
51 logger.addTransport(transport)
52 }
53 return logger
54 }
55
56 constructor({
57 level,
58 context,
59 contextFilter,
60 }: {
61 level?: LogLevel
62 context?: LogContext
63 contextFilter?: string
64 } = {}) {
65 this.context = context
66 this.level = level || LogLevel.Info
67 this.contextFilter = contextFilter || ''
68 if (this.contextFilter) {
69 this.level = LogLevel.Debug
70 }
71 this.debugContextRegexes = (this.contextFilter || '')
72 .split(',')
73 .map(filter => {
74 return new RegExp(filter.replace(/[^\w:*-]/, '').replace(/\*/g, '.*'))
75 })
76 }
77
78 debug(message: string, metadata: Metadata = {}) {
79 this.transport({level: LogLevel.Debug, message, metadata})
80 }
81
82 info(message: string, metadata: Metadata = {}) {
83 this.transport({level: LogLevel.Info, message, metadata})
84 }
85
86 log(message: string, metadata: Metadata = {}) {
87 this.transport({level: LogLevel.Log, message, metadata})
88 }
89
90 warn(message: string, metadata: Metadata = {}) {
91 this.transport({level: LogLevel.Warn, message, metadata})
92 }
93
94 error(error: Error | string, metadata: Metadata = {}) {
95 this.transport({level: LogLevel.Error, message: error, metadata})
96 }
97
98 metric<E extends keyof MetricEvents>(
99 event: E & string,
100 metadata: MetricEvents[E],
101 options: {
102 /**
103 * Optionally also send to StatSig
104 */
105 statsig?: boolean
106 } = {statsig: true},
107 ) {
108 logEvent(event, metadata, {
109 lake: !options.statsig,
110 })
111
112 for (const transport of this.transports) {
113 transport(LogLevel.Info, LogContext.Metric, event, metadata, Date.now())
114 }
115 }
116
117 addTransport(transport: Transport) {
118 this.transports.push(transport)
119 return () => {
120 this.transports.splice(this.transports.indexOf(transport), 1)
121 }
122 }
123
124 protected transport({
125 level,
126 message,
127 metadata = {},
128 }: {
129 level: LogLevel
130 message: string | Error
131 metadata: Metadata
132 }) {
133 if (
134 level === LogLevel.Debug &&
135 !!this.contextFilter &&
136 !!this.context &&
137 !this.debugContextRegexes.find(reg => reg.test(this.context!))
138 )
139 return
140
141 const timestamp = Date.now()
142 const meta = metadata || {}
143
144 // send every log to syslog
145 add({
146 id: nanoid(),
147 timestamp,
148 level,
149 context: this.context,
150 message,
151 metadata: meta,
152 })
153
154 if (!enabledLogLevels[this.level].includes(level)) return
155
156 for (const transport of this.transports) {
157 transport(level, this.context, message, meta, timestamp)
158 }
159 }
160}
161
162/**
163 * Default logger instance. See `@/logger/README` for docs.
164 *
165 * Basic usage:
166 *
167 * `logger.debug(message[, metadata])`
168 * `logger.info(message[, metadata])`
169 * `logger.log(message[, metadata])`
170 * `logger.warn(message[, metadata])`
171 * `logger.error(error[, metadata])`
172 */
173export const logger = Logger.create(Logger.Context.Default)