Skip to content

Logger ​

Ts.ED has its own logger available through @tsed/logger package.

Installation ​

sh
npm install --save @tsed/logger
sh
yarn add @tsed/logger
sh
pnpm add @tsed/logger
sh
bun add @tsed/logger

Features ​

Ts.ED logger supports many features, and is optimized to be used in production:

  • ContextLogger buffers the request logs and writes them when the response is sent to your consumer. See request logger section below.
  • Layouts support,
  • Appenders support;

Configuration ​

Logger can be configured through the Configuration decorator:

PropsDescription
logger.levelChange the default log level displayed in the terminal. Values: debug, info, warn, error or off. By default: info (off when NODE_ENV is test).
logger.logRequestLog all incoming requests. Requires @tsed/platform-log-request (see request logger). By default, it's true.
logger.alterLogA function (level, obj, ctx) => obj called to alter each object logged through ctx.logger. Requires @tsed/platform-log-request. By default: defaultAlterLog.
logger.onLogResponseA function (ctx) => void called when the response is sent, to log the end of the request. Requires @tsed/platform-log-request. By default: defaultLogResponse.
logger.requestFieldsLegacy. Only read by the deprecated @tsed/platform-log-middleware package. It has no effect with @tsed/platform-log-request; use logger.alterLog instead.
logger.reqIdBuilderA function called for each incoming request to create a request id. By default, the x-request-id header is used, otherwise a uuid v4 is generated.
logger.jsonIndentationThe number of space characters to use as white space in JSON output. The value is always computed by Ts.ED from NODE_ENV: 2 (0 in production). A value given in the configuration is overwritten.
logger.disableRoutesSummaryDisable routes table displayed in the logger.
logger.formatSpecify log format. Example: %[%d{[yyyy-MM-dd hh:mm:ss,SSS}] %p%] %m. Requires the @tsed/logger-pattern-layout package to be installed and imported, otherwise the logger falls back to the colored layout. See @tsed/logger configuration.
logger.ignoreUrlPatterns(String or RegExp) List of patterns to ignore logged request according to the request.url.

WARNING

It's recommended to disable logRequest in production. Logger has a cost on the performance.

Layouts and Appenders ​

Layouts ​

You can configure a layout to format the log output. The following layouts are available:

NameDescription
Basic layoutBasic layout will output the timestamp, level, category, followed by the formatted log event data.
Colored layoutThis layout is the same as basic, except that the timestamp, level and category will be colored according to the log event's level
Dummy layoutThis layout only outputs the first value in the log event's data.
Message layoutUse a simple message format to display log
Json layoutDisplay log to JSON format
Pattern layoutUse custom pattern to format log
Custom layoutlogging to stdout or stderr with a custom layout.

Appenders ​

You can configure an appender to send log events to a destination. The following appenders are available:

NameDescription
Connectallows connecting Ts.ED logger with another logger.
Consolelog to the console.
Filelog to a file.
File datelog to a file with configurable log rolling based on file size or date.
Stdoutlog to stdout.
Stderrlog to stderr.
Insightlog to Insight.
LogEntrieslog to LogEntries.
LogStash HTTPlog to LogStash.
LogStash UDPlog to LogStash.
Logglylog to Loggly.
RabbitMQlog to RabbitMQ.
Seqlog to Seq.
Slacklog to Slack.
Smtplog to SMTP.

TIP

You can create your own layout/appender:

Use Json Layout in production ​

You add this code to switch the logger to Json layout in production mode:

typescript
import {Env} from "@tsed/core";
import {Configuration} from "@tsed/di";
import {$log} from "@tsed/logger";
import "@tsed/logger/layouts/JsonLayout.js"; // add this line since @tsed/logger v8
import "@tsed/platform-express";

export const isProduction = process.env.NODE_ENV === Env.PROD;

if (isProduction) {
  $log.appenders.set("stdout", {
    type: "stdout",
    levels: ["info", "debug"],
    layout: {
      type: "json"
    }
  });
  $log.appenders.set("stderr", {
    levels: ["trace", "fatal", "error", "warn"],
    type: "stderr",
    layout: {
      type: "json"
    }
  });
}

@Configuration({
  logger: {
    disableRoutesSummary: isProduction // remove table with routes summary
  }
})
export class Server {}

This configuration will display the log as following:

bash
{"startTime":"2017-06-05T22:23:08.479Z","categoryName":"json-test","data":["this is just a test"],"level":"INFO","context":{}}

It's more useful if you planned to parse the log with LogStash or any log tool parser.

Inject logger ​

Logger can be injected in any injectable provider as follows:

typescript
import {Logger} from "@tsed/logger";
import {Injectable, Inject} from "@tsed/di";

@Injectable()
export class MyService {
  @Inject()
  logger: Logger;

  $onInit() {
    this.logger.info("Hello world");
  }
}

TIP

Prefer the ContextLogger usage if you want to attach your log the current request. See the next section.

Request logger ​

For each Request, a logger will be attached to the PlatformContext and can be used like here:

typescript
import {Controller, Inject} from "@tsed/di";
import type {PlatformContext} from "@tsed/platform-http";
import {Context} from "@tsed/platform-params";
import {Get} from "@tsed/schema";
import {MyService} from "../services/MyService.js";

@Controller("/")
class MyController {
  @Inject()
  myService: MyService;

  @Get("/")
  get(@Context() ctx: PlatformContext) {
    ctx.logger.info({customData: "test"}); // parameter is optional
    ctx.logger.debug({customData: "test"});
    ctx.logger.warn({customData: "test"});
    ctx.logger.error({customData: "test"});
    ctx.logger.trace({customData: "test"});

    // forward ctx object to the service and use logger inside.
    // All logs are attached to the same request
    this.myService.doSomething("test", ctx);
  }
}
typescript
import {Injectable} from "@tsed/di";
import type {PlatformContext} from "@tsed/platform-http";

@Injectable()
export class MyService {
  doSomething(input: string, ctx: PlatformContext) {
    ctx.logger.info({event: "test", input});
  }
}

TIP

All log use through ctx.logger will be associated with the uniq request id generated by Ts.ED.

TIP

ContextLogger buffers the request logs and writes them when the response is sent to your consumer. The buffer is also flushed as soon as it exceeds logger.maxStackSize entries (30 by default) or when an error or fatal log is emitted.

By default, a log emitted through ctx.logger contains reqId, time, duration and your own data. To add the request information and to log the end of each request, install @tsed/platform-log-request and import it in your server:

typescript
import {Configuration} from "@tsed/di";
import "@tsed/platform-log-request";

@Configuration({
  logger: {
    logRequest: true // default value
  }
})
export class Server {}

With this module, a call with one of these methods will generate a log with the method, url and route of the request on the info level:

bash
[2017-09-01 11:12:46.994] [INFO ] [TSED] - {
  "method": "GET",
  "url": "/api-doc/swagger.json",
  "route": "/api-doc/swagger.json",
  "reqId": "e6ebb0ec5b6d4d2a9d3b3d3a0a6f1b2c",
  "time": "2017-09-01T11:12:46.994Z",
  "duration": 92,
  "customData": "test"
}

On the other levels (debug, warn, error, etc.), headers, body, query and params are also added.

When the response is sent, the module logs a request.end event with the status, status_code and state (OK or KO) fields. This event is logged on the error level, with the error details (error_name, error_message, error_stack, etc.), when the response status is greater than or equal to 400, otherwise on the info level.

WARNING

The logger.requestFields option and the PlatformLogMiddleware class come from the deprecated @tsed/platform-log-middleware package. They have no effect with @tsed/platform-log-request: use the alterLog and onLogResponse options instead.

You can change the logged fields by giving your own alterLog function. It receives the log level, the object to log and the current context. It's called for each log emitted through ctx.logger:

typescript
import {Configuration, type DIContext} from "@tsed/di";
import "@tsed/platform-log-request";

function alterLog(level: string, obj: Record<string, unknown>, ctx: DIContext) {
  const {request} = ctx;

  // NOTE: request => PlatformRequest. To get Express.Request use ctx.getRequest<Express.Request>();
  return {
    method: request.method,
    url: request.url,
    headers: request.headers,
    body: request.body,
    query: request.query,
    params: request.params,
    ...obj
  };
}

@Configuration({
  logger: {
    alterLog
  }
})
export class Server {}

Another example to redact some fields, based on the default implementation:

typescript
import {Configuration, type DIContext} from "@tsed/di";
import {defaultAlterLog} from "@tsed/platform-log-request";

const attributesToHide = ["password", "client_secret"];

function redactAttributes(body: any): any {
  if (body && typeof body === "object") {
    body = {...body};

    for (const attribute of attributesToHide) {
      if (body[attribute]) {
        body[attribute] = "[REDACTED]";
      }
    }
  }

  return body;
}

function alterLog(level: string, obj: Record<string, unknown>, ctx: DIContext) {
  const log: Record<string, unknown> = defaultAlterLog(level, obj, ctx);

  if ("body" in log) {
    log.body = redactAttributes(log.body);
  }

  return log;
}

@Configuration({
  logger: {
    alterLog
  }
})
export class Server {}

The log emitted when the response is sent can be replaced with the onLogResponse option:

typescript
import {Configuration, type DIContext} from "@tsed/di";
import "@tsed/platform-log-request";

function onLogResponse(ctx: DIContext) {
  ctx.logger.info({
    event: "request.end",
    status: ctx.response.statusCode
  });
}

@Configuration({
  logger: {
    onLogResponse
  }
})
export class Server {}

Shutdown logger ​

Shutdown returns a Promise that will be resolved when @tsed/logger has closed all appenders and finished writing log events. Use this when your program exits to make sure all your logs are written to files, sockets are closed, etc.

typescript
import {$log} from "@tsed/logger";

$log.shutdown().then(() => {
  console.log("Complete");
});

Released under the MIT License.