Skip to content

Commit 5942400

Browse files
committed
update request logging
1 parent 6df1607 commit 5942400

File tree

3 files changed

+95
-75
lines changed

3 files changed

+95
-75
lines changed

ui/request-logging.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { unsealData } from 'iron-session';
2+
import { decodeJwt } from 'jose';
3+
4+
// Request logging utilities
5+
export async function extractUserInfoFromRequest(req) {
6+
try {
7+
const cookieName = process.env.WORKOS_COOKIE_NAME || 'wos-session';
8+
const cookiePassword = process.env.WORKOS_COOKIE_PASSWORD;
9+
10+
if (!cookiePassword) {
11+
return { userId: 'anonymous', orgId: 'anonymous' };
12+
}
13+
14+
const cookieHeader = req.headers?.cookie || req.getHeader?.('cookie');
15+
if (!cookieHeader) {
16+
return { userId: 'anonymous', orgId: 'anonymous' };
17+
}
18+
19+
const cookies = cookieHeader.split(';').reduce((acc, cookie) => {
20+
const [key, value] = cookie.trim().split('=');
21+
acc[key] = decodeURIComponent(value);
22+
return acc;
23+
}, {});
24+
25+
const sessionCookie = cookies[cookieName];
26+
if (!sessionCookie) {
27+
return { userId: 'anonymous', orgId: 'anonymous' };
28+
}
29+
30+
const session = await unsealData(sessionCookie, {
31+
password: cookiePassword,
32+
});
33+
34+
if (!session?.user?.id || !session?.accessToken) {
35+
return { userId: 'anonymous', orgId: 'anonymous' };
36+
}
37+
38+
// Decode JWT to get organization ID
39+
let orgId = 'anonymous';
40+
try {
41+
const decoded = decodeJwt(session.accessToken);
42+
orgId = decoded.org_id || 'anonymous';
43+
} catch (error) {
44+
// If JWT decode fails, just use anonymous
45+
}
46+
47+
return { userId: session.user.id, orgId };
48+
} catch (error) {
49+
return { userId: 'anonymous', orgId: 'anonymous' };
50+
}
51+
}
52+
53+
export function logRequestInit(method, path, requestId, userId, orgId) {
54+
console.log(JSON.stringify({
55+
event: 'request_initialized',
56+
method,
57+
path,
58+
requestId,
59+
userId,
60+
orgId,
61+
}));
62+
}
63+
64+
export function logResponse(method, path, requestId, latency, statusCode) {
65+
console.log(JSON.stringify({
66+
event: 'response_sent',
67+
method,
68+
path,
69+
requestId,
70+
latency,
71+
statusCode,
72+
}));
73+
}
74+

ui/server-start.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
66
import { Readable } from 'node:stream';
77
import { createGzip } from 'node:zlib';
88
import serverHandler from './dist/server/server.js';
9+
import { extractUserInfoFromRequest, logRequestInit, logResponse } from './request-logging.js';
910

1011
const __dirname = fileURLToPath(new URL('.', import.meta.url));
1112
const PORT = process.env.PORT || 3030;
@@ -68,6 +69,15 @@ const server = createServer(async (req, res) => {
6869
const requestId = req.headers['x-request-id'] || `ssr-${Math.random().toString(36).slice(2, 10)}`;
6970
const requestStart = Date.now();
7071

72+
// Parse URL early for logging
73+
const url = new URL(req.url, `http://${req.headers.host}`);
74+
const pathname = url.pathname;
75+
const method = req.method;
76+
77+
// Extract user ID and org ID and log request initialization
78+
const { userId, orgId } = await extractUserInfoFromRequest(req);
79+
logRequestInit(method, pathname, requestId, userId, orgId);
80+
7181
// Set request timeout
7282
req.setTimeout(REQUEST_TIMEOUT, () => {
7383
console.error(`⏱️ Request timeout (${REQUEST_TIMEOUT}ms): ${req.method} ${req.url} [${requestId}]`);
@@ -78,8 +88,6 @@ const server = createServer(async (req, res) => {
7888
});
7989

8090
try {
81-
const url = new URL(req.url, `http://${req.headers.host}`);
82-
const pathname = url.pathname;
8391

8492
// Try to serve static files from dist/client first
8593
// Serve: /assets/*, *.js, *.css, *.json, images, fonts, favicons
@@ -99,6 +107,9 @@ const server = createServer(async (req, res) => {
99107
'Cache-Control': 'public, max-age=31536000, immutable',
100108
});
101109
res.end(content);
110+
// Log response for static files
111+
const latency = Date.now() - requestStart;
112+
logResponse(method, pathname, requestId, latency, 200);
102113
return;
103114
} catch (err) {
104115
// File not found, fall through to SSR handler
@@ -221,13 +232,20 @@ const server = createServer(async (req, res) => {
221232
} else {
222233
res.end();
223234
}
235+
236+
// Log response after sending
237+
const latency = Date.now() - requestStart;
238+
logResponse(method, pathname, requestId, latency, res.statusCode);
224239
} catch (error) {
225240
console.error(`Server error [${requestId}]:`, error);
226241
if (!res.headersSent) {
227242
res.statusCode = 500;
228243
res.setHeader('Content-Type', 'text/plain');
229244
res.end('Internal Server Error');
230245
}
246+
// Log error response
247+
const latency = Date.now() - requestStart;
248+
logResponse(method, pathname, requestId, latency, res.statusCode || 500);
231249
}
232250
});
233251

ui/vite.config.ts

Lines changed: 1 addition & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,9 @@ import { defineConfig, loadEnv } from 'vite';
22
import tsConfigPaths from 'vite-tsconfig-paths';
33
import { tanstackStart } from '@tanstack/react-start/plugin/vite';
44
import viteReact from '@vitejs/plugin-react';
5-
import { unsealData } from 'iron-session';
6-
import { decodeJwt } from 'jose';
5+
import { extractUserInfoFromRequest, logRequestInit, logResponse } from './request-logging.js';
76
import type { Plugin } from 'vite';
87

9-
// Request logging utilities
10-
async function extractUserInfoFromRequest(req: any): Promise<{ userId: string; orgId: string }> {
11-
try {
12-
const cookieName = process.env.WORKOS_COOKIE_NAME || 'wos-session';
13-
const cookiePassword = process.env.WORKOS_COOKIE_PASSWORD;
14-
15-
if (!cookiePassword) {
16-
return { userId: 'anonymous', orgId: 'anonymous' };
17-
}
18-
19-
const cookieHeader = req.headers?.cookie || req.getHeader?.('cookie');
20-
if (!cookieHeader) {
21-
return { userId: 'anonymous', orgId: 'anonymous' };
22-
}
23-
24-
const cookies = cookieHeader.split(';').reduce((acc: Record<string, string>, cookie: string) => {
25-
const [key, value] = cookie.trim().split('=');
26-
acc[key] = decodeURIComponent(value);
27-
return acc;
28-
}, {});
29-
30-
const sessionCookie = cookies[cookieName];
31-
if (!sessionCookie) {
32-
return { userId: 'anonymous', orgId: 'anonymous' };
33-
}
34-
35-
const session = await unsealData(sessionCookie, {
36-
password: cookiePassword,
37-
}) as { user?: { id?: string }; accessToken?: string };
38-
39-
if (!session?.user?.id || !session?.accessToken) {
40-
return { userId: 'anonymous', orgId: 'anonymous' };
41-
}
42-
43-
// Decode JWT to get organization ID
44-
let orgId = 'anonymous';
45-
try {
46-
const decoded = decodeJwt<{ org_id?: string }>(session.accessToken);
47-
orgId = decoded.org_id || 'anonymous';
48-
} catch (error) {
49-
// If JWT decode fails, just use anonymous
50-
}
51-
52-
return { userId: session.user.id, orgId };
53-
} catch (error) {
54-
return { userId: 'anonymous', orgId: 'anonymous' };
55-
}
56-
}
57-
58-
function logRequestInit(method: string, path: string, requestId: string, userId: string, orgId: string) {
59-
console.log(JSON.stringify({
60-
event: 'request_initialized',
61-
method,
62-
path,
63-
requestId,
64-
userId,
65-
orgId,
66-
}));
67-
}
68-
69-
function logResponse(method: string, path: string, requestId: string, latency: number, statusCode: number) {
70-
console.log(JSON.stringify({
71-
event: 'response_sent',
72-
method,
73-
path,
74-
requestId,
75-
latency,
76-
statusCode,
77-
}));
78-
}
79-
808
// Logging middleware plugin
819
function requestLoggingPlugin(): Plugin {
8210
return {

0 commit comments

Comments
 (0)