Introduce
In Backend applications centralized logger handling and exception handling are important for error handling and troubleshooting. NestJS has provided some modules, we can implement arbitrary according to the logic we want.
Logger
NestJS has provided a set of text-base loggers, which are used during bootstraping, in other cases such as displaying exceptions.
Basic
In some cases and depending on the project we use some other log sets like Winston:
You can turn off, or specify the log level right from the bootstraping process of the application
1 2 3 4 5 | <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token keyword">await</span> NestFactory <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> ApplicationModule <span class="token punctuation">,</span> <span class="token punctuation">{</span> logger <span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> app <span class="token punctuation">.</span> <span class="token function">listen</span> <span class="token punctuation">(</span> <span class="token number">3000</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Determine the log level at the time of bootstraping:
1 2 3 4 5 | <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token keyword">await</span> NestFactory <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> ApplicationModule <span class="token punctuation">,</span> <span class="token punctuation">{</span> logger <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token string">'error'</span> <span class="token punctuation">,</span> <span class="token string">'warn'</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> app <span class="token punctuation">.</span> <span class="token function">listen</span> <span class="token punctuation">(</span> <span class="token number">3000</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Log levels are supported: log
, error
, warn
, debug
, verbose
Custom
NestJS provides pre base class LoggerService
and Logger
, we can LoggerService
some log levels.
For example
1 2 3 4 5 6 7 8 9 | <span class="token keyword">import</span> <span class="token punctuation">{</span> Logger <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span> <span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">MyLogger</span> <span class="token keyword">extends</span> <span class="token class-name">Logger</span> <span class="token punctuation">{</span> <span class="token function">error</span> <span class="token punctuation">(</span> message <span class="token punctuation">:</span> string <span class="token punctuation">,</span> trace <span class="token punctuation">:</span> string <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Send stack trace to chatwork, or some others logic</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> message <span class="token punctuation">,</span> trace <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Building log module
DI (Dependency injection)
As you know, NestJS is built according to DDD model, the project will be divided into small modules. I will build a module specializing in handling log problems, from which I can inject into other modules.
Create class LoggerService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <span class="token keyword">import</span> <span class="token punctuation">{</span> Injectable <span class="token punctuation">,</span> Logger <span class="token punctuation">,</span> Scope <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span> @ <span class="token function">Injectable</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> scope <span class="token punctuation">:</span> Scope <span class="token punctuation">.</span> <span class="token constant">TRANSIENT</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">LoggerService</span> <span class="token keyword">extends</span> <span class="token class-name">Logger</span> <span class="token punctuation">{</span> <span class="token function">error</span> <span class="token punctuation">(</span> message <span class="token punctuation">:</span> any <span class="token punctuation">,</span> trace <span class="token operator">?</span> <span class="token punctuation">:</span> string <span class="token punctuation">,</span> context <span class="token operator">?</span> <span class="token punctuation">:</span> string <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TO DO</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> message <span class="token punctuation">,</span> trace <span class="token punctuation">,</span> context <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">warn</span> <span class="token punctuation">(</span> message <span class="token punctuation">:</span> any <span class="token punctuation">,</span> context <span class="token operator">?</span> <span class="token punctuation">:</span> string <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TO DO</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">warn</span> <span class="token punctuation">(</span> message <span class="token punctuation">,</span> context <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">log</span> <span class="token punctuation">(</span> message <span class="token punctuation">:</span> any <span class="token punctuation">,</span> context <span class="token operator">?</span> <span class="token punctuation">:</span> string <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TO DO</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> message <span class="token punctuation">,</span> context <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">debug</span> <span class="token punctuation">(</span> message <span class="token punctuation">:</span> any <span class="token punctuation">,</span> context <span class="token operator">?</span> <span class="token punctuation">:</span> string <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TO DO</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">debug</span> <span class="token punctuation">(</span> message <span class="token punctuation">,</span> context <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">verbose</span> <span class="token punctuation">(</span> message <span class="token punctuation">:</span> any <span class="token punctuation">,</span> context <span class="token operator">?</span> <span class="token punctuation">:</span> string <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TO DO</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">verbose</span> <span class="token punctuation">(</span> message <span class="token punctuation">,</span> context <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Create the logger module
1 2 3 4 5 6 7 8 9 | <span class="token keyword">import</span> <span class="token punctuation">{</span> Module <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> LoggerService <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./logger.service'</span> @ <span class="token function">Module</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> providers <span class="token punctuation">:</span> <span class="token punctuation">[</span> LoggerService <span class="token punctuation">]</span> <span class="token punctuation">,</span> exports <span class="token punctuation">:</span> <span class="token punctuation">[</span> LoggerService <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">LoggerModule</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> |
Application logging
After building the Log Module, to use we just need to inject the module into any context, controller. For example Inject logger to controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">import</span> <span class="token punctuation">{</span> Injectable <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span> <span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> MyLogger <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./my-logger.service'</span> <span class="token punctuation">;</span> @ <span class="token function">Injectable</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">CatsService</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> readonly cats <span class="token punctuation">:</span> Cat <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> <span class="token keyword">private</span> myLogger <span class="token punctuation">:</span> MyLogger <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> myLogger <span class="token punctuation">.</span> <span class="token function">setContext</span> <span class="token punctuation">(</span> <span class="token string">'CatsService'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">findAll</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> Cat <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> myLogger <span class="token punctuation">.</span> <span class="token function">warn</span> <span class="token punctuation">(</span> <span class="token string">'About to return cats!'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> cats <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Exception
After the logger has been built, this time it is the Exception Handling section. Basically, all unhandled exceptions in the code will be concentrated on this layer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <span class="token keyword">import</span> <span class="token punctuation">{</span> ArgumentsHost <span class="token punctuation">,</span> Catch <span class="token punctuation">,</span> ExceptionFilter <span class="token punctuation">,</span> HttpException <span class="token punctuation">,</span> HttpStatus <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> LoggerService <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../logger/logger.service'</span> @ <span class="token function">Catch</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">AllExceptionFilter</span> <span class="token keyword">implements</span> <span class="token class-name">ExceptionFilter</span> <span class="token punctuation">{</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> <span class="token keyword">private</span> logger <span class="token punctuation">:</span> LoggerService <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> exception <span class="token punctuation">:</span> unknown <span class="token punctuation">,</span> host <span class="token punctuation">:</span> ArgumentsHost <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> ctx <span class="token operator">=</span> host <span class="token punctuation">.</span> <span class="token function">switchToHttp</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">const</span> response <span class="token operator">=</span> ctx <span class="token punctuation">.</span> <span class="token function">getResponse</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">const</span> request <span class="token operator">=</span> ctx <span class="token punctuation">.</span> <span class="token function">getRequest</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">let</span> message <span class="token punctuation">:</span> string <span class="token operator">|</span> Object <span class="token operator">=</span> <span class="token string">'Internal Server Error'</span> <span class="token keyword">const</span> status <span class="token operator">=</span> exception <span class="token keyword">instanceof</span> <span class="token class-name">HttpException</span> <span class="token operator">?</span> exception <span class="token punctuation">.</span> <span class="token function">getStatus</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> HttpStatus <span class="token punctuation">.</span> <span class="token constant">INTERNAL_SERVER_ERROR</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> exception <span class="token keyword">instanceof</span> <span class="token class-name">HttpException</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> message <span class="token operator">=</span> exception <span class="token punctuation">.</span> <span class="token function">getResponse</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> exception <span class="token keyword">instanceof</span> <span class="token class-name">Error</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> message <span class="token operator">=</span> exception <span class="token punctuation">.</span> stack <span class="token punctuation">.</span> <span class="token function">toString</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> logger <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> message <span class="token punctuation">)</span> response <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> status <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> statusCode <span class="token punctuation">:</span> status <span class="token punctuation">,</span> occurAt <span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toISOString</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> path <span class="token punctuation">:</span> request <span class="token punctuation">.</span> url <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Here I will distinguish two main exception types, that is Built-in Error and HttpException After catching the exception, will proceed to get error message, status code. Here I have injected Logger service into class AllExceptionFilter , when catching exception other than repsone on client side, I will write to log.
Above, I have briefly summarized the process of building the log and handling the exception exception, details you can refer to repo: https://github.com/hoangtm1601/nest-base