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