Skip to content

Commit f334308

Browse files
committed
feat: Add request/response logging and remove config manager
1 parent e66fff5 commit f334308

5 files changed

Lines changed: 113 additions & 42 deletions

File tree

src/lib/config.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/lib/logger.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import consola from "consola"
2+
import fs from "node:fs/promises"
3+
import path from "pathe"
4+
5+
export interface LoggerOptions {
6+
enabled: boolean
7+
filePath?: string
8+
}
9+
10+
export const logger = {
11+
options: {
12+
enabled: false,
13+
filePath: undefined,
14+
} as LoggerOptions,
15+
16+
async initialize(filePath?: string): Promise<void> {
17+
if (!filePath) {
18+
this.options.enabled = false
19+
return
20+
}
21+
22+
try {
23+
// Ensure the directory exists
24+
await fs.mkdir(path.dirname(filePath), { recursive: true })
25+
26+
// Initialize the log file with a header
27+
const timestamp = new Date().toISOString()
28+
await fs.writeFile(
29+
filePath,
30+
`# API Request/Response Log\n# Started: ${timestamp}\n\n`,
31+
{ flag: "w" },
32+
)
33+
34+
this.options.enabled = true
35+
this.options.filePath = filePath
36+
consola.success(`Request logging enabled to: ${filePath}`)
37+
} catch (error) {
38+
consola.error(`Failed to initialize log file`, error)
39+
this.options.enabled = false
40+
}
41+
},
42+
43+
async logRequest(
44+
endpoint: string,
45+
method: string,
46+
payload: unknown,
47+
): Promise<void> {
48+
if (!this.options.enabled || !this.options.filePath) return
49+
50+
const timestamp = new Date().toISOString()
51+
const logEntry = [
52+
`## Request - ${timestamp}`,
53+
`Endpoint: ${endpoint}`,
54+
`Method: ${method}`,
55+
`Payload:`,
56+
`\`\`\`json`,
57+
JSON.stringify(payload, null, 2),
58+
`\`\`\``,
59+
`\n`,
60+
].join("\n")
61+
62+
try {
63+
await fs.appendFile(this.options.filePath, logEntry)
64+
} catch (error) {
65+
consola.error(`Failed to write to log file`, error)
66+
}
67+
},
68+
69+
async logResponse(endpoint: string, response: unknown): Promise<void> {
70+
if (!this.options.enabled || !this.options.filePath) return
71+
72+
const timestamp = new Date().toISOString()
73+
const logEntry = [
74+
`## Response - ${timestamp}`,
75+
`Endpoint: ${endpoint}`,
76+
`Response:`,
77+
`\`\`\`json`,
78+
JSON.stringify(response, null, 2),
79+
`\`\`\``,
80+
`\n`,
81+
].join("\n")
82+
83+
try {
84+
await fs.appendFile(this.options.filePath, logEntry)
85+
} catch (error) {
86+
consola.error(`Failed to write to log file`, error)
87+
}
88+
},
89+
}

src/lib/port.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
import consola from "consola"
22
import { getPort } from "get-port-please"
33

4-
import { configManager } from "./config"
5-
64
export async function initializePort(requestedPort?: number): Promise<number> {
7-
const config = configManager.getConfig()
8-
95
const port = await getPort({
106
name: "copilot-api",
11-
port: requestedPort ?? config.PORT,
12-
portRange: config.PORT_RANGE,
7+
port: requestedPort,
8+
portRange: [4142, 4200],
139
random: false,
1410
})
1511

@@ -19,7 +15,5 @@ export async function initializePort(requestedPort?: number): Promise<number> {
1915
)
2016
}
2117

22-
configManager.setConfig({ PORT: port })
23-
2418
return port
2519
}

src/main.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import consola from "consola"
55
import { serve, type ServerHandler } from "srvx"
66

77
import { initializeApp } from "./lib/initialization"
8+
import { logger } from "./lib/logger"
89
import { initializePort } from "./lib/port"
910
import { server } from "./server"
1011

@@ -22,6 +23,10 @@ const main = defineCommand({
2223
default: false,
2324
description: "Enable verbose logging",
2425
},
26+
logFile: {
27+
type: "string",
28+
description: "File to log request/response details",
29+
},
2530
},
2631
async run({ args }) {
2732
if (args.verbose) {
@@ -33,6 +38,8 @@ const main = defineCommand({
3338

3439
const port = await initializePort(portInt)
3540

41+
await logger.initialize(args.logFile)
42+
3643
await initializeApp()
3744

3845
const serverUrl = `http://localhost:${port}`

src/routes/chat-completions/handler.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,26 @@ import { streamSSE, type SSEMessage } from "hono/streaming"
44

55
import type { ChatCompletionsPayload } from "~/services/copilot/chat-completions/types"
66

7+
import { logger } from "~/lib/logger"
78
import { chatCompletions } from "~/services/copilot/chat-completions/service"
89
import { chatCompletionsStream } from "~/services/copilot/chat-completions/service-streaming"
910

1011
export async function handlerStreaming(c: Context) {
1112
const payload = await c.req.json<ChatCompletionsPayload>()
1213

14+
// Log the request
15+
await logger.logRequest("/chat/completions", "POST", payload)
16+
1317
if (payload.stream) {
1418
const response = await chatCompletionsStream(payload)
1519

20+
// For streaming responses, we'll log the initial response setup
21+
await logger.logResponse("/chat/completions", {
22+
type: "stream",
23+
timestamp: new Date().toISOString(),
24+
model: payload.model,
25+
})
26+
1627
return streamSSE(c, async (stream) => {
1728
for await (const chunk of response) {
1829
await stream.writeSSE(chunk as SSEMessage)
@@ -21,5 +32,9 @@ export async function handlerStreaming(c: Context) {
2132
}
2233

2334
const response = await chatCompletions(payload)
35+
36+
// Log the non-streaming response
37+
await logger.logResponse("/chat/completions", response)
38+
2439
return c.json(response)
2540
}

0 commit comments

Comments
 (0)