Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions apps/api/src/instrument.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { configureLoggerAsync } from "@init/observability/logger"
import { initializeErrorMonitoring } from "@init/observability/monitoring/server"

void configureLoggerAsync()

initializeErrorMonitoring()
4 changes: 1 addition & 3 deletions apps/api/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { database } from "@init/db/client"
import { kv } from "@init/kv/client"
import { getLogger, LoggerCategory } from "@init/observability/logger"
import { honoLogger } from "@init/observability/logger/integrations"
import { captureException } from "@init/observability/monitoring"
import { Scalar } from "@scalar/hono-api-reference"
Expand All @@ -15,10 +14,9 @@ import v1Routes from "#routes/v1/index.ts"
import workflowRoutes from "#routes/workflows.ts"
import { auth } from "#shared/auth.ts"
import env from "#shared/env.ts"
import { LoggerCategory, logger } from "#shared/logger.ts"
import { factory } from "#shared/utils.ts"

const logger = getLogger()

const app = factory.createApp()

app.use(honoLogger({ category: LoggerCategory.HONO }))
Expand Down
18 changes: 18 additions & 0 deletions apps/api/src/shared/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { buildLogger, LoggerCategory } from "@init/observability/logger"
import { singleton } from "@init/utils/singleton"

export const logger = singleton("logger:api", () =>
buildLogger(
[
LoggerCategory.DEFAULT,
LoggerCategory.EMAIL,
LoggerCategory.LOGTAPE,
LoggerCategory.HONO,
LoggerCategory.DRIZZLE_ORM,
LoggerCategory.INNGEST,
],
{ async: true }
)
)

export { LoggerCategory }
4 changes: 2 additions & 2 deletions apps/api/src/shared/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Database } from "@init/db/client"
import type { KeyValue } from "@init/kv/client"
import type { getLogger } from "@init/observability/logger"
import type { Auth, Session } from "#shared/auth.ts"
import type { logger } from "#shared/logger.ts"

type AppLogger = ReturnType<typeof getLogger>
type AppLogger = typeof logger

export type AppContext = {
Variables: {
Expand Down
3 changes: 0 additions & 3 deletions apps/app/src/client.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { configureLogger } from "@init/observability/logger"
import { StartClient } from "@tanstack/react-start/client"
import { StrictMode } from "react"
import { hydrateRoot } from "react-dom/client"

configureLogger({ isDevelopment: import.meta.env.DEV })

hydrateRoot(
document,
<StrictMode>
Expand Down
8 changes: 0 additions & 8 deletions apps/app/src/routes/_unauthenticated/sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,9 @@ import {
SignInWithGitHubButton,
SignInWithGoogleButton,
} from "#features/auth/components/sign-in-with-social-buttons.tsx"
import { authClient } from "#shared/auth.ts"

export const Route = createFileRoute("/_unauthenticated/sign-in")({
component: RouteComponent,
loader: async () => {
const session = await authClient.getSession()

return {
session,
}
},
})

function RouteComponent() {
Expand Down
3 changes: 0 additions & 3 deletions apps/app/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { configureLoggerAsync } from "@init/observability/logger"
import { createStartHandler, defaultStreamHandler } from "@tanstack/react-start/server"

void configureLoggerAsync()

export default {
fetch: createStartHandler(defaultStreamHandler),
}
10 changes: 8 additions & 2 deletions apps/app/src/shared/logger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import { getLogger } from "@init/observability/logger"
import { buildLogger, LoggerCategory } from "@init/observability/logger"
import { singleton } from "@init/utils/singleton"

export const logger = getLogger()
export const logger = singleton("logger:app", () =>
buildLogger([LoggerCategory.DEFAULT], {
async: globalThis.window === undefined,
isDevelopment: import.meta.env.DEV,
})
)
3 changes: 0 additions & 3 deletions apps/desktop/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { QueryClient } from "@tanstack/react-query"
import { configureLogger } from "@init/observability/logger"
import { createHashHistory, createRouter, RouterProvider } from "@tanstack/react-router"
import { StrictMode } from "react"
import ReactDOM from "react-dom/client"
Expand All @@ -10,8 +9,6 @@ import { queryClient } from "#shared/query-client.ts"

import "@init/ui/globals.css"

configureLogger({ isDevelopment: import.meta.env.DEV })

export type RouterContext = {
queryClient: QueryClient
logger: typeof logger
Expand Down
9 changes: 7 additions & 2 deletions apps/desktop/src/shared/logger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { getLogger } from "@init/observability/logger"
import { buildLogger, LoggerCategory } from "@init/observability/logger"
import { singleton } from "@init/utils/singleton"

export const logger = getLogger()
export const logger = singleton("logger:desktop", () =>
buildLogger([LoggerCategory.DEFAULT], {
isDevelopment: import.meta.env.DEV,
})
)
3 changes: 0 additions & 3 deletions apps/extension/src/entrypoints/background.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { configureLogger } from "@init/observability/logger"
import { browser } from "wxt/browser"
import { defineBackground } from "wxt/utils/define-background"
import { logger } from "#shared/logger.ts"

configureLogger({ isDevelopment: import.meta.env.DEV })

export default defineBackground({
main: () => {
logger.with({ id: browser.runtime.id }).info`Hello from the background script!`
Expand Down
9 changes: 7 additions & 2 deletions apps/extension/src/shared/logger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { getLogger } from "@init/observability/logger"
import { buildLogger, LoggerCategory } from "@init/observability/logger"
import { singleton } from "@init/utils/singleton"

export const logger = getLogger()
export const logger = singleton("logger:extension", () =>
buildLogger([LoggerCategory.DEFAULT], {
isDevelopment: import.meta.env.DEV,
})
)
3 changes: 0 additions & 3 deletions apps/mobile/src/instrument.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
import { configureLogger } from "@init/observability/logger"
import { initializeErrorMonitoring } from "@init/observability/monitoring/expo"

configureLogger()
initializeErrorMonitoring()
5 changes: 3 additions & 2 deletions apps/mobile/src/shared/logger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getLogger } from "@init/observability/logger"
import { buildLogger, LoggerCategory } from "@init/observability/logger"
import { singleton } from "@init/utils/singleton"

export const logger = getLogger()
export const logger = singleton("logger:mobile", () => buildLogger([LoggerCategory.DEFAULT]))
4 changes: 3 additions & 1 deletion packages/email/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ReactNode } from "react"
import { resend } from "@init/env/presets"
import { SendEmailError, BatchSendEmailError } from "@init/error"
import { logger } from "@init/observability/logger"
import { getLogger, LoggerCategory } from "@init/observability/logger"
import { type DurationInput, milliseconds } from "@init/utils/duration"
import { singleton } from "@init/utils/singleton"
import { render } from "@react-email/render"
Expand All @@ -17,6 +17,8 @@ type EmailSendParams = {

export const email = singleton("email", () => new Resend(resend().RESEND_API_KEY))

const logger = getLogger(LoggerCategory.EMAIL)

export async function sendEmail(body: ReactNode, params: EmailSendParams) {
const env = resend()
const { emails, subject, sendAt, from = env.EMAIL_FROM } = params
Expand Down
113 changes: 63 additions & 50 deletions packages/observability/src/logger/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,65 @@ export const LoggerCategory = {
CONVEX: ["convex"],
DEFAULT: ["default"],
DRIZZLE_ORM: ["drizzle-orm"],
EMAIL: ["email"],
HONO: ["hono"],
INNGEST: ["inngest"],
LOGTAPE: ["logtape", "meta"],
SECURITY: ["security"],
} as const satisfies Record<string, string[]>

type LoggerCategoryType = (typeof LoggerCategory)[keyof typeof LoggerCategory]

type LoggerConfigOptions = {
type BuildLoggerOptions = {
async?: boolean
isDevelopment?: boolean
}

function buildConfig(nonBlocking: boolean, options?: LoggerConfigOptions): Config<string, string> {
const LOGGER_CONFIGS = [
{
category: LoggerCategory.LOGTAPE,
lowestLevel: "warning",
sinks: ["meta"],
},
{
category: LoggerCategory.INNGEST,
lowestLevel: "info",
sinks: ["console"],
},
{
category: LoggerCategory.CONVEX,
lowestLevel: "info",
sinks: ["console"],
},
{
category: LoggerCategory.HONO,
lowestLevel: "info",
sinks: ["console"],
},
{
category: LoggerCategory.DRIZZLE_ORM,
lowestLevel: "debug",
sinks: ["console"],
},
{
category: LoggerCategory.EMAIL,
lowestLevel: "info",
sinks: ["console"],
},
{
category: LoggerCategory.DEFAULT,
lowestLevel: "trace",
sinks: ["console"],
},
] as const satisfies Config<string, string>["loggers"]

export function buildLogger(
categories: readonly LoggerCategoryType[],
options?: BuildLoggerOptions
) {
if (categories.length === 0) {
throw new Error("At least one logger category is required")
}

const isDev = options?.isDevelopment ?? isDevelopment
const consoleSink = getConsoleSink({
formatter: isDev
Expand All @@ -40,66 +86,33 @@ function buildConfig(nonBlocking: boolean, options?: LoggerConfigOptions): Confi
timestamp: "time",
})
: jsonLinesFormatter,
nonBlocking,
nonBlocking: options?.async === true,
})

return {
loggers: [
{
category: LoggerCategory.LOGTAPE,
lowestLevel: "warning",
sinks: ["meta"],
},
{
category: LoggerCategory.SECURITY,
lowestLevel: "info",
sinks: ["console"],
},
{
category: LoggerCategory.INNGEST,
lowestLevel: "info",
sinks: ["console"],
},
{
category: LoggerCategory.CONVEX,
lowestLevel: "info",
sinks: ["console"],
},
{
category: LoggerCategory.HONO,
lowestLevel: "info",
sinks: ["console"],
},
{
category: LoggerCategory.DRIZZLE_ORM,
lowestLevel: "debug",
sinks: ["console"],
},
{
category: LoggerCategory.DEFAULT,
lowestLevel: "trace",
sinks: ["console"],
},
],
const configuredCategories = new Set(categories.map((category) => category.join("/")))

const config: Config<string, string> = {
loggers: LOGGER_CONFIGS.filter((logger) => configuredCategories.has(logger.category.join("/"))),
sinks: {
console: redactSink(consoleSink),
meta: consoleSink,
},
}
}

export function configureLogger(options?: LoggerConfigOptions) {
configureSync(buildConfig(false, options))
}
if (options?.async) {
void configure(config)
} else {
configureSync(config)
}

export async function configureLoggerAsync(options?: LoggerConfigOptions) {
await configure(buildConfig(true, options))
const defaultCategoryKey = LoggerCategory.DEFAULT.join("/")
const defaultCategory = categories.find((category) => category.join("/") === defaultCategoryKey)

return getLogger(defaultCategory ?? categories[0])
}

export function getLogger(category: LoggerCategoryType = LoggerCategory.DEFAULT) {
return getLogtapeLogger(category)
}

export const logger = getLogger()

export type Logger = LogtapeLogger
4 changes: 2 additions & 2 deletions packages/workflows/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getLogger } from "@init/observability/logger"
import { getLogger, LoggerCategory } from "@init/observability/logger"
import { singleton } from "@init/utils/singleton"
import { dependencyInjectionMiddleware, Inngest } from "inngest"
import { extendedTracesMiddleware } from "inngest/experimental"
Expand All @@ -14,7 +14,7 @@ export const inngest = singleton(
() =>
new Inngest({
id: "init",
logger: getLogger(["inngest"]),
logger: getLogger(LoggerCategory.INNGEST),
middleware: [
dependencyInjectionMiddleware({
// Add any dependencies here
Expand Down