Open-source full-stack error monitoring for Next.js apps
Flytrap Logs is a full-stack error monitoring tool, that monitors your Server Actions, Route Handlers & React components, alerts you when your system health is degraded, and provides you context from tools and libraries you use such as Prisma, Drizzle, Supabase, Auth.js to help you fix bugs & outages faster. Read more →
Flytrap Logs is under active development. Flytrap Logs is an ongoing effort to build a much more developer friendly error monitoring and debugging experience. Some features are not yet built. Open an issue or join our Discord to suggest features and follow our progress. Check out our Roadmap →
- Monitors Server-rendered pages, API Routes, Server Actions & React components. Learn more →
- Saves context from libraries you use (eg. Prisma, Drizzle, Supabase, Stripe, Auth.js) to make debugging easier. Learn more →
- Type-safe structured canonical logging Learn more →
- Encryption for log data Learn more →
- Install the Flytrap Logs SDK
$ npm install @useflytrap/logs
- Install the Next.js plugin
$ npm install -D @useflytrap/logs-transform
- Add the plugin to
next.config.js
const { nextjs } = require('@useflytrap/logs-transform')
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: nextjs({
// 👈 plugin options here
})
}
module.exports = nextConfig
- Create a logging file (
logging.ts
)
In your logging file, you can codify your logs with TypeScript, enable encryption, configure the log API endpoints and more.
🚨 Remember to export all the functions as shown below
import { PermissionedApiKey } from "@/types"
import { createFlytrapLogger } from "@useflytrap/logs"
export type AdditionalLogContext = {
team_id: string
project_id: string
auth_type: "api_key" | "dashboard"
permissions_used: PermissionedApiKey["permissions"]
build_id: string
key_id: string
}
export const {
addContext,
flushAsync,
flush,
catchUncaughtAction,
catchUncaughtRoute,
response,
nextResponse,
json,
nextJson,
redirect,
nextRedirect,
parseJson,
parseText,
} = createFlytrapLogger<AdditionalLogContext>({
publicKey:
"pk_MIIBI...",
encryption: {
enabled: true,
},
})
Your code-base will now automatically emit log data from Server-rendered pages, API routes, Server Actions & React components.
Try making a request, and you should see requests & server actions get logged in the console. If you want to send them to an API, you can change the flushMethod
to 'api'
in your logging.ts
, and define logsEndpoint
with your API endpoint.
You can manually add context to your log data using the addContext
function like this:
const apiKey = assertApiKey(headers())
addContext({
auth_type: "api_key",
key_id: apiKey.id,
})
The Flytrap Logs code-transform automatically wraps your SSR pages, API routes, Server Actions & React components to capture errors that happen in them. It also automatically gathers useful context that can be used when fixing bugs.
- SSR pages: request headers, params, duration, path, status code, uncaught errors
- API routes: request & response payload, request & response headers, duration, path, status code, uncaught errors
- Server Actions: input data, response object, headers, duration, path, status code, uncaught errors
How SSR pages are transformed
+ import { catchUncaughtPage } from "./lib/logging"
- export default function Home() {}
+ export default catchUncaughtPage(function Home() {}, {
+ path: "/"
+})
- The
catchUncaughtPage
doesn't affect the runtime nature of your code at all, if bubbles any caught errors up, so error boundaries work perfectly. - Only React Server Component pages are transformed.
How API routes are transformed
+ import { catchUncaughtRoute } from "@/lib/logging"
- export function GET() {}
+ export const GET = catchUncaughtRoute(function GET() {}, {
+ path: "/"
+})
- The
catchUncaughtRoute
doesn't affect the runtime nature of your code at all, it bubbles any caught errors up - Only React Server Component pages are transformed.
The worst thing that can happen when trying to fix a bug is not having enough context. Because of this, Flytrap logs automatically adds context from your libraries such as Prisma, Drizzle, Supabase, Auth.js, Stripe & more, so when you have enough context when fixing bugs.
Here's an example to illustrate how context gets added to an Auth.js call on the server side:
import { auth } from "@/lib/auth"
+ import { addContext } from "@/lib/logging"
export async function getCurrentUser() {
const session = await auth()
+ if (session) {
+ addContext({
+ 'auth.js': {
+ user_id: session.user.id,
+ user_email: session.user.email
+ }
+ })
+ }
return session?.user
}
Boom! Now your bug reports will automatically contain the authenticated user.
Here's another example with Supabase:
import { auth } from "@/lib/auth"
+ import { addContext } from "@/lib/logging"
export async function findUserById(userId: string) {
const session = await auth()
const { data: { users }, error } = await supabase.auth.admin.listUsers()
+ if (error) {
+ addContext({
+ 'supabase': { error } // 👈 error context saved automatically
+ })
+ }
+ addContext({
+ 'supabase': {
+ 'supabase.auth.admin.listUsers()': users // 👈 fetched users saved to make bug-fixing easier
+ }
+ })
return users.find(({ id }) => id === userId)
}
Canonical logging is a way of doing structured logging at scale, popularized by Stripe, where each action produces one log line, which contains all the necessary context to fix bugs, associate log lines with users, API keys or organizations, monitor latencies and more.
It's a really good way to handle logs and easy to work with and extend. You can use TypeScript to make your canonical logs fully type-safe.
Here's an example:
import { PermissionedApiKey } from "@/types"
import { createFlytrapLogger } from "@useflytrap/logs"
export type AdditionalLogContext = {
team_id: string
project_id: string
auth_type: "api_key" | "dashboard"
permissions_used: PermissionedApiKey["permissions"]
build_id: string
key_id: string
}
export const {
getContext,
addContext,
flush,
flushAsync,
catchUncaughtAction,
catchUncaughtRoute,
response,
nextResponse,
json,
nextJson,
redirect,
nextRedirect,
parseJson,
parseText,
} = createFlytrapLogger<AdditionalLogContext>({
publicKey:
"pk_MIIBI...",
vercel: {
enabled: true,
},
encryption: {
enabled: true,
},
})
Here we're adding keys such as team_id
, key_id
, build_id
, permissions_used
so we can easily find logs filtered by team, API key and more.
To manually add this context to our canonical log lines during execution of Server Actions or API Routes, we can use the addContext
function.
import { addContext, json } from "@/lib/logging"
import { db } from "@/lib/db"
import { Err, Ok } from "ts-results"
async function assertUserInTeam(teamId: string, userId: string) {
const team = await db.teamMember.count({
where: {
teamId,
userId,
},
})
if (team === 0) {
return Err(
json(
{
message: `You're trying to access resources for team with ID "${teamId}" without having access to it.`,
},
{ status: 401 }
)
)
}
addContext({
team_id: teamId,
})
return Ok(true)
}
To enable encryption for log data, simply add encryption.enabled
to true
in your logging.ts
file, and your public key as the publicKey
key. This uses the public key to encrypt the log data.
By default, keys req
, req_headers
, res
, res_headers
& error
are encrypted. If you want to add other keys to encrypt, simply add them to the encryption.encryptKeys
key as an array:
export const {
// ... omitted for brevity
} = createFlytrapLogger<AdditionalLogContext>({
publicKey:
"pk_MIIBI...",
vercel: {
enabled: true,
},
encryption: {
enabled: true,
encryptKeys: ['api_key', 'user_id', 'user_email']
},
})
If you want automatically set-up dashboards for your Route Handlers, Server Actions that look like this 👇, you can integrate our Logs SDK with the Flytrap Logs Dashboard.
- Sign up on Flytrap
- Create a project, select the "Logs" product during the onboarding
- Create your
logging.ts
file, and add thepublicKey
from your onboarding there. - Set the
flushMethod
in yourlogging.ts
file to'api'
TODO!
- Clone this repository
- Enable Corepack using
corepack enable
(usenpm i -g corepack
for Node.js < 16.10) - Install dependencies using
pnpm install
- Run the tests using
pnpm dev
Made with ❤️ in Helsinki, Finland.
Published under MIT License.