Logger
Ts.ED has its own logger available through @tsed/logger package.
Installation
npm install --save @tsed/loggeryarn add @tsed/loggerpnpm add @tsed/loggerbun add @tsed/loggerFeatures
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:
| Props | Description |
|---|---|
logger.level | Change 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.logRequest | Log all incoming requests. Requires @tsed/platform-log-request (see request logger). By default, it's true. |
logger.alterLog | A function (level, obj, ctx) => obj called to alter each object logged through ctx.logger. Requires @tsed/platform-log-request. By default: defaultAlterLog. |
logger.onLogResponse | A 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.requestFields | Legacy. Only read by the deprecated @tsed/platform-log-middleware package. It has no effect with @tsed/platform-log-request; use logger.alterLog instead. |
logger.reqIdBuilder | A 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.jsonIndentation | The 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.disableRoutesSummary | Disable routes table displayed in the logger. |
logger.format | Specify 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:
| Name | Description |
|---|---|
| Basic layout | Basic layout will output the timestamp, level, category, followed by the formatted log event data. |
| Colored layout | This layout is the same as basic, except that the timestamp, level and category will be colored according to the log event's level |
| Dummy layout | This layout only outputs the first value in the log event's data. |
| Message layout | Use a simple message format to display log |
| Json layout | Display log to JSON format |
| Pattern layout | Use custom pattern to format log |
| Custom layout | logging 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:
| Name | Description |
|---|---|
| Connect | allows connecting Ts.ED logger with another logger. |
| Console | log to the console. |
| File | log to a file. |
| File date | log to a file with configurable log rolling based on file size or date. |
| Stdout | log to stdout. |
| Stderr | log to stderr. |
| Insight | log to Insight. |
| LogEntries | log to LogEntries. |
| LogStash HTTP | log to LogStash. |
| LogStash UDP | log to LogStash. |
| Loggly | log to Loggly. |
| RabbitMQ | log to RabbitMQ. |
| Seq | log to Seq. |
| Slack | log to Slack. |
| Smtp | log to SMTP. |
Use Json Layout in production
You add this code to switch the logger to Json layout in production mode:
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:
{"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:
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:
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);
}
}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:
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:
[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:
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:
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:
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.
import {$log} from "@tsed/logger";
$log.shutdown().then(() => {
console.log("Complete");
});