Skip to content

Adding pretty logs and name based stack webhook#8

Closed
Chanceium wants to merge 9 commits into
aklinker1:mainfrom
Chanceium:main
Closed

Adding pretty logs and name based stack webhook#8
Chanceium wants to merge 9 commits into
aklinker1:mainfrom
Chanceium:main

Conversation

@Chanceium
Copy link
Copy Markdown
Contributor

added logging handler

@Chanceium Chanceium changed the title chore(logging): pretty logs Adding pretty logs and name based stack webhook Dec 17, 2025
… credentials

chore: format code for improved readability in env.ts
@aklinker1
Copy link
Copy Markdown
Owner

aklinker1 commented Jan 31, 2026

Hey, thanks for the PR @Chanceium. There's a lot more than just logging here, could we split it up into multiple PRs?

  • Add the JSON logger
  • Add additional logs
  • New endpoint to update stacks by name
  • Process error handling
  • Portainer access token support

As it stands now, the way you've implemented logging is very hacky... Not surprising since Zeta is my own personal library lol. I have added a docs site since this PR was created, though I'm not sure how useful it is... It doesn't include real examples for accomplishing common tasks. So I'm not criticizing you for how it was implemented, it's my fault lol.

So here's some suggestions:

  • For logging, create a plugin:

    // plugins/request-logger-plugin.ts   
    export const requestLoggerPlugin = createApp()
      .onGlobalRequest(({ method, path, url, request }) => {
        const startTime = performance.now();
        const requestId = crypto.randomUUID();
        const xff =
          request.headers.get("x-forwarded-for") ||
          request.headers.get("x-forwarded") ||
          request.headers.get("forwarded-for");
        const clientId = xff?.split(",")[0]?.trim();
        // ...
        logger.info("request.start", {
          requestId,
          method,
          url: String(url),
          path,
          xff,
          clientId,
        });
    
        // Return values so they're available in subsequent hooks
        return {
          startTime,
          requestId,
        };
      })
      .onTransform(({ requestId, route }) => {
        logger.info("request.routeMatched", { requestId, route });
      })
      .onGlobalAfterResponse(({ startTime, requestId, response }) => {
        const duration = performance.now() - startTime;
        logger.info("request.end", {
          requestId,
          duration,
          status: response.status,
        });
      })
      .onGlobalError(({ error, requestId }) => {
        logger.error("request.error", { requestId, error: String(error) })
      })
      .export();

    Then just use the plugin on the main app:

    // src/app.ts
    export const app = createApp({
      // ...
    })
    + .use(requestLoggerPlugin)
    - .onGlobalError(({ error }) => console.error(error))
      .use(healthApp)
      .use(stacksApp)
      .use(webhooksApp);

    No need for a wrapper function, and this way everything stays type-safe and is present for all requests to the server automatically. Additionally, onGlobalAfterResponse also runs when an error was thrown, so no need to include extra data there. You can mess around with what data gets logged at each step, but I like to just include a request ID for subsequent logs, not all the metadata over again.

  • We should probably strip ansi escape characters out of the message when using the json logger.

     const logJson = (level: string, message: string, meta?: Meta) => {
       const entry: Record<string, unknown> = {
         time: new Date().toISOString(),
         service,
         level,
    -    message,
    +    message: Bun.stripANSI(message),
       };
       if (meta) Object.assign(entry, meta);
       console.log(JSON.stringify(entry));
     };
  • No breaking changes - keep POST /api/stack/{id} endpoint as-is, don't add the additional "/id" or change the operation ID. I don't want to have to change all my CI workflows.

    Not sure what the best path is is for the new endpoint, POST /api/stacks/name/{name} is probably fine.

That's it for high-level recommendations. I'll share more targeted comments if you split this PR up into multiple smaller ones.

@Chanceium
Copy link
Copy Markdown
Contributor Author

@aklinker1 Yeah i made the PR and then just kept pushing changes to the repo how I wanted my instance setup lol

@Chanceium Chanceium closed this by deleting the head repository May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants