Skip to content

Commit

Permalink
feat(backend): instrument sentry correctly (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
aldy505 authored Jan 2, 2024
1 parent c3da824 commit cbea705
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 182 deletions.
1 change: 0 additions & 1 deletion packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"dependencies": {
"@logtail/node": "0.4.0",
"@sentry/node": "^7.91.0",
"@sentry/tracing": "^7.91.0",
"@tinyhttp/proxy-addr": "2.1.0",
"dotenv": "16.0.3",
"flourite": "1.2.3",
Expand Down
33 changes: 32 additions & 1 deletion packages/backend/src/handler/core.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,42 @@
import type { Middleware } from "polka";
import { ZodError } from "zod";
import * as Sentry from "@sentry/node";
import { generateImage } from "~/logic/generate-image";
import { logger } from "~/utils";
import { logger, sentryTraceFromHeader } from "~/utils";
import { optionSchema, OptionSchema } from "~/schema/options";

export const coreHandler: Middleware = async (req, res) => {
/* c8 ignore start */
const abortController = new AbortController();
const sentrySpan = Sentry.continueTrace(
{ sentryTrace: sentryTraceFromHeader(req.headers), baggage: req.headers["baggage"] },
(ctx) =>
Sentry.startTransaction(
{
name: `${req.method.toUpperCase()} ${req.path}`,
op: "http.server",
origin: "manual.http.node.tracingHandler",
...ctx,
metadata: {
...ctx.metadata,
request: req,
source: "url"
}
},
{ request: Sentry.extractRequestData(req) }
)
);

req.once("close", () => {
if (req.destroyed) {
abortController.abort("Request closed");
}
sentrySpan.setHttpStatus(res.statusCode);
sentrySpan.finish();
});

Sentry.getCurrentHub().getScope().setSpan(sentrySpan);

await logger.info("Incoming POST request", {
body: req.body ?? "",
headers: {
Expand Down
8 changes: 8 additions & 0 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import console from "node:console";
import polka, { type Middleware } from "polka";
import * as Sentry from "@sentry/node";
import sirv from "sirv";
import helmet from "helmet";
import { cors, bodyParser, errorHandler, notFoundHandler, rateLimiter } from "~/middleware/index.js";
Expand All @@ -13,6 +14,13 @@ const MAX_AGE = 24 * 60; // 1 day
const CWD = dirname(fileURLToPath(import.meta.url));
const STATIC_PATH = resolve(CWD, "./views");

Sentry.init({
dsn: "",
integrations: [new Sentry.Integrations.Http({ tracing: true }), new Sentry.Integrations.Undici()],
sampleRate: 1.0,
tracesSampleRate: 0.5
});

const app = polka({ onError: errorHandler, onNoMatch: notFoundHandler })
.use(
helmet() as Middleware,
Expand Down
118 changes: 61 additions & 57 deletions packages/backend/src/logic/generate-image.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import flourite from "flourite";
import sharp from "sharp";
import * as shikiji from "shikiji";
import * as Sentry from "@sentry/node";
import { SvgRenderer } from "~/logic/svg-renderer";
import type { OptionSchema } from "~/schema/options";
import { FONT_MAPPING } from "shared";

function guessLanguage(code: string, language: string): string {
const guess = language === "auto-detect" ? flourite(code, { shiki: true, heuristic: true }).language : language;
const guessedLanguage = guess === "unknown" ? "md" : guess;
return guessedLanguage;
return Sentry.startSpan({ name: "Guess Language", op: "logic.generate_image.guess_language" }, () => {
const guess = language === "auto-detect" ? flourite(code, { shiki: true, heuristic: true }).language : language;
return guess === "unknown" ? "md" : guess;
});
}

export async function generateImage({
export function generateImage({
code,
language,
border,
Expand All @@ -21,65 +23,67 @@ export async function generateImage({
font,
showLineNumber
}: OptionSchema): Promise<{ image: Buffer; length: number; format: string }> {
const highlighter = await shikiji.getHighlighter({ themes: [theme] });
const resolvedTheme = highlighter.getTheme(theme);
const fontConfig = FONT_MAPPING[font];
return Sentry.startSpan({ name: "Generate Image", op: "logic.generate_image.generate_image" }, async () => {
const highlighter = await shikiji.getHighlighter({ themes: [theme] });
const resolvedTheme = highlighter.getTheme(theme);
const fontConfig = FONT_MAPPING[font];

const svgRenderer = new SvgRenderer({
...fontConfig,
showLineNumber,
lineNumberForeground: resolvedTheme.fg,
background: resolvedTheme.bg,
radius: border.radius
});
const svgRenderer = new SvgRenderer({
...fontConfig,
showLineNumber,
lineNumberForeground: resolvedTheme.fg,
background: resolvedTheme.bg,
radius: border.radius
});

const guessedLanguage = guessLanguage(code, language);
const tokens = await shikiji.codeToThemedTokens(code, {
lang: guessedLanguage as shikiji.BundledLanguage,
theme: theme as shikiji.BundledTheme
});
const { svg } = svgRenderer.renderToSVG(tokens);
const guessedLanguage = guessLanguage(code, language);
const tokens = await shikiji.codeToThemedTokens(code, {
lang: guessedLanguage as shikiji.BundledLanguage,
theme: theme as shikiji.BundledTheme
});
const { svg } = svgRenderer.renderToSVG(tokens);

if (imageFormat === "svg") {
const svgBuffer = Buffer.from(svg);
return {
image: svgBuffer,
format: imageFormat,
length: svgBuffer.byteLength
};
}
if (imageFormat === "svg") {
const svgBuffer = Buffer.from(svg);
return {
image: svgBuffer,
format: imageFormat,
length: svgBuffer.byteLength
};
}

const codeFrame = sharp(Buffer.from(svg), {
density: Math.floor(72 * upscale)
});
const codeFrameMeta = await codeFrame.metadata();
const codeFrame = sharp(Buffer.from(svg), {
density: Math.floor(72 * upscale)
});
const codeFrameMeta = await codeFrame.metadata();

const borderThickness = border.thickness;
const borderColour = border.colour;
const borderThickness = border.thickness;
const borderColour = border.colour;

// Convert the SVG to PNG
const codeImage = await sharp({
create: {
width: codeFrameMeta.width as number,
height: codeFrameMeta.height as number,
channels: 4,
background: borderThickness !== 0 ? borderColour : { r: 0, g: 0, b: 0, alpha: 0 }
}
})
.composite([{ input: await codeFrame.toBuffer() }])
.extend({
left: borderThickness * upscale,
right: borderThickness * upscale,
bottom: borderThickness * upscale,
top: borderThickness * upscale,
background: borderColour
// Convert the SVG to PNG
const codeImage = await sharp({
create: {
width: codeFrameMeta.width as number,
height: codeFrameMeta.height as number,
channels: 4,
background: borderThickness !== 0 ? borderColour : { r: 0, g: 0, b: 0, alpha: 0 }
}
})
[imageFormat]()
.toBuffer();
.composite([{ input: await codeFrame.toBuffer() }])
.extend({
left: borderThickness * upscale,
right: borderThickness * upscale,
bottom: borderThickness * upscale,
top: borderThickness * upscale,
background: borderColour
})
[imageFormat]()
.toBuffer();

return {
image: codeImage,
format: imageFormat,
length: codeImage.byteLength
};
return {
image: codeImage,
format: imageFormat,
length: codeImage.byteLength
};
});
}
Loading

0 comments on commit cbea705

Please sign in to comment.