elysia-logger

repository·master·Indexed 42 days ago

https://github.com/bogeychan/elysia-logger

A plugin for Elysia.js that integrates the Pino logging library to provide structured logging and automatic request/error logging. It features support for stream and file logging, custom request property injection via customProps, and a type-safe InferContext for Elysia context typing. Version 0.1.10.

Tokens
4.7K
Snippets
20
Records
24
Agent score
52%

What's inside elysia-logger

  1. Understand `InferContext` behavior (v0.1.0 migration)

    master
    In version 0.1.0, the InferContext type was updated to be a union that contains either a standard Context or an ErrorContext. Note that this type is designed to work within the plugin scope; it will not function correctly if used outside of the plugin's context.
  2. Use `InferContext` to simplify Elysia Context typing (v0.10 migration)

    master

    Since version 0.0.10, you can use the InferContext type to simplify the typing of the Elysia context within customProps, replacing the more verbose ElysiaContextForInstance<InferElysiaInstance<typeof app>> pattern.

    import { type InferContext } from "@bogeychan/elysia-logger";
    
    app.use(
      logger({
        customProps(ctx: InferContext<typeof app>) {
          return {};
        },
      })
    );
  3. Replace obsolete `contextKeyName` with Elysia `derive`

    master

    The contextKeyName option is obsolete. To expose the logger instance under a specific key in the Elysia context, use Elysia's .derive() method instead. This allows you to rename or restructure the context properties manually.

    app
      .use(logger().derive(({ log, ...rest }) => ({ myLogger: log, ...rest })))
      .get("/", (ctx) => {
        ctx.myLogger.info(ctx.request, "Request");
      });
  4. Disable automatic `onResponse` logging (v0.12 migration)

    master

    Starting from version 0.0.12, onResponse logging is enabled by default. If you wish to disable this behavior, you must explicitly set autoLogging: false in the configuration object for both the logger() plugin and the log.into() method.

    import { logger, createPinoLogger } from "@bogeychan/elysia-logger";
    
    app.use(
      logger({
        autoLogging: false,
      })
    );
    
    const log = createPinoLogger();
    
    app.use(
      log.into({
        autoLogging: false,
      })
    );
  5. Handle `onError` logging in `customProps` (v0.1.0 migration)

    master

    Starting from version 0.1.0, onError logging is enabled by default. When implementing the customProps option, you must account for the fact that the context might represent an error state. Use the ctx.isError flag to check if the current log entry is due to an error. If isError is true, you may need to return an empty object or handle properties differently, as certain request properties might be undefined during an error state.

    import { type InferContext } from "@bogeychan/elysia-logger";
    
    app.use(
      logger({
        customProps(ctx: InferContext<typeof app>) {
          if (ctx.isError) {
            // you can pass additional properties like `ctx.myProperty` too
            // but they might be `undefined` depending on error cause
            return {};
          }
    
          return {
            myProperty: ctx.myProperty,
          };
        },
      })
    );
  6. Understand ElysiaLoggerContext

    master

    The ElysiaLoggerContext is a discriminated union used to provide type-safe context for logging. It ensures that the available properties on the context match the state of the request:

    • Success State: When { isError: false }, the context provides the standard Elysia Context.
    • Error State: When { isError: true }, the context provides an ErrorContext, which includes error-specific information from the Elysia error handler.
  7. How automatic logging works in the Elysia plugin

    master

    By default, the logger() and fileLogger() plugins enable automatic logging. The plugin hooks into the Elysia lifecycle:

    1. onRequest: Records the startTime in the context store.
    2. onAfterResponse: Calculates responseTime and logs the request using the configured useLevel (defaults to info).
    3. onError: If an error occurs, it logs the error. Note that if the error code is NOT_FOUND, it uses the standard useLevel instead of error level.

    You can customize this behavior using ElysiaLoggerOptions:

    • autoLogging: Set to false to disable automatic request/error logging.
    • useLevel: The log level used for successful requests (default: 'info').
    • customProps: A function (ctx) => object that allows you to add custom properties to the logger child instance for every request.
  8. Basic usage of @bogeychan/elysia-logger

    master

    Register the logger plugin in your Elysia application. By default, it provides a log object within the Elysia context (ctx.log). You can configure the logging level (e.g., "error", "info").

    import { Elysia } from "elysia";
    import { logger } from "@bogeychan/elysia-logger";
    
    const app = new Elysia()
      .use(
        logger({
          level: "error",
        })
      )
      .get("/", (ctx) => {
        ctx.log.error(ctx, "Context");
        ctx.log.info(ctx.request, "Request"); // noop
    
        return "Hello World";
      })
      .listen(3000);
    
    console.log(`Listening on ${app.server!.url}`);
  9. Include additional request context info via customProps

    master

    Use the customProps option to inject dynamic properties into every log call. The function receives the context (ctx) and should return an object containing the properties to be logged. This is useful for logging request parameters, query strings, or custom decorated properties from your Elysia app. You can use InferContext<typeof app> to get the correct type for the context.

    import { logger, type InferContext } from "@bogeychan/elysia-logger";
    
    const myPlugin = () => new Elysia().decorate("myProperty", 42);
    
    // ...
    
    class MyError extends Error {
      constructor(message: string, public myValue: string) {
        super(message);
      }
    }
    
    app = app.error("myError", MyError).use(myPlugin());
    
    app
      .use(
        logger({
          /**
           * This function will be invoked for each `log`-method called
           * where you can pass additional properties that need to be logged
           */
          customProps(ctx: InferContext<typeof app>) {
            if (ctx.isError && ctx.code === "myError") {
              return {
                myValue: ctx.error.myValue,
              };
            }
    
            return {
              params: ctx.params,
              query: ctx.query,
              myProperty: ctx.myProperty,
            };
          },
        })
      )
      .get("/", (ctx) => {
        ctx.log.info(ctx, "Context");
    
        return "with-context";
      })
      .get("/error", () => {
        throw new MyError("whelp", "yay");
      });
  10. Configure automatic logging with autoLogging

    master

    The plugin provides automatic logging for onAfterResponse and onError events (similar to pino-http).

    • autoLogging: true (default): Enables automatic logging.
    • autoLogging: false: Disables automatic logging.
    • autoLogging: { ignore(ctx) { ... } }: Allows you to provide a predicate function to skip logging for specific requests.
    import { logger } from "@bogeychan/elysia-logger";
    
    app
      .use(
        logger({
          autoLogging: true, // default
          autoLogging: false, // disabled
          autoLogging: {
            ignore(ctx) {
              return true; // ignore logging for requests based on condition
            },
          },
        })
      )
      .get("/", (ctx) => "autoLogging");