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