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