├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .travis.yml ├── README.md ├── angular.json ├── integration ├── app │ ├── browserslist │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── src │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.ts │ │ └── app.module.ts │ ├── styles.css │ ├── tsconfig.app.json │ └── tsconfig.spec.json └── tests │ ├── autobind.spec.ts │ ├── clipboard.spec.ts │ ├── console-format-api.spec.ts │ ├── console-group-api.spec.ts │ ├── console.spec.ts │ ├── css-parcer.spec.ts │ ├── decorators.spec.ts │ ├── helpers │ ├── console-fake.ts │ ├── custom-colors.enum.ts │ └── test.component.ts │ ├── injector.spec.ts │ ├── json.spec.ts │ ├── logger.module.spec.ts │ ├── setupJest.ts │ └── tsconfig.spec.json ├── jest.config.js ├── lib ├── ng-package.json ├── package.json ├── src │ ├── decorators │ │ ├── autobind.decorator.ts │ │ ├── debug.decorator.ts │ │ ├── error.decorator.ts │ │ ├── groups │ │ │ ├── group-collapsed.decorator.ts │ │ │ ├── group.common.ts │ │ │ └── group.decorator.ts │ │ ├── info.decorator.ts │ │ ├── log.decorator.ts │ │ ├── logger.decorator.ts │ │ ├── timer.decorator.ts │ │ ├── trace.decorator.ts │ │ └── warn.decorator.ts │ ├── interfaces │ │ ├── logger.external.ts │ │ └── logger.internal.ts │ ├── logger.config.ts │ ├── logger.injector.ts │ ├── logger.module.ts │ ├── logger.options.ts │ ├── logger.service.ts │ ├── public-api.ts │ └── services │ │ ├── clipboard-factory.service.ts │ │ ├── console.service.ts │ │ ├── css-factory.service.ts │ │ ├── factory.service.ts │ │ ├── group-factory.service.ts │ │ ├── json-factory.service.ts │ │ └── timer-factory.service.ts └── tsconfig.lib.json ├── ngx-logger.iml ├── package.json ├── renovate.json ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_size = 4 7 | end_of_line = lf 8 | indent_style = space 9 | max_line_length = 120 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@angular-ru/eslint-config" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | *.iml 3 | 4 | # compiled output 5 | /dist 6 | /tmp 7 | /out-tsc 8 | # Only exists if Bazel was run 9 | /bazel-out 10 | /.cache 11 | 12 | # dependencies 13 | node_modules/ 14 | 15 | # profiling files 16 | chrome-profiler-events.json 17 | speed-measure-plugin.json 18 | 19 | # IDEs and editors 20 | /.idea 21 | .project 22 | .classpath 23 | .c9/ 24 | *.launch 25 | .settings/ 26 | *.sublime-workspace 27 | 28 | # IDE - VSCode 29 | .vscode/* 30 | !.vscode/settings.json 31 | !.vscode/tasks.json 32 | !.vscode/launch.json 33 | !.vscode/extensions.json 34 | .history/* 35 | 36 | # misc 37 | /.sass-cache 38 | /connect.lock 39 | /coverage 40 | /libpeerconnection.log 41 | npm-debug.log 42 | yarn-error.log 43 | testem.log 44 | /typings 45 | 46 | # System Files 47 | .DS_Store 48 | Thumbs.db 49 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .idea/** 2 | .cache/** 3 | coverage/** 4 | **/node_modules/** 5 | dist/** 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: node_js 3 | sudo: false 4 | node_js: 5 | - "12.3.1" 6 | install: 7 | - npm install 8 | script: 9 | - npm run lint 10 | - npm test 11 | - npm run build:lib 12 | - npm run build:app 13 | after_success: 14 | - npm run coverage 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Logger 2 | 3 | > Lightweight and configurable Angular logger 4 | 5 | [![Build Status](https://travis-ci.org/Angular-RU/angular-logger.svg?branch=master)](https://travis-ci.org/Angular-RU/angular-logger) 6 | [![npm version](https://badge.fury.io/js/%40angular-ru%2Flogger.svg)](https://badge.fury.io/js/%40angular-ru%2Flogger) 7 | [![Coverage Status](https://coveralls.io/repos/github/Angular-RU/angular-logger/badge.svg?branch=develop)](https://coveralls.io/github/Angular-RU/angular-logger?branch=develop) 8 | [![npm-stat](https://img.shields.io/npm/dt/@angular-ru/logger.svg)](https://npm-stat.com/charts.html?package=@angular-ru/logger&from=2017-01-12) 9 | 10 | ```typescript 11 | import { LoggerModule } from '@angular-ru/logger'; 12 | ... 13 | 14 | @NgModule({ 15 | imports: [ 16 | LoggerModule.forRoot() 17 | ], 18 | ... 19 | }) 20 | export class AppModule {} 21 | ``` 22 | 23 | ## Motivation 24 | 25 | This logger is a handy tool that can be useful in the design and development of the enterprise application level. Easy 26 | setting of logging levels and convenient work with groups. Among other things, you can use meta programming 27 | (decorators). 28 | 29 | ## Table of contents 30 | 31 | - [Logging](#) 32 | - [Basic usage API `trace`, `debug`, `info`, `warn`, `error`](#example-basic-methods) 33 | - [Groups, `groupCollapsed`, `collapsible`](#example-groups) 34 | - [Nested groups (usage pipe method)](#example-nested-groups) 35 | - [Set logging level (worked in single or groups)](#example-set-minimal-logging-level) 36 | - [Customization style line](#example-set-style-line) 37 | - [Customization global style line](#example-set-global-style-line) 38 | - [Add css classes](#example-css-classes) 39 | - [Output pretty json `stringify`](#example-pretty-json) 40 | - [Copy `json, object, text` to clipboard](#example-clipboard) 41 | - [Basic decorators](#example-decorators) 42 | - [Decorator groups](#example-decorator-groups) 43 | - [Decorator groups with function title](#example-decorator-group-with-function-title) 44 | - [Configuration `Angular Logger`](#example-full-configurations) 45 | 46 | * [Todo](#todo) 47 | 48 | ## Logging 49 | 50 | ``` 51 | $ npm install @angular-ru/logger --save 52 | ``` 53 | 54 | ```typescript 55 | import { LoggerModule } from '@angular-ru/logger'; 56 | ... 57 | 58 | @NgModule({ 59 | imports: [ 60 | LoggerModule.forRoot() 61 | ], 62 | ... 63 | }) 64 | export class AppModule {} 65 | ``` 66 | 67 | **Online examples**: https://stackblitz.com/github/Angular-RU/ng-logger 68 | 69 | ![](https://habrastorage.org/webt/lq/a9/_s/lqa9_sp8gxkwax_sy6x9w3qf5ry.gif) 70 | 71 | ### Example: basic methods 72 | 73 | ```typescript 74 | import { LoggerService } from '@angular-ru/logger'; 75 | 76 | export class AppComponent implements OnInit { 77 | constructor(private readonly logger: LoggerService) {} 78 | 79 | public ngOnInit(): void { 80 | this.logger.trace('trace is worked', 1, { a: 1 }); 81 | this.logger.debug('debug is worked', 2, {}); 82 | this.logger.info('info is worked', 3, Object); 83 | this.logger.warn('warn is worked', 4, String); 84 | this.logger.error('error is worked', 5, (2.55).toFixed()); 85 | } 86 | } 87 | ``` 88 | 89 | - **Default level: All** 90 | 91 | ![](https://habrastorage.org/webt/0u/yj/1t/0uyj1tli-mzh0cor1cg4jwphsdk.png) 92 | 93 | - **Disable trace on console (filter):** 94 | 95 | ```typescript 96 | import { LoggerService } from '@angular-ru/logger'; 97 | 98 | export class AppComponent implements OnInit { 99 | private readonly traceIsWork: string = 'trace is worked'; 100 | constructor(private readonly logger: LoggerService) {} 101 | 102 | public ngOnInit(): void { 103 | this.logger.group('Show trace in opened group', ({ trace }: LoggerService): void => { 104 | for (let i: number = 0; i < 20; i++) { 105 | trace(this.traceIsWork, i); 106 | } 107 | }); 108 | } 109 | } 110 | ``` 111 | 112 | ### Example: groups 113 | 114 | - **Logger groups with auto closed (usage callback):** 115 | 116 | ```typescript 117 | import { LoggerService } from '@angular-ru/logger'; 118 | 119 | export class AppComponent implements OnInit { 120 | private readonly traceIsWork: string = 'trace is worked'; 121 | private readonly debugIsWork: string = 'debug is worked'; 122 | private readonly infoIsWork: string = 'info is worked'; 123 | private readonly warnIsWork: string = 'warn is worked'; 124 | private readonly errorIsWork: string = 'error is worked'; 125 | 126 | constructor(private readonly logger: LoggerService) {} 127 | 128 | public ngOnInit(): void { 129 | this.logger.groupCollapsed('EXAMPLE 2: show stack', () => { 130 | this.logger.trace(this.traceIsWork, 1, { a: 1 }); 131 | this.logger.debug(this.debugIsWork, 2, console); 132 | this.logger.info(this.infoIsWork, 3, Object); 133 | this.logger.warn(this.warnIsWork, 4, String); 134 | this.logger.error(this.errorIsWork, 5, (2.55).toFixed()); 135 | }); 136 | 137 | this.logger.group('Show trace in opened group', ({ trace }: LoggerService): void => { 138 | for (let i: number = 0; i < 20; i++) { 139 | trace(this.traceIsWork, i); 140 | } 141 | }); 142 | 143 | this.logger.groupCollapsed('Show trace in collapsed group', ({ debug }: LoggerService): void => { 144 | for (let i: number = 0; i < 15; i++) { 145 | debug(this.traceIsWork, i); 146 | } 147 | }); 148 | } 149 | } 150 | ``` 151 | 152 | ![](https://habrastorage.org/webt/oo/ob/uh/ooobuhwfa3ncpctwirtirgmth6y.png) 153 | 154 | ### Example: nested groups 155 | 156 | - **Logger nested groups (with pipe):** 157 | 158 | ```typescript 159 | import { LoggerService } from '@angular-ru/logger'; 160 | 161 | export class AppComponent implements OnInit { 162 | private readonly traceIsWork: string = 'trace is worked'; 163 | private readonly debugIsWork: string = 'debug is worked'; 164 | private readonly infoIsWork: string = 'info is worked'; 165 | private readonly warnIsWork: string = 'warn is worked'; 166 | private readonly errorIsWork: string = 'error is worked'; 167 | 168 | constructor(private readonly logger: LoggerService) {} 169 | 170 | public ngOnInit(): void { 171 | this.logger 172 | .groupCollapsed('GROUP TEST') 173 | .pipe(({ trace, debug, info, warn, error }: LoggerService) => { 174 | trace(this.traceIsWork); 175 | debug(this.debugIsWork); 176 | info(this.infoIsWork); 177 | warn(this.warnIsWork); 178 | error(this.errorIsWork); 179 | }) 180 | .close(); 181 | 182 | this.logger 183 | .group('A') 184 | .pipe( 185 | ({ trace }: LoggerService) => trace(this.traceIsWork), 186 | ({ debug }: LoggerService) => debug(this.debugIsWork), 187 | ({ info }: LoggerService) => info(this.infoIsWork), 188 | ({ warn }: LoggerService) => warn(this.warnIsWork), 189 | ({ error }: LoggerService) => error(this.errorIsWork) 190 | ) 191 | .groupCollapsed('B') 192 | .pipe( 193 | ({ trace }: LoggerService) => trace(this.traceIsWork), 194 | ({ debug }: LoggerService) => debug(this.debugIsWork), 195 | ({ info }: LoggerService) => info(this.infoIsWork), 196 | ({ warn }: LoggerService) => warn(this.warnIsWork), 197 | ({ error }: LoggerService) => error(this.errorIsWork) 198 | ) 199 | .group('C') 200 | .pipe( 201 | ({ trace }: LoggerService) => trace(this.traceIsWork), 202 | ({ debug }: LoggerService) => debug(this.debugIsWork), 203 | ({ info }: LoggerService) => info(this.infoIsWork), 204 | ({ warn }: LoggerService) => warn(this.warnIsWork), 205 | ({ error }: LoggerService) => error(this.errorIsWork) 206 | ) 207 | .closeAll(); 208 | } 209 | } 210 | ``` 211 | 212 | ![](https://habrastorage.org/webt/_n/wz/8l/_nwz8l25o12tmu0d8sxyyjttmck.gif) 213 | 214 | ### Example: set minimal logging level 215 | 216 | Basic parameterization 217 | 218 | ```typescript 219 | import { LoggerService } from '@angular-ru/logger'; 220 | 221 | export class AppComponent implements OnInit { 222 | private readonly traceIsWork: string = 'trace is worked'; 223 | private readonly debugIsWork: string = 'debug is worked'; 224 | private readonly infoIsWork: string = 'info is worked'; 225 | private readonly warnIsWork: string = 'warn is worked'; 226 | private readonly errorIsWork: string = 'error is worked'; 227 | 228 | constructor(private readonly logger: LoggerService) {} 229 | 230 | public ngOnInit(): void { 231 | this.logger.trace(this.traceIsWork, 1, { a: 1 }); 232 | this.logger.debug(this.debugIsWork, 2, console); 233 | this.logger.info(this.infoIsWork, 3, Object); 234 | this.logger.warn(this.warnIsWork, 4, String); 235 | this.logger.error(this.errorIsWork, 5, (2.55).toFixed()); 236 | 237 | this.logger.level = LoggerLevel.INFO; 238 | this.logger.log('Set new logger level'); 239 | 240 | this.logger.trace(this.traceIsWork, 1, { a: 1 }); 241 | this.logger.debug(this.debugIsWork, 2, console); 242 | this.logger.info(this.infoIsWork, 3, Object); 243 | this.logger.warn(this.warnIsWork, 4, String); 244 | this.logger.error(this.errorIsWork, 5, (2.55).toFixed()); 245 | } 246 | } 247 | ``` 248 | 249 | ![](https://habrastorage.org/webt/0r/ya/xn/0ryaxnmaedlbc14imvodsezq4lg.png) 250 | 251 | - **Logger level groups (pretty usage API):** 252 | 253 | ```typescript 254 | import { LoggerService, LoggerLevel } from '@angular-ru/logger'; 255 | 256 | export class AppComponent implements OnInit { 257 | private readonly traceIsWork: string = 'trace is worked'; 258 | private readonly debugIsWork: string = 'debug is worked'; 259 | private readonly infoIsWork: string = 'info is worked'; 260 | private readonly warnIsWork: string = 'warn is worked'; 261 | private readonly errorIsWork: string = 'error is worked'; 262 | 263 | constructor(private readonly logger: LoggerService) {} 264 | 265 | public ngOnInit(): void { 266 | this.logger.level = LoggerLevel.INFO; 267 | 268 | this.logger.trace 269 | .group('A') 270 | .pipe( 271 | ({ trace }: LoggerService) => trace(this.traceIsWork), 272 | ({ debug }: LoggerService) => debug(this.debugIsWork), 273 | ({ info }: LoggerService) => info(this.infoIsWork), 274 | ({ warn }: LoggerService) => warn(this.warnIsWork), 275 | ({ error }: LoggerService) => error(this.errorIsWork) 276 | ) 277 | .close() 278 | 279 | .debug.group('B') 280 | .pipe( 281 | ({ trace }: LoggerService) => trace(this.traceIsWork), 282 | ({ debug }: LoggerService) => debug(this.debugIsWork), 283 | ({ info }: LoggerService) => info(this.infoIsWork), 284 | ({ warn }: LoggerService) => warn(this.warnIsWork), 285 | ({ error }: LoggerService) => error(this.errorIsWork) 286 | ) 287 | .close() 288 | 289 | .info.group('C') 290 | .pipe( 291 | ({ trace }: LoggerService) => trace(this.traceIsWork), 292 | ({ debug }: LoggerService) => debug(this.debugIsWork), 293 | ({ info }: LoggerService) => info(this.infoIsWork), 294 | ({ warn }: LoggerService) => warn(this.warnIsWork), 295 | ({ error }: LoggerService) => error(this.errorIsWork) 296 | ) 297 | .close() 298 | 299 | .warn.group('D') 300 | .pipe( 301 | ({ trace }: LoggerService) => trace(this.traceIsWork), 302 | ({ debug }: LoggerService) => debug(this.debugIsWork), 303 | ({ info }: LoggerService) => info(this.infoIsWork), 304 | ({ warn }: LoggerService) => warn(this.warnIsWork), 305 | ({ error }: LoggerService) => error(this.errorIsWork) 306 | ) 307 | .close() 308 | 309 | .error.group('E') 310 | .pipe( 311 | ({ trace }: LoggerService) => trace(this.traceIsWork), 312 | ({ debug }: LoggerService) => debug(this.debugIsWork), 313 | ({ info }: LoggerService) => info(this.infoIsWork), 314 | ({ warn }: LoggerService) => warn(this.warnIsWork), 315 | ({ error }: LoggerService) => error(this.errorIsWork) 316 | ) 317 | .close(); 318 | 319 | this.logger.level = LoggerLevel.ALL; 320 | } 321 | } 322 | ``` 323 | 324 | ![](https://habrastorage.org/webt/x-/lm/pl/x-lmplcexk_nd0icuqe6ehslub4.png) 325 | 326 | ### Example: set style line 327 | 328 | ```typescript 329 | import { LoggerService } from '@angular-ru/logger'; 330 | 331 | export class AppComponent implements OnInit { 332 | constructor(private readonly logger: LoggerService) {} 333 | 334 | public ngOnInit(): void { 335 | this.logger.clear(); 336 | 337 | this.logger.css('text-transform: uppercase; font-weight: bold').debug('window current ', window); 338 | this.logger.css('color: red; text-decoration: underline; font-weight: bold').info('It is awesome logger'); 339 | this.logger.debug({ a: 1 }); 340 | 341 | this.logger.warn(setStyle); 342 | this.logger.info('For global configuration, use the constructor parameters'); 343 | } 344 | } 345 | ``` 346 | 347 | ![](https://habrastorage.org/webt/8b/dn/nv/8bdnnvsgj1m2rxnssviqptks874.png) 348 | 349 | ### Example: set global style line 350 | 351 | ```typescript 352 | import { LoggerService } from '@angular-ru/logger'; 353 | 354 | export class AppComponent implements OnInit { 355 | constructor(private readonly logger: LoggerService) {} 356 | 357 | public ngOnInit(): void { 358 | this.logger.clear(); 359 | 360 | this.logger.css('font-weight: normal; text-decoration: none; font-style: italic').info(3.14); 361 | this.logger.css('font-weight: normal;').info(3.14); 362 | this.logger.warn('global format with style!'); 363 | } 364 | } 365 | ``` 366 | 367 | ![](https://habrastorage.org/webt/y4/wm/vz/y4wmvzvsmtzt6zdqjcupxqmvodm.png) 368 | 369 | ### Example: CSS classes 370 | 371 | ```typescript 372 | import { LoggerModule } from '@angular-ru/logger'; 373 | 374 | @NgModule({ 375 | // .. 376 | imports: [ 377 | LoggerModule.forRoot({ 378 | cssClassMap: { 379 | bold: 'font-weight: bold', 380 | 'line-through': 'text-decoration: line-through', 381 | 'code-sandbox': ` 382 | color: #666; 383 | background: #f4f4f4; 384 | border-left: 3px solid #f36d33; 385 | font-family: monospace; 386 | font-size: 15px;` 387 | } 388 | }) 389 | ] 390 | // .. 391 | }) 392 | ``` 393 | 394 | ```typescript 395 | import { LoggerService } from '@angular-ru/logger'; 396 | 397 | export class AppComponent implements OnInit { 398 | constructor(private readonly logger: LoggerService) {} 399 | 400 | public ngOnInit(): void { 401 | this.logger.cssClass('bold line-through').log('JavaScript sucks', 'JavaScript is the best'); 402 | 403 | this.logger 404 | .cssClass('code-sandbox') 405 | .log('\n @Component({ .. })' + '\n export class AppComponent { .. } \n\n'); 406 | 407 | this.logger.cssClass('bold line-through').debug('JavaScript sucks', 'JavaScript is the best'); 408 | } 409 | } 410 | export class AppModule {} 411 | ``` 412 | 413 | ![](https://habrastorage.org/webt/d5/tm/aa/d5tmaaomjql5px_wkzxnodhacnk.png) 414 | 415 | ### Example: pretty json 416 | 417 | ```typescript 418 | import { LoggerService } from '@angular-ru/logger'; 419 | 420 | export class AppComponent implements OnInit { 421 | constructor(private readonly logger: LoggerService) {} 422 | 423 | public ngOnInit(): void { 424 | // default browser print json 425 | this.logger.debug('Classic output json', jsonExample); 426 | 427 | // for pretty json usage logger.log method 428 | this.logger.log(...this.logger.prettyJSON(jsonExample)); 429 | } 430 | } 431 | ``` 432 | 433 | ![](https://habrastorage.org/webt/eo/ej/k5/eoejk5t_hqvo2xeaitkbzm43grm.png) 434 | 435 | ### Example: clipboard 436 | 437 | ```typescript 438 | import { LoggerService } from '@angular-ru/logger'; 439 | 440 | export class AppComponent implements OnInit { 441 | constructor(private readonly logger: LoggerService) {} 442 | 443 | public ngOnInit(): void { 444 | const example: string = 'test string'; 445 | this.logger.log(example); 446 | this.logger.copy(example); 447 | } 448 | } 449 | ``` 450 | 451 | ![](https://habrastorage.org/webt/sd/uo/3a/sduo3ags-5rsnoanvxtckysresw.gif) 452 | 453 | ### Example: decorators 454 | 455 | ```typescript 456 | import { LoggerService, Logger, DebugLog, TraceLog, InfoLog, WarnLog, ErrorLog, Log, LogFn } from '@angular-ru/logger'; 457 | 458 | export class AppComponent { 459 | @Logger() public logger: LoggerService; 460 | @TraceLog() public trace: LogFn; 461 | @DebugLog() public debug: LogFn; 462 | @InfoLog() public info: LogFn; 463 | @ErrorLog() public error: LogFn; 464 | @WarnLog() public warn: LogFn; 465 | @Log() public log: LogFn; 466 | 467 | private readonly traceIsWork: string = 'trace is worked'; 468 | private readonly debugIsWork: string = 'debug is worked'; 469 | private readonly infoIsWork: string = 'info is worked'; 470 | private readonly warnIsWork: string = 'warn is worked'; 471 | private readonly errorIsWork: string = 'error is worked'; 472 | 473 | public showExample(): void { 474 | this.logger.clear(); 475 | this.logger.log('log is worked'); 476 | this.trace(this.traceIsWork, 1, { a: 1 }); 477 | this.debug(this.debugIsWork, 2, console); 478 | this.info(this.infoIsWork, 3, Object); 479 | this.warn(this.warnIsWork, 4, String); 480 | this.error(this.errorIsWork, 5, (2.55).toFixed()); 481 | } 482 | } 483 | ``` 484 | 485 | ![](https://habrastorage.org/webt/fk/ar/a5/fkara5xhh75iz1q9_dales4cu9s.png) 486 | 487 | ### Example: decorator groups 488 | 489 | ```typescript 490 | import { LoggerService, Logger, LoggerLevel, Group } from '@angular-ru/logger'; 491 | 492 | export class AppComponent { 493 | @Logger() public logger: LoggerService; 494 | 495 | @Group('test title', LoggerLevel.WARN) 496 | private helloWorld(name: string): string { 497 | this.logger.log('log only in group', name); 498 | return 'hello world'; 499 | } 500 | 501 | public showExample11(): void { 502 | this.logger.log(this.helloWorld('Hello')); 503 | } 504 | } 505 | ``` 506 | 507 | ![](https://habrastorage.org/webt/na/bu/zw/nabuzwdhsanhy0sznh5tvwdu8es.png) 508 | 509 | ### Example: decorator group with function title 510 | 511 | ```typescript 512 | import { Log, LogFn, Group } from '@angular-ru/logger'; 513 | 514 | export class AppComponent { 515 | @Log() public log: LogFn; 516 | 517 | @Group((name: string) => `Test group with ${name}`) 518 | public method(name: string): string { 519 | this.log('group is worked'); 520 | return name; 521 | } 522 | 523 | public showExample(): void { 524 | this.method('hello world'); 525 | } 526 | } 527 | ``` 528 | 529 | ![](https://habrastorage.org/webt/j9/mz/4v/j9mz4vbyhi0dg_8kr5wjmfwgiye.png) 530 | 531 | ### Example: timer decorator 532 | 533 | ```typescript 534 | import { Log, LogFn, TimerLog, LoggerLevel, LoggerService, Logger } from '@angular-ru/logger'; 535 | export class AppComponent { 536 | @Log() public log: LogFn; 537 | @Logger() public logger: LoggerService; 538 | 539 | @TimerLog('Test timer') 540 | public showExample(): void { 541 | this.logger.clear(); 542 | this.log('test log'); 543 | } 544 | 545 | @TimerLog('Advanced timer', LoggerLevel.WARN, false) 546 | public showExample(): void { 547 | this.logger.clear(); 548 | this.log('Advanced test log'); 549 | } 550 | } 551 | ``` 552 | 553 | ![](https://habrastorage.org/webt/ur/zl/66/urzl66me9nixsnauhsfpge2tn0a.png) 554 | 555 | ### Example: format output 556 | 557 | ```typescript 558 | import { LoggerModule, NgModule, FormatOutput } from '@angular-ru/logger'; 559 | 560 | @NgModule({ 561 | //.. 562 | imports: [ 563 | LoggerModule.forRoot({ 564 | format(label: string, labelStyle: string): FormatOutput { 565 | const date = new Date().toLocaleString('ru-RU').replace(',', ''); 566 | const customLabel: string = `${date} ${label}`; 567 | return { label: customLabel, style: labelStyle }; 568 | } 569 | }) 570 | ] 571 | }) 572 | export class AppModule {} 573 | ``` 574 | 575 | ```typescript 576 | import { LoggerService, OnInit } from '@angular-ru/logger'; 577 | 578 | export class AppComponent implements OnInit { 579 | constructor(private readonly logger: LoggerService) {} 580 | 581 | public ngOnInit(): void { 582 | this.logger.trace('trace is worked', 1, { a: 1 }); 583 | this.logger.debug('debug is worked', 2, {}); 584 | this.logger.info('info is worked', 3, Object); 585 | this.logger.warn('warn is worked', 4, String); 586 | this.logger.error('error is worked', 5, (2.55).toFixed()); 587 | } 588 | } 589 | ``` 590 | 591 | ![](https://habrastorage.org/webt/pi/gi/ax/pigiax25o6qapoen_9wjoy4jdio.png) 592 | 593 | ### Example: full configurations 594 | 595 | ```typescript 596 | import { LoggerModule, NgModule, LoggerLevel } from '@angular-ru/logger'; 597 | 598 | @NgModule({ 599 | // .. 600 | imports: [ 601 | LoggerModule.forRoot({ 602 | useLevelGroup: true, 603 | globalLineStyle: 'color: red; text-decoration: underline; font-weight: bold; font-size: 15px', 604 | cssClassMap: { 605 | bold: 'font-weight: bold', 606 | 'line-through': 'text-decoration: line-through', 607 | 'code-sandbox': ` 608 | color: #666; 609 | background: #f4f4f4; 610 | border-left: 3px solid #f36d33; 611 | font-family: monospace; 612 | font-size: 15px;` 613 | }, 614 | labelNames: { 615 | [LoggerLevel.TRACE]: '[trace]', 616 | [LoggerLevel.DEBUG]: '[debug]', 617 | [LoggerLevel.INFO]: '[info]', 618 | [LoggerLevel.WARN]: '[warn]', 619 | [LoggerLevel.ERROR]: '[error]' 620 | }, 621 | labelColors: { 622 | [LoggerLevel.TRACE]: 'violet', 623 | [LoggerLevel.DEBUG]: 'black', 624 | [LoggerLevel.INFO]: 'tomato', 625 | [LoggerLevel.WARN]: 'green', 626 | [LoggerLevel.ERROR]: 'cyan' 627 | } 628 | }) 629 | ] 630 | // .. 631 | }) 632 | export class AppModule {} 633 | ``` 634 | 635 | ```typescript 636 | import { LoggerService } from '@angular-ru/logger'; 637 | 638 | export class AppComponent implements OnInit { 639 | 640 | public ngOnInit(): void { 641 | private readonly traceIsWork: string = 'trace is worked'; 642 | private readonly debugIsWork: string = 'debug is worked'; 643 | private readonly infoIsWork: string = 'info is worked'; 644 | private readonly warnIsWork: string = 'warn is worked'; 645 | private readonly errorIsWork: string = 'error is worked'; 646 | 647 | constructor(private readonly logger: LoggerService) {} 648 | 649 | public showExample(): void { 650 | this.logger.log('Example'); 651 | this.logger.trace(this.traceIsWork, 1, { a: 1 }); 652 | this.logger.debug(this.debugIsWork, 2, console); 653 | this.logger.info(this.infoIsWork, 3, Object); 654 | this.logger.warn(this.warnIsWork, 4, String); 655 | this.logger.error(this.errorIsWork, 5, (2.55).toFixed()); 656 | } 657 | } 658 | ``` 659 | 660 | ![](https://habrastorage.org/webt/we/vi/xw/wevixwtlrik5gqxfif2wadprura.png) 661 | 662 | ## Todo 663 | 664 | - [x] Override console 665 | - [x] Logger method (trace, debug, info, warning, error) 666 | - [x] Logger group + groupCollapsible (pipes) 667 | - [x] Logger pretty write object 668 | - [x] Set style by css 669 | - [x] Logger level groups (trace, debug, info, warn, error) 670 | - [x] Clipboard data 671 | - [x] Set global style 672 | - [x] Added css classes 673 | - [x] Dependency Injection for Angular 674 | - [x] Switch enable/disable default console output 675 | - [x] Decorators 676 | - [x] Timers (decorator) 677 | - [x] Format output console 678 | 679 | ## Authors 680 | 681 | [Eleonora Zbarskaya](https://github.com/kingofferelden), [Ivanov Maxim](https://github.com/splincode) 682 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "integration": { 7 | "root": "", 8 | "prefix": "", 9 | "sourceRoot": "integration", 10 | "projectType": "application", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/integration", 17 | "index": "integration/app/index.html", 18 | "main": "integration/app/main.ts", 19 | "polyfills": "integration/app/polyfills.ts", 20 | "tsConfig": "integration/app/tsconfig.app.json", 21 | "assets": ["integration/app/favicon.ico", "integration/app/assets"], 22 | "styles": ["integration/app/styles.css"], 23 | "scripts": [], 24 | "es5BrowserSupport": true 25 | }, 26 | "configurations": { 27 | "production": { 28 | "fileReplacements": [ 29 | { 30 | "replace": "integration/app/src/environments/environment.ts", 31 | "with": "integration/app/src/environments/environment.prod.ts" 32 | } 33 | ], 34 | "optimization": true, 35 | "outputHashing": "all", 36 | "sourceMap": false, 37 | "extractCss": true, 38 | "namedChunks": false, 39 | "aot": true, 40 | "extractLicenses": true, 41 | "vendorChunk": true, 42 | "buildOptimizer": true, 43 | "budgets": [ 44 | { 45 | "type": "initial", 46 | "maximumWarning": "2mb", 47 | "maximumError": "5mb" 48 | } 49 | ] 50 | } 51 | } 52 | }, 53 | "serve": { 54 | "builder": "@angular-devkit/build-angular:dev-server", 55 | "options": { 56 | "browserTarget": "integration:build" 57 | }, 58 | "configurations": { 59 | "production": { 60 | "browserTarget": "integration:build:production" 61 | } 62 | } 63 | } 64 | } 65 | }, 66 | "logger": { 67 | "root": "", 68 | "prefix": "", 69 | "sourceRoot": "", 70 | "projectType": "library", 71 | "architect": { 72 | "build": { 73 | "builder": "@angular-devkit/build-ng-packagr:build", 74 | "options": { 75 | "tsConfig": "lib/tsconfig.lib.json", 76 | "project": "lib/ng-package.json" 77 | } 78 | } 79 | } 80 | } 81 | }, 82 | "defaultProject": "logger" 83 | } 84 | -------------------------------------------------------------------------------- /integration/app/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /integration/app/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | import { Any } from '../../../lib/src/interfaces/logger.internal'; 2 | 3 | export const environment: Any = { 4 | production: true, 5 | useConfig: true 6 | }; 7 | -------------------------------------------------------------------------------- /integration/app/environments/environment.ts: -------------------------------------------------------------------------------- 1 | import { Any } from '../../../lib/src/interfaces/logger.internal'; 2 | 3 | export const environment: Any = { 4 | production: false, 5 | useConfig: true 6 | }; 7 | -------------------------------------------------------------------------------- /integration/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Angular-RU/angular-logger/ce0cd7d55d0b9d077132f06d2c899dae544074a4/integration/app/favicon.ico -------------------------------------------------------------------------------- /integration/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgxLogger 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /integration/app/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { environment } from './environments/environment'; 5 | import { AppModule } from './src/app.module'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch((err: Error): void => console.error(err)); 14 | -------------------------------------------------------------------------------- /integration/app/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone'; 2 | 3 | import { Any } from '../../lib/src/interfaces/logger.internal'; 4 | 5 | (window as Any)['__importDefault'] = 6 | (window as Any)['__importDefault'] || 7 | function (mod: Any): Any { 8 | return mod && mod.__esModule ? mod : { default: mod }; 9 | }; 10 | 11 | (window as Any).global = window; // Included with Angular CLI. 12 | -------------------------------------------------------------------------------- /integration/app/src/app.component.css: -------------------------------------------------------------------------------- 1 | .ext-box { 2 | display: none; 3 | } 4 | 5 | .ext-box.load { 6 | display: table; 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | .int-box { 12 | display: table-cell; 13 | vertical-align: middle; 14 | opacity: 0; 15 | transition: opacity 0.3s ease-out; 16 | } 17 | 18 | .load .int-box { 19 | opacity: 1; 20 | transition: opacity 0.3s ease-out; 21 | } 22 | 23 | .center-block { 24 | margin: 0 auto; 25 | display: block; 26 | padding: 10px; 27 | } 28 | 29 | .example.no-display { 30 | transition: opacity 0.3s ease-out; 31 | opacity: 0; 32 | height: 0; 33 | overflow: hidden; 34 | } 35 | 36 | .example { 37 | opacity: 1; 38 | height: auto; 39 | transition: opacity 0.3s ease-out; 40 | } 41 | -------------------------------------------------------------------------------- /integration/app/src/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |

Simple logger for javascript

7 |

8 | In order to see examples, you need to open the browser console or press F12.
9 |

10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 | Basic method
18 | (trace, debug, info, ..) 19 |
20 |
21 | 22 |
23 | 24 |
25 | Example groups
26 | (closed, opened) 27 |
28 |
29 | 30 |
31 | 32 |
33 | Nested groups
34 | (deep) 35 |
36 |
37 | 38 |
39 | 40 |
Show only warnings and error
41 |
42 | 43 |
44 | 45 |
46 | Custom CSS styles
47 | for line 48 |
49 |
50 | 51 |
52 | 53 |
54 | Pretty JSON
55 | (output) 56 |
57 |
58 | 59 |
60 | 61 |
62 | Copy to clipboard
63 | ((string text)) 64 |
65 |
66 | 67 |
68 | 69 |
70 | Level groups
71 | (trace, debug, info, ..) 72 |
73 |
74 | 75 |
76 | 77 |
78 | Global style line
79 | (css global style) 80 |
81 |
82 | 83 |
84 | 85 |
86 | CSS classes
87 | (set in console) 88 |
89 |
90 | 91 |
92 | 93 |
Decorator
94 |
95 | 96 |
97 | 98 |
99 | Decorator group
100 | with function title 101 |
102 |
103 | 104 |
105 | 106 |
Decorator timer
107 |
108 | 109 |
110 | 111 |
Advanced timer decorator
112 |
113 | 114 |
115 | 116 |
117 | Full configuration
118 | (change labels, colors) 119 |
120 |
121 |
122 |
123 | 124 |
125 |
126 |

You need open console

127 |
128 |
129 |
130 |
131 | -------------------------------------------------------------------------------- /integration/app/src/app.component.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable:no-duplicate-string */ 3 | import { 4 | DebugLog, 5 | ErrorLog, 6 | Group, 7 | InfoLog, 8 | Log, 9 | LogFn, 10 | Logger, 11 | LoggerLevel, 12 | LoggerService, 13 | TimerLog, 14 | TraceLog, 15 | WarnLog 16 | } from '@angular-ru/logger'; 17 | import { Component, OnInit, ViewEncapsulation } from '@angular/core'; 18 | import * as devtools from 'devtools-detect'; 19 | 20 | @Component({ 21 | selector: 'app-root', 22 | templateUrl: './app.component.html', 23 | styleUrls: ['./app.component.css'], 24 | encapsulation: ViewEncapsulation.None 25 | }) 26 | export class AppComponent implements OnInit { 27 | @Logger() public loggerInjection!: LoggerService; 28 | @TraceLog() public trace!: LogFn; 29 | @DebugLog() public debug!: LogFn; 30 | @InfoLog() public info!: LogFn; 31 | @ErrorLog() public error!: LogFn; 32 | @WarnLog() public warn!: LogFn; 33 | @Log() public log!: LogFn; 34 | 35 | public isLoaded: boolean = false; 36 | public devToolsIsOpen: boolean = devtools.isOpen; 37 | 38 | private readonly traceIsWork: string = 'trace is worked'; 39 | private readonly debugIsWork: string = 'debug is worked'; 40 | private readonly infoIsWork: string = 'info is worked'; 41 | private readonly warnIsWork: string = 'warn is worked'; 42 | private readonly errorIsWork: string = 'error is worked'; 43 | 44 | constructor(private readonly logger: LoggerService) {} 45 | 46 | public ngOnInit(): void { 47 | this.isLoaded = true; 48 | window.addEventListener('devtoolschange', (e: devtools.DevToolsEvent) => { 49 | this.devToolsIsOpen = e.detail.isOpen; 50 | }); 51 | } 52 | 53 | public showExample1(): void { 54 | this.logger.clear(); 55 | this.log('log is worked'); 56 | this.trace(this.traceIsWork, 1, { a: 1 }); 57 | this.debug(this.debugIsWork, 2, console); 58 | this.info(this.infoIsWork, 3, Object); 59 | this.warn(this.warnIsWork, 4, String); 60 | this.error(this.errorIsWork, 5, (2.55).toFixed()); 61 | } 62 | 63 | public showExample2(): void { 64 | this.logger.clear(); 65 | 66 | this.logger.groupCollapsed('EXAMPLE 2: show stack', () => { 67 | this.trace(this.traceIsWork, 1, { a: 1 }); 68 | this.debug(this.debugIsWork, 2, console); 69 | this.info(this.infoIsWork, 3, Object); 70 | this.warn(this.warnIsWork, 4, String); 71 | this.error(this.errorIsWork, 5, (2.55).toFixed()); 72 | }); 73 | 74 | this.logger.group('Show trace in opened group', ({ trace }: LoggerService): void => { 75 | for (let i: number = 0; i < 20; i++) { 76 | trace(this.traceIsWork, i); 77 | } 78 | }); 79 | 80 | this.logger 81 | .groupCollapsed('Show trace in collapsed group', ({ debug }: LoggerService): void => { 82 | for (let i: number = 0; i < 15; i++) { 83 | debug(this.traceIsWork, i); 84 | } 85 | }) 86 | .closeAll(); 87 | } 88 | 89 | public showExample3(): void { 90 | this.logger.clear(); 91 | 92 | this.logger 93 | .groupCollapsed('GROUP TEST') 94 | .pipe(({ trace, debug, info, warn, error }: LoggerService) => { 95 | trace(this.traceIsWork); 96 | debug(this.debugIsWork); 97 | info(this.infoIsWork); 98 | warn(this.warnIsWork); 99 | error(this.errorIsWork); 100 | }) 101 | .close(); 102 | 103 | this.logger 104 | .group('A') 105 | .pipe( 106 | ({ trace }: LoggerService) => trace(this.traceIsWork), 107 | ({ debug }: LoggerService) => debug(this.debugIsWork), 108 | ({ info }: LoggerService) => info(this.infoIsWork), 109 | ({ warn }: LoggerService) => warn(this.warnIsWork), 110 | ({ error }: LoggerService) => error(this.errorIsWork) 111 | ) 112 | .groupCollapsed('B') 113 | .pipe( 114 | ({ trace }: LoggerService) => trace(this.traceIsWork), 115 | ({ debug }: LoggerService) => debug(this.debugIsWork), 116 | ({ info }: LoggerService) => info(this.infoIsWork), 117 | ({ warn }: LoggerService) => warn(this.warnIsWork), 118 | ({ error }: LoggerService) => error(this.errorIsWork) 119 | ) 120 | .group('C') 121 | .pipe( 122 | ({ trace }: LoggerService) => trace(this.traceIsWork), 123 | ({ debug }: LoggerService) => debug(this.debugIsWork), 124 | ({ info }: LoggerService) => info(this.infoIsWork), 125 | ({ warn }: LoggerService) => warn(this.warnIsWork), 126 | ({ error }: LoggerService) => error(this.errorIsWork) 127 | ) 128 | .closeAll(); 129 | } 130 | 131 | public showExample4(): void { 132 | this.logger.clear(); 133 | 134 | this.logger.level = LoggerLevel.INFO; 135 | 136 | this.logger.log('log is working', 1, String); 137 | this.trace(this.traceIsWork, 4, String); 138 | this.debug(this.debugIsWork, 4, String); 139 | this.warn(this.warnIsWork, 4, String); 140 | this.error(this.errorIsWork, 5, (2.55).toFixed()); 141 | 142 | this.logger.level = LoggerLevel.ALL; 143 | } 144 | 145 | public showExample5(): void { 146 | this.logger.clear(); 147 | 148 | this.logger.css('text-transform: uppercase; font-weight: bold').debug('window current ', window); 149 | 150 | this.logger.css('color: red; text-decoration: underline; font-weight: bold').info('It is awesome logger'); 151 | this.debug({ a: 1 }); 152 | 153 | this.warn('logger.css(...) does not define a global format!'); 154 | this.info('For global configuration, use the constructor parameters'); 155 | } 156 | 157 | public showExample6(): void { 158 | this.logger.clear(); 159 | 160 | const jsonExample: object = { 161 | id: 1, 162 | hello: 'world' 163 | }; 164 | 165 | this.debug('Classic output json', jsonExample); 166 | 167 | this.logger.log(...this.logger.prettyJSON(jsonExample)); 168 | } 169 | 170 | public showExample7(): void { 171 | this.logger.clear(); 172 | 173 | const example: string = 'test string'; 174 | 175 | this.logger.log(example); 176 | this.logger.copy(example); 177 | } 178 | 179 | public showExample8(): void { 180 | this.logger.clear(); 181 | this.logger.level = LoggerLevel.INFO; 182 | 183 | this.trace 184 | .group('A') 185 | .pipe( 186 | ({ trace }: LoggerService) => trace(this.traceIsWork), 187 | ({ debug }: LoggerService) => debug(this.debugIsWork), 188 | ({ info }: LoggerService) => info(this.infoIsWork), 189 | ({ warn }: LoggerService) => warn(this.warnIsWork), 190 | ({ error }: LoggerService) => error(this.errorIsWork) 191 | ) 192 | .close() 193 | 194 | .debug.group('B') 195 | .pipe( 196 | ({ trace }: LoggerService) => trace(this.traceIsWork), 197 | ({ debug }: LoggerService) => debug(this.debugIsWork), 198 | ({ info }: LoggerService) => info(this.infoIsWork), 199 | ({ warn }: LoggerService) => warn(this.warnIsWork), 200 | ({ error }: LoggerService) => error(this.errorIsWork) 201 | ) 202 | .close() 203 | 204 | .info.group('C') 205 | .pipe( 206 | ({ trace }: LoggerService) => trace(this.traceIsWork), 207 | ({ debug }: LoggerService) => debug(this.debugIsWork), 208 | ({ info }: LoggerService) => info(this.infoIsWork), 209 | ({ warn }: LoggerService) => warn(this.warnIsWork), 210 | ({ error }: LoggerService) => error(this.errorIsWork) 211 | ) 212 | .close() 213 | 214 | .warn.group('D') 215 | .pipe( 216 | ({ trace }: LoggerService) => trace(this.traceIsWork), 217 | ({ debug }: LoggerService) => debug(this.debugIsWork), 218 | ({ info }: LoggerService) => info(this.infoIsWork), 219 | ({ warn }: LoggerService) => warn(this.warnIsWork), 220 | ({ error }: LoggerService) => error(this.errorIsWork) 221 | ) 222 | .close() 223 | 224 | .error.group('E') 225 | .pipe( 226 | ({ trace }: LoggerService) => trace(this.traceIsWork), 227 | ({ debug }: LoggerService) => debug(this.debugIsWork), 228 | ({ info }: LoggerService) => info(this.infoIsWork), 229 | ({ warn }: LoggerService) => warn(this.warnIsWork), 230 | ({ error }: LoggerService) => error(this.errorIsWork) 231 | ) 232 | .close(); 233 | 234 | this.logger.level = LoggerLevel.ALL; 235 | } 236 | 237 | public showExample9(): void { 238 | this.logger.clear(); 239 | 240 | this.logger.css('font-weight: normal; text-decoration: none; font-style: italic;').info(3.14); 241 | this.logger.css('font-weight: normal;').info(3.14); 242 | this.warn('global format with style!'); 243 | } 244 | 245 | public showExample10(): void { 246 | this.logger.clear(); 247 | 248 | this.logger.cssClass('bold line-through').log('JavaScript sucks', 'JavaScript is the best'); 249 | 250 | this.logger 251 | .cssClass('code-sandbox') 252 | .log('\n @Component({ .. })' + '\n export class AppComponent { .. } \n\n'); 253 | 254 | this.logger.cssClass('bold line-through').debug('JavaScript sucks', 'JavaScript is the best'); 255 | 256 | this.logger.level = LoggerLevel.INFO; 257 | } 258 | 259 | public showExample11(): void { 260 | this.loggerInjection.clear(); 261 | this.logger.log(this.helloWorld('Max')); 262 | } 263 | 264 | @Group((name: string) => `Test group with ${name}`) 265 | public method(name: string): string { 266 | this.loggerInjection.log('group is worked'); 267 | return name; 268 | } 269 | 270 | public showExample12(): void { 271 | this.loggerInjection.clear(); 272 | this.method('hello world'); 273 | } 274 | 275 | @TimerLog('Test timer') 276 | public showExample13(): void { 277 | this.logger.clear(); 278 | this.log('test log'); 279 | } 280 | 281 | @TimerLog('Advanced timer', LoggerLevel.WARN, false) 282 | public showExample14(): void { 283 | this.logger.clear(); 284 | this.log('Advanced test log'); 285 | } 286 | 287 | @Group('test title', LoggerLevel.WARN) 288 | private helloWorld(name: string): string { 289 | this.logger.log('log only in group', name); 290 | return 'hello world'; 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /integration/app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { LoggerModule } from '@angular-ru/logger'; 2 | import { HttpClientModule } from '@angular/common/http'; 3 | import { NgModule } from '@angular/core'; 4 | import { BrowserModule } from '@angular/platform-browser'; 5 | 6 | import { environment } from '../environments/environment'; 7 | import { AppComponent } from './app.component'; 8 | 9 | @NgModule({ 10 | declarations: [AppComponent], 11 | imports: [ 12 | LoggerModule.forRoot( 13 | environment.useConfig 14 | ? { 15 | useLevelGroup: true, 16 | cssClassMap: { 17 | bold: 'font-weight: bold', 18 | 'line-through': 'text-decoration: line-through', 19 | 'code-sandbox': ` 20 | color: #666; 21 | background: #f4f4f4; 22 | border-left: 3px solid #f36d33; 23 | font-family: monospace; 24 | font-size: 15px;` 25 | } 26 | } 27 | : {} 28 | ), 29 | BrowserModule, 30 | HttpClientModule 31 | ], 32 | providers: [], 33 | bootstrap: [AppComponent] 34 | }) 35 | export class AppModule {} 36 | -------------------------------------------------------------------------------- /integration/app/styles.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Lato:400, 300, 300italic, 400italic, 700, 700italic); 2 | 3 | body, 4 | html { 5 | height: 100%; 6 | width: 100%; 7 | margin: 0; 8 | padding: 0; 9 | left: 0; 10 | top: 0; 11 | font-size: 100%; 12 | } 13 | 14 | .center, 15 | .container { 16 | margin-left: auto; 17 | margin-right: auto; 18 | } 19 | 20 | * { 21 | font-family: Lato, Helvetica, sans-serif; 22 | color: #333447; 23 | line-height: 1.5; 24 | } 25 | 26 | h1 { 27 | font-size: 2.5rem; 28 | } 29 | 30 | h2 { 31 | font-size: 2rem; 32 | } 33 | 34 | h3 { 35 | font-size: 1.375rem; 36 | } 37 | 38 | h4 { 39 | font-size: 1.125rem; 40 | } 41 | 42 | h5 { 43 | font-size: 1rem; 44 | } 45 | 46 | h6 { 47 | font-size: 0.875rem; 48 | } 49 | 50 | p { 51 | font-size: 1.125rem; 52 | font-weight: 200; 53 | line-height: 1.8; 54 | } 55 | 56 | .font-light { 57 | font-weight: 300; 58 | } 59 | 60 | .font-regular { 61 | font-weight: 400; 62 | } 63 | 64 | .font-heavy { 65 | font-weight: 700; 66 | } 67 | 68 | .left { 69 | text-align: left; 70 | } 71 | 72 | .right { 73 | text-align: right; 74 | } 75 | 76 | .center { 77 | text-align: center; 78 | } 79 | 80 | .justify { 81 | text-align: justify; 82 | } 83 | 84 | .container { 85 | width: 90%; 86 | } 87 | 88 | .row { 89 | position: relative; 90 | width: 100%; 91 | } 92 | 93 | .row [class^='col'] { 94 | float: left; 95 | margin: 0.5rem 2%; 96 | min-height: 0.125rem; 97 | } 98 | 99 | .col-1, 100 | .col-10, 101 | .col-11, 102 | .col-12, 103 | .col-2, 104 | .col-3, 105 | .col-4, 106 | .col-5, 107 | .col-6, 108 | .col-7, 109 | .col-8, 110 | .col-9 { 111 | width: 96%; 112 | } 113 | 114 | .col-1-sm { 115 | width: 4.33%; 116 | } 117 | 118 | .col-2-sm { 119 | width: 12.66%; 120 | } 121 | 122 | .col-3-sm { 123 | width: 21%; 124 | } 125 | 126 | .col-4-sm { 127 | width: 29.33%; 128 | } 129 | 130 | .col-5-sm { 131 | width: 37.66%; 132 | } 133 | 134 | .col-6-sm { 135 | width: 46%; 136 | } 137 | 138 | .col-7-sm { 139 | width: 54.33%; 140 | } 141 | 142 | .col-8-sm { 143 | width: 62.66%; 144 | } 145 | 146 | .col-9-sm { 147 | width: 71%; 148 | } 149 | 150 | .col-10-sm { 151 | width: 79.33%; 152 | } 153 | 154 | .col-11-sm { 155 | width: 87.66%; 156 | } 157 | 158 | .col-12-sm { 159 | width: 96%; 160 | } 161 | 162 | .row::after { 163 | content: ''; 164 | display: table; 165 | clear: both; 166 | } 167 | 168 | .hidden-sm { 169 | display: none; 170 | } 171 | 172 | @media only screen and (min-width: 33.75em) { 173 | .container { 174 | width: 80%; 175 | } 176 | } 177 | 178 | @media only screen and (min-width: 45em) { 179 | .col-1 { 180 | width: 4.33%; 181 | } 182 | 183 | .col-2 { 184 | width: 12.66%; 185 | } 186 | 187 | .col-3 { 188 | width: 21%; 189 | } 190 | 191 | .col-4 { 192 | width: 29.33%; 193 | } 194 | 195 | .col-5 { 196 | width: 37.66%; 197 | } 198 | 199 | .col-6 { 200 | width: 46%; 201 | } 202 | 203 | .col-7 { 204 | width: 54.33%; 205 | } 206 | 207 | .col-8 { 208 | width: 62.66%; 209 | } 210 | 211 | .col-9 { 212 | width: 71%; 213 | } 214 | 215 | .col-10 { 216 | width: 79.33%; 217 | } 218 | 219 | .col-11 { 220 | width: 87.66%; 221 | } 222 | 223 | .col-12 { 224 | width: 96%; 225 | } 226 | 227 | .hidden-sm { 228 | display: block; 229 | } 230 | } 231 | 232 | @media only screen and (min-width: 60em) { 233 | .container { 234 | width: 75%; 235 | max-width: 60rem; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /integration/app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": ["**/*.spec.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /integration/app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["test.ts", 8 | "polyfills.ts" 9 | ], 10 | "include": ["**/*.spec.ts", "**/*.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /integration/tests/autobind.spec.ts: -------------------------------------------------------------------------------- 1 | import { autoBind } from '../../lib/src/decorators/autobind.decorator'; 2 | 3 | describe('@autoBind', () => { 4 | class Foo { 5 | @autoBind 6 | public getFoo(): this { 7 | return this; 8 | } 9 | 10 | public getFooAgain(): this { 11 | return this; 12 | } 13 | 14 | @autoBind 15 | public onlyOnFoo(): this { 16 | return this; 17 | } 18 | } 19 | 20 | class Bar extends Foo { 21 | @autoBind() 22 | public getFoo(): this { 23 | return super.getFoo(); 24 | } 25 | 26 | public getSuperMethod_getFoo(): () => this { 27 | return super.getFoo; 28 | } 29 | 30 | @autoBind 31 | public onlyOnBar(): this { 32 | return this; 33 | } 34 | } 35 | 36 | it('returns a bound instance for a method', function(): void { 37 | const foo: Foo = new Foo(); 38 | const { getFoo }: Foo = foo; 39 | 40 | expect(getFoo()).toEqual(foo); 41 | }); 42 | 43 | it('sets the correct instance descriptor options when bound', function(): void { 44 | const foo: Foo = new Foo(); 45 | const { getFoo }: Foo = foo; 46 | const desc: PropertyDescriptor = Object.getOwnPropertyDescriptor(foo, 'getFoo') as PropertyDescriptor; 47 | 48 | expect(desc.configurable).toEqual(true); 49 | expect(desc.enumerable).toEqual(false); 50 | expect(desc.writable).toEqual(true); 51 | expect(desc.value).toEqual(getFoo); 52 | }); 53 | 54 | it('works with multiple instances of the same class', function(): void { 55 | const foo1: Foo = new Foo(); 56 | const foo2: Foo = new Foo(); 57 | 58 | const getFoo1: () => Foo = foo1.getFoo; 59 | const getFoo2: () => Foo = foo2.getFoo; 60 | 61 | expect(getFoo1()).toEqual(foo1); 62 | expect(getFoo2()).toEqual(foo2); 63 | }); 64 | 65 | it('returns the same bound function every time', function(): void { 66 | const foo: Foo = new Foo(); 67 | const bar: Bar = new Bar(); 68 | 69 | expect(foo.getFoo).toEqual(foo.getFoo); 70 | expect(bar.getFoo).toEqual(bar.getFoo); 71 | expect(bar.getSuperMethod_getFoo()).toEqual(bar.getSuperMethod_getFoo()); 72 | expect(bar.getFooAgain()).toEqual(bar.getFooAgain()); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /integration/tests/clipboard.spec.ts: -------------------------------------------------------------------------------- 1 | import { LoggerService } from '../../lib/src/logger.service'; 2 | import { ConsoleFake } from './helpers/console-fake'; 3 | import { TestBed } from '@angular/core/testing'; 4 | import { LoggerModule } from '../../lib/src/logger.module'; 5 | import { ObjectKeyMap } from '../../lib/src/interfaces/logger.internal'; 6 | 7 | describe('[TEST]: Check clipboard', () => { 8 | let logger: LoggerService; 9 | let buffer: string | null = null; 10 | const fakeConsole: ConsoleFake = new ConsoleFake(); 11 | const textarea: Partial = { 12 | textContent: null, 13 | style: {} as CSSStyleDeclaration, 14 | select: (): void => {} 15 | }; 16 | 17 | beforeAll(() => { 18 | TestBed.configureTestingModule({ 19 | imports: [ 20 | LoggerModule.forRoot({ 21 | instance: fakeConsole 22 | }) 23 | ] 24 | }); 25 | 26 | logger = TestBed.get(LoggerService); 27 | }); 28 | 29 | beforeEach(() => { 30 | buffer = null; 31 | window.clipboardData = null!; 32 | document.queryCommandSupported = null!; 33 | textarea.textContent = null!; 34 | document.execCommand = null!; 35 | }); 36 | 37 | it(`Copy is security and save data local memory`, () => { 38 | Object.defineProperty(window, 'clipboardData', { 39 | writable: true, 40 | value: { 41 | setData: (type: string, value: string): void | boolean => { 42 | if (type === 'Text') { 43 | buffer = value; 44 | return true; 45 | } 46 | } 47 | } 48 | }); 49 | 50 | const stringValue: string = 'test string'; 51 | const isExec: boolean = logger.copy(stringValue); 52 | 53 | expect(isExec).toEqual(true); 54 | expect(buffer).toEqual(stringValue); 55 | }); 56 | 57 | it('should be correct copy/paste document copy', () => { 58 | createMockQueryCommands(textarea); 59 | 60 | Object.defineProperty(document, 'execCommand', { 61 | writable: true, 62 | value: (): void | boolean => { 63 | buffer = textarea.textContent!; 64 | return true; 65 | } 66 | }); 67 | 68 | const JsonValue: ObjectKeyMap = { a: 1, b: [1, 2, 3] }; 69 | const isExec: boolean = logger.copy(JsonValue); 70 | 71 | expect(isExec).toEqual(true); 72 | expect(buffer).toEqual(JSON.stringify(JsonValue, null, 4)); 73 | }); 74 | 75 | it('should be throw exception when incorrect execCommand', () => { 76 | createMockQueryCommands(textarea); 77 | 78 | const JsonValue: ObjectKeyMap = { a: 1, b: [1, 2, 3] }; 79 | const isExec: boolean = logger.copy(JsonValue); 80 | 81 | expect(isExec).toEqual(false); 82 | expect(buffer).toEqual(null); 83 | }); 84 | 85 | it(`should be correct fallback`, () => { 86 | const stringValue: string = 'test string'; 87 | const isExec: boolean = logger.copy(stringValue); 88 | 89 | expect(isExec).toEqual(false); 90 | expect(buffer).toEqual(null); 91 | }); 92 | }); 93 | 94 | function createMockQueryCommands(textareaRef: Partial): void { 95 | Object.defineProperty(document, 'queryCommandSupported', { 96 | writable: true, 97 | value: (): void | boolean => true 98 | }); 99 | 100 | Object.defineProperty(document, 'body', { 101 | writable: true, 102 | value: { 103 | appendChild: (): void => {}, 104 | removeChild: (): void => {} 105 | } 106 | }); 107 | 108 | Object.defineProperty(document, 'createElement', { 109 | writable: true, 110 | value: (elementName: string): Partial | any => { 111 | if (elementName === 'textarea') { 112 | return textareaRef; 113 | } 114 | } 115 | }); 116 | } 117 | -------------------------------------------------------------------------------- /integration/tests/console-format-api.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { LoggerModule } from '../../lib/src/logger.module'; 3 | import { LoggerService } from '../../lib/src/logger.service'; 4 | import { ConsoleFake } from './helpers/console-fake'; 5 | import { CUSTOM_COLORS, CUSTOM_LABELS } from './helpers/custom-colors.enum'; 6 | import { FormatOutput, LoggerLevel } from '../../lib/src/interfaces/logger.external'; 7 | import { ObjectKeyMap } from '../../lib/src/interfaces/logger.internal'; 8 | 9 | describe('[TEST]: Check global style', () => { 10 | let logger: LoggerService; 11 | const fakeConsole: ConsoleFake = new ConsoleFake(); 12 | 13 | const traceIsWork: string = 'trace is worked'; 14 | const debugIsWork: string = 'debug is worked'; 15 | const infoIsWork: string = 'info is worked'; 16 | const warnIsWork: string = 'warn is worked'; 17 | const errorIsWork: string = 'error is worked'; 18 | 19 | beforeAll(() => { 20 | TestBed.configureTestingModule({ 21 | imports: [ 22 | LoggerModule.forRoot({ 23 | instance: fakeConsole, 24 | labelNames: { 25 | [LoggerLevel.TRACE]: CUSTOM_LABELS.TRACE, 26 | [LoggerLevel.DEBUG]: CUSTOM_LABELS.DEBUG, 27 | [LoggerLevel.INFO]: CUSTOM_LABELS.INFO, 28 | [LoggerLevel.WARN]: CUSTOM_LABELS.WARN, 29 | [LoggerLevel.ERROR]: CUSTOM_LABELS.ERROR 30 | }, 31 | labelColors: { 32 | [LoggerLevel.TRACE]: CUSTOM_COLORS.TRACE, 33 | [LoggerLevel.DEBUG]: CUSTOM_COLORS.DEBUG, 34 | [LoggerLevel.INFO]: CUSTOM_COLORS.INFO, 35 | [LoggerLevel.WARN]: CUSTOM_COLORS.WARN, 36 | [LoggerLevel.ERROR]: CUSTOM_COLORS.ERROR 37 | }, 38 | format(label: string, labelStyle: string): FormatOutput { 39 | const customLabel: string = `${label}`; 40 | return { label: customLabel, style: labelStyle }; 41 | } 42 | }) 43 | ] 44 | }); 45 | 46 | logger = TestBed.get(LoggerService); 47 | }); 48 | 49 | beforeEach(() => { 50 | logger.clear(); 51 | }); 52 | 53 | it(`Set new text for labels: [trace, debug, info, warn, error]`, () => { 54 | logger.level = LoggerLevel.ALL; 55 | 56 | const traceLine: number = 0; 57 | logger.trace(traceIsWork, 1, { a: 1 }); 58 | 59 | const debugLine: number = 1; 60 | logger.debug(debugIsWork, 2, {}); 61 | 62 | const infoLine: number = 2; 63 | logger.info(infoIsWork, 3, Object); 64 | 65 | const warnLine: number = 3; 66 | logger.warn(warnIsWork, 4, String); 67 | 68 | const errorLine: number = 4; 69 | logger.error(errorIsWork, 5, (2.55).toFixed()); 70 | 71 | const stackOptionsList: ObjectKeyMap = fakeConsole.stackOptionsList(); 72 | 73 | const { label: traceLabel }: ObjectKeyMap = stackOptionsList[traceLine]; 74 | const { label: debugLabel }: ObjectKeyMap = stackOptionsList[debugLine]; 75 | const { label: infoLabel }: ObjectKeyMap = stackOptionsList[infoLine]; 76 | const { label: warnLabel }: ObjectKeyMap = stackOptionsList[warnLine]; 77 | const { label: errorLabel }: ObjectKeyMap = stackOptionsList[errorLine]; 78 | 79 | expect(traceLabel).toEqual(CUSTOM_LABELS.TRACE); 80 | expect(debugLabel).toEqual(CUSTOM_LABELS.DEBUG); 81 | expect(infoLabel).toEqual(CUSTOM_LABELS.INFO); 82 | expect(warnLabel).toEqual(CUSTOM_LABELS.WARN); 83 | expect(errorLabel).toEqual(CUSTOM_LABELS.ERROR); 84 | }); 85 | 86 | it(`Detect custom colors for labels`, () => { 87 | logger.level = LoggerLevel.ALL; 88 | 89 | const traceLine: number = 0; 90 | logger.trace(traceIsWork, 1, { a: 1 }); 91 | 92 | const debugLine: number = 1; 93 | logger.debug(debugIsWork, 2, {}); 94 | 95 | const infoLine: number = 2; 96 | logger.info(infoIsWork, 3, Object); 97 | 98 | const warnLine: number = 3; 99 | logger.warn(warnIsWork, 4, String); 100 | 101 | const errorLine: number = 4; 102 | logger.error(errorIsWork, 5, (2.55).toFixed()); 103 | 104 | const stackOptionsList: ObjectKeyMap = fakeConsole.stackOptionsList(); 105 | 106 | const { styles: traceStyle }: ObjectKeyMap = stackOptionsList[traceLine]; 107 | const { styles: debugStyle }: ObjectKeyMap = stackOptionsList[debugLine]; 108 | const { styles: infoStyle }: ObjectKeyMap = stackOptionsList[infoLine]; 109 | const { styles: warnStyle }: ObjectKeyMap = stackOptionsList[warnLine]; 110 | const { styles: errorStyle }: ObjectKeyMap = stackOptionsList[errorLine]; 111 | 112 | expect(traceStyle.color).toEqual(CUSTOM_COLORS.TRACE); 113 | expect(debugStyle.color).toEqual(CUSTOM_COLORS.DEBUG); 114 | expect(infoStyle.color).toEqual(CUSTOM_COLORS.INFO); 115 | expect(warnStyle.color).toEqual(CUSTOM_COLORS.WARN); 116 | expect(errorStyle.color).toEqual(CUSTOM_COLORS.ERROR); 117 | }); 118 | 119 | it(`Clear custom labels: `, () => { 120 | logger.setLabels({ 121 | [LoggerLevel.TRACE]: CUSTOM_LABELS.TRACE, 122 | [LoggerLevel.DEBUG]: CUSTOM_LABELS.DEBUG, 123 | [LoggerLevel.INFO]: CUSTOM_LABELS.INFO, 124 | [LoggerLevel.WARN]: CUSTOM_LABELS.WARN, 125 | [LoggerLevel.ERROR]: CUSTOM_LABELS.ERROR 126 | }); 127 | 128 | const traceLine: number = 0; 129 | logger.trace(traceIsWork, 1, { a: 1 }); 130 | 131 | const debugLine: number = 1; 132 | logger.debug(debugIsWork, 2, {}); 133 | 134 | const infoLine: number = 2; 135 | logger.info(infoIsWork, 3, Object); 136 | 137 | const warnLine: number = 3; 138 | logger.warn(warnIsWork, 4, String); 139 | 140 | const errorLine: number = 4; 141 | logger.error(errorIsWork, 5, (2.55).toFixed()); 142 | 143 | const stackOptionsList: ObjectKeyMap = fakeConsole.stackOptionsList(); 144 | 145 | const { label: traceLabel }: ObjectKeyMap = stackOptionsList[traceLine]; 146 | const { label: debugLabel }: ObjectKeyMap = stackOptionsList[debugLine]; 147 | const { label: infoLabel }: ObjectKeyMap = stackOptionsList[infoLine]; 148 | const { label: warnLabel }: ObjectKeyMap = stackOptionsList[warnLine]; 149 | const { label: errorLabel }: ObjectKeyMap = stackOptionsList[errorLine]; 150 | 151 | expect(traceLabel).toEqual(CUSTOM_LABELS.TRACE); 152 | expect(debugLabel).toEqual(CUSTOM_LABELS.DEBUG); 153 | expect(infoLabel).toEqual(CUSTOM_LABELS.INFO); 154 | expect(warnLabel).toEqual(CUSTOM_LABELS.WARN); 155 | expect(errorLabel).toEqual(CUSTOM_LABELS.ERROR); 156 | }); 157 | 158 | it(`Set new colors for labels`, () => { 159 | logger.level = LoggerLevel.ALL; 160 | logger.clear(); 161 | 162 | logger.setColors({ 163 | [LoggerLevel.TRACE]: CUSTOM_COLORS.TRACE, 164 | [LoggerLevel.DEBUG]: CUSTOM_COLORS.DEBUG, 165 | [LoggerLevel.INFO]: CUSTOM_COLORS.INFO, 166 | [LoggerLevel.WARN]: CUSTOM_COLORS.WARN, 167 | [LoggerLevel.ERROR]: CUSTOM_COLORS.ERROR 168 | }); 169 | 170 | const traceLine: number = 0; 171 | logger.trace(traceIsWork, 1, { a: 1 }); 172 | 173 | const debugLine: number = 1; 174 | logger.debug(debugIsWork, 2, {}); 175 | 176 | const infoLine: number = 2; 177 | logger.info(infoIsWork, 3, Object); 178 | 179 | const warnLine: number = 3; 180 | logger.warn(warnIsWork, 4, String); 181 | 182 | const errorLine: number = 4; 183 | logger.error(errorIsWork, 5, (2.55).toFixed()); 184 | 185 | const stackOptionsList: ObjectKeyMap = fakeConsole.stackOptionsList(); 186 | 187 | const { styles: traceStyle }: ObjectKeyMap = stackOptionsList[traceLine]; 188 | const { styles: debugStyle }: ObjectKeyMap = stackOptionsList[debugLine]; 189 | const { styles: infoStyle }: ObjectKeyMap = stackOptionsList[infoLine]; 190 | const { styles: warnStyle }: ObjectKeyMap = stackOptionsList[warnLine]; 191 | const { styles: errorStyle }: ObjectKeyMap = stackOptionsList[errorLine]; 192 | 193 | expect(traceStyle.color).toEqual(CUSTOM_COLORS.TRACE); 194 | expect(debugStyle.color).toEqual(CUSTOM_COLORS.DEBUG); 195 | expect(infoStyle.color).toEqual(CUSTOM_COLORS.INFO); 196 | expect(warnStyle.color).toEqual(CUSTOM_COLORS.WARN); 197 | expect(errorStyle.color).toEqual(CUSTOM_COLORS.ERROR); 198 | }); 199 | }); 200 | -------------------------------------------------------------------------------- /integration/tests/console-group-api.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:quotemark */ 2 | import { LoggerService } from '../../lib/src/logger.service'; 3 | import { ConsoleFake, TestLoggerGroupType, TestLoggerLineType } from './helpers/console-fake'; 4 | import { TestBed } from '@angular/core/testing'; 5 | import { LoggerModule } from '../../lib/src/logger.module'; 6 | import { CUSTOM_COLORS, CUSTOM_LABELS } from './helpers/custom-colors.enum'; 7 | import { LoggerLevel } from '../../lib/src/interfaces/logger.external'; 8 | 9 | // tslint:disable-next-line:no-big-function 10 | describe('[TEST]: Check work in groups', () => { 11 | let logger: LoggerService; 12 | const fakeConsole: ConsoleFake = new ConsoleFake(); 13 | 14 | const traceIsWork: string = 'trace is worked'; 15 | const debugIsWork: string = 'debug is worked'; 16 | const infoIsWork: string = 'info is worked'; 17 | const warnIsWork: string = 'warn is worked'; 18 | const errorIsWork: string = 'error is worked'; 19 | 20 | const traceGroupIsWork: string = 'trace group is worked'; 21 | const debugGroupIsWork: string = 'debug group is worked'; 22 | const infoGroupIsWork: string = 'info group is worked'; 23 | const warnGroupIsWork: string = 'warn group is worked'; 24 | const errorGroupIsWork: string = 'error group is worked'; 25 | 26 | beforeAll(() => { 27 | TestBed.configureTestingModule({ 28 | imports: [ 29 | LoggerModule.forRoot({ 30 | instance: fakeConsole, 31 | labelNames: { 32 | [LoggerLevel.TRACE]: CUSTOM_LABELS.TRACE, 33 | [LoggerLevel.DEBUG]: CUSTOM_LABELS.DEBUG, 34 | [LoggerLevel.INFO]: CUSTOM_LABELS.INFO, 35 | [LoggerLevel.WARN]: CUSTOM_LABELS.WARN, 36 | [LoggerLevel.ERROR]: CUSTOM_LABELS.ERROR 37 | }, 38 | labelColors: { 39 | [LoggerLevel.TRACE]: CUSTOM_COLORS.TRACE, 40 | [LoggerLevel.DEBUG]: CUSTOM_COLORS.DEBUG, 41 | [LoggerLevel.INFO]: CUSTOM_COLORS.INFO, 42 | [LoggerLevel.WARN]: CUSTOM_COLORS.WARN, 43 | [LoggerLevel.ERROR]: CUSTOM_COLORS.ERROR 44 | } 45 | }) 46 | ] 47 | }); 48 | 49 | logger = TestBed.get(LoggerService); 50 | }); 51 | 52 | beforeEach(() => { 53 | logger.clear(); 54 | }); 55 | 56 | it(`Show classic group`, () => { 57 | logger.group('group label', ({ trace }: LoggerService): void => { 58 | trace(traceIsWork, 1, { a: 1 }); 59 | }); 60 | 61 | expect(fakeConsole.stack()).toEqual( 62 | fakeConsole.createStack( 63 | { [TestLoggerGroupType.GROUP_OPEN]: ['group label'] }, 64 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork, 1, { a: 1 }] }, 65 | { [TestLoggerGroupType.GROUP_END]: [] } 66 | ) 67 | ); 68 | }); 69 | 70 | it(`Pipe group`, () => { 71 | logger 72 | .group('group name') 73 | .pipe(({ trace }: LoggerService) => trace(traceIsWork)) 74 | .pipe(({ debug }: LoggerService) => debug(debugIsWork)) 75 | .pipe(({ info }: LoggerService) => info(infoIsWork)) 76 | .pipe(({ warn }: LoggerService) => warn(warnIsWork)) 77 | .pipe(({ error }: LoggerService) => error(errorIsWork)) 78 | .close(); 79 | 80 | expect(fakeConsole.stack()).toEqual( 81 | fakeConsole.createStack( 82 | { [TestLoggerGroupType.GROUP_OPEN]: [`group name`] }, 83 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork] }, 84 | { [TestLoggerLineType.DEBUG]: [debugIsWork] }, 85 | { [TestLoggerLineType.INFO]: [infoIsWork] }, 86 | { [TestLoggerLineType.WARN]: [warnIsWork] }, 87 | { [TestLoggerLineType.ERROR]: [errorIsWork] }, 88 | { [TestLoggerGroupType.GROUP_END]: [] } 89 | ) 90 | ); 91 | }); 92 | 93 | it(`Pipe group-collapsed`, () => { 94 | logger 95 | .groupCollapsed('group collapsed name') 96 | .pipe(({ trace }: LoggerService) => trace(traceIsWork)) 97 | .pipe(({ debug }: LoggerService) => debug(debugIsWork)) 98 | .pipe(({ info }: LoggerService) => info(infoIsWork)) 99 | .pipe(({ warn }: LoggerService) => warn(warnIsWork)) 100 | .pipe(({ error }: LoggerService) => error(errorIsWork)) 101 | .close(); 102 | 103 | expect(fakeConsole.stack()).toEqual( 104 | fakeConsole.createStack( 105 | { [TestLoggerGroupType.GROUP_COLLAPSED_OPEN]: [`group collapsed name`] }, 106 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork] }, 107 | { [TestLoggerLineType.DEBUG]: [debugIsWork] }, 108 | { [TestLoggerLineType.INFO]: [infoIsWork] }, 109 | { [TestLoggerLineType.WARN]: [warnIsWork] }, 110 | { [TestLoggerLineType.ERROR]: [errorIsWork] }, 111 | { [TestLoggerGroupType.GROUP_END]: [] } 112 | ) 113 | ); 114 | }); 115 | 116 | it(`Pipe groups (with collapsed)`, () => { 117 | logger 118 | .groupCollapsed('group A') 119 | .pipe(({ trace }: LoggerService) => trace(traceIsWork)) 120 | .close() 121 | .group('group B') 122 | .pipe(({ trace }: LoggerService) => trace(traceIsWork)) 123 | .close(); 124 | 125 | expect(fakeConsole.stack()).toEqual( 126 | fakeConsole.createStack( 127 | { [TestLoggerGroupType.GROUP_COLLAPSED_OPEN]: [`group A`] }, 128 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork] }, 129 | { [TestLoggerGroupType.GROUP_END]: [] }, 130 | { [TestLoggerGroupType.GROUP_OPEN]: [`group B`] }, 131 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork] }, 132 | { [TestLoggerGroupType.GROUP_END]: [] } 133 | ) 134 | ); 135 | }); 136 | 137 | it(`Great groups with group`, () => { 138 | logger 139 | .group('A') 140 | .pipe( 141 | ({ trace }: LoggerService) => trace(traceIsWork), 142 | ({ debug }: LoggerService) => debug(debugIsWork), 143 | ({ info }: LoggerService) => info(infoIsWork), 144 | ({ warn }: LoggerService) => warn(warnIsWork), 145 | ({ error }: LoggerService) => error(errorIsWork) 146 | ) 147 | .groupCollapsed('B') 148 | .pipe( 149 | ({ trace }: LoggerService) => trace(traceIsWork), 150 | ({ debug }: LoggerService) => debug(debugIsWork), 151 | ({ info }: LoggerService) => info(infoIsWork), 152 | ({ warn }: LoggerService) => warn(warnIsWork), 153 | ({ error }: LoggerService) => error(errorIsWork) 154 | ) 155 | .group('C') 156 | .pipe( 157 | ({ trace }: LoggerService) => trace(traceIsWork), 158 | ({ debug }: LoggerService) => debug(debugIsWork), 159 | ({ info }: LoggerService) => info(infoIsWork), 160 | ({ warn }: LoggerService) => warn(warnIsWork), 161 | ({ error }: LoggerService) => error(errorIsWork) 162 | ) 163 | .closeAll(); 164 | 165 | expect(fakeConsole.stack()).toEqual( 166 | fakeConsole.createStack( 167 | { [TestLoggerGroupType.GROUP_OPEN]: [`A`] }, 168 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork] }, 169 | { [TestLoggerLineType.DEBUG]: [debugIsWork] }, 170 | { [TestLoggerLineType.INFO]: [infoIsWork] }, 171 | { [TestLoggerLineType.WARN]: [warnIsWork] }, 172 | { [TestLoggerLineType.ERROR]: [errorIsWork] }, 173 | { [TestLoggerGroupType.GROUP_COLLAPSED_OPEN]: [`B`] }, 174 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork] }, 175 | { [TestLoggerLineType.DEBUG]: [debugIsWork] }, 176 | { [TestLoggerLineType.INFO]: [infoIsWork] }, 177 | { [TestLoggerLineType.WARN]: [warnIsWork] }, 178 | { [TestLoggerLineType.ERROR]: [errorIsWork] }, 179 | { [TestLoggerGroupType.GROUP_OPEN]: [`C`] }, 180 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork] }, 181 | { [TestLoggerLineType.DEBUG]: [debugIsWork] }, 182 | { [TestLoggerLineType.INFO]: [infoIsWork] }, 183 | { [TestLoggerLineType.WARN]: [warnIsWork] }, 184 | { [TestLoggerLineType.ERROR]: [errorIsWork] }, 185 | { [TestLoggerGroupType.GROUP_END]: [] }, 186 | { [TestLoggerGroupType.GROUP_END]: [] }, 187 | { [TestLoggerGroupType.GROUP_END]: [] } 188 | ) 189 | ); 190 | }); 191 | 192 | it(`Level pretty groups`, () => { 193 | logger.level = LoggerLevel.ALL; 194 | 195 | logger.trace.group('A opened', ({ trace }: LoggerService) => trace(traceGroupIsWork)); 196 | logger.debug.group('B opened', ({ debug }: LoggerService) => debug(debugGroupIsWork)); 197 | logger.info.group('C opened', ({ info }: LoggerService) => info(infoGroupIsWork)); 198 | logger.warn.group('D opened', ({ warn }: LoggerService) => warn(warnGroupIsWork)); 199 | logger.error.group('E opened', ({ error }: LoggerService) => error(errorGroupIsWork)); 200 | 201 | logger.level = LoggerLevel.INFO; 202 | 203 | logger.trace.groupCollapsed('A collapsed', ({ trace }: LoggerService) => trace(traceGroupIsWork)); 204 | logger.debug.groupCollapsed('B collapsed', ({ debug }: LoggerService) => debug(debugGroupIsWork)); 205 | logger.info.groupCollapsed('C collapsed', ({ info }: LoggerService) => info(infoGroupIsWork)); 206 | logger.warn.groupCollapsed('D collapsed', ({ warn }: LoggerService) => warn(warnGroupIsWork)); 207 | logger.error.groupCollapsed('E collapsed', ({ error }: LoggerService) => error(errorGroupIsWork)); 208 | 209 | expect(fakeConsole.stack()).toEqual( 210 | fakeConsole.createStack( 211 | { [TestLoggerGroupType.GROUP_OPEN]: [`A opened`] }, 212 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceGroupIsWork] }, 213 | { [TestLoggerGroupType.GROUP_END]: [] }, 214 | 215 | { [TestLoggerGroupType.GROUP_OPEN]: [`B opened`] }, 216 | { [TestLoggerLineType.DEBUG]: [debugGroupIsWork] }, 217 | { [TestLoggerGroupType.GROUP_END]: [] }, 218 | 219 | { [TestLoggerGroupType.GROUP_OPEN]: [`C opened`] }, 220 | { [TestLoggerLineType.INFO]: [infoGroupIsWork] }, 221 | { [TestLoggerGroupType.GROUP_END]: [] }, 222 | 223 | { [TestLoggerGroupType.GROUP_OPEN]: [`D opened`] }, 224 | { [TestLoggerLineType.WARN]: [warnGroupIsWork] }, 225 | { [TestLoggerGroupType.GROUP_END]: [] }, 226 | 227 | { [TestLoggerGroupType.GROUP_OPEN]: [`E opened`] }, 228 | { [TestLoggerLineType.ERROR]: [errorGroupIsWork] }, 229 | { [TestLoggerGroupType.GROUP_END]: [] }, 230 | 231 | { [TestLoggerGroupType.GROUP_COLLAPSED_OPEN]: [`C collapsed`] }, 232 | { [TestLoggerLineType.INFO]: [infoGroupIsWork] }, 233 | { [TestLoggerGroupType.GROUP_END]: [] }, 234 | 235 | { [TestLoggerGroupType.GROUP_COLLAPSED_OPEN]: [`D collapsed`] }, 236 | { [TestLoggerLineType.WARN]: [warnGroupIsWork] }, 237 | { [TestLoggerGroupType.GROUP_END]: [] }, 238 | 239 | { [TestLoggerGroupType.GROUP_COLLAPSED_OPEN]: [`E collapsed`] }, 240 | { [TestLoggerLineType.ERROR]: [errorGroupIsWork] }, 241 | { [TestLoggerGroupType.GROUP_END]: [] } 242 | ) 243 | ); 244 | }); 245 | 246 | it(`Level groups with pretty pipes`, () => { 247 | logger.level = LoggerLevel.INFO; 248 | 249 | logger.trace 250 | .group('A') 251 | .pipe(({ trace }: LoggerService) => trace('trace is worked from A')) 252 | .pipe(({ debug }: LoggerService) => debug('debug is worked from A')) 253 | .pipe(({ info }: LoggerService) => info('info is worked from A')) 254 | .pipe(({ warn }: LoggerService) => warn('warn is worked from A')) 255 | .pipe(({ error }: LoggerService) => error('error is worked from A')) 256 | .close() 257 | 258 | .debug.group('B') 259 | .pipe(({ trace }: LoggerService) => trace('trace is worked from B')) 260 | .pipe(({ debug }: LoggerService) => debug('debug is worked from B')) 261 | .pipe(({ info }: LoggerService) => info('info is worked from B')) 262 | .pipe(({ warn }: LoggerService) => warn('warn is worked from B')) 263 | .pipe(({ error }: LoggerService) => error('error is worked from B')) 264 | .close() 265 | 266 | .info.group('C') 267 | .pipe(({ trace }: LoggerService) => trace('trace is worked from C')) 268 | .pipe(({ debug }: LoggerService) => debug('debug is worked from C')) 269 | .pipe(({ info }: LoggerService) => info('info is worked from C')) 270 | .pipe(({ warn }: LoggerService) => warn('warn is worked from C')) 271 | .pipe(({ error }: LoggerService) => error('error is worked from C')) 272 | .close() 273 | 274 | .warn.group('D') 275 | .pipe(({ trace }: LoggerService) => trace('trace is worked from D')) 276 | .pipe(({ debug }: LoggerService) => debug('debug is worked from D')) 277 | .pipe(({ info }: LoggerService) => info('info is worked from D')) 278 | .pipe(({ warn }: LoggerService) => warn('warn is worked from D')) 279 | .pipe(({ error }: LoggerService) => error('error is worked from D')) 280 | .close() 281 | 282 | .error.group('E') 283 | .pipe(({ trace }: LoggerService) => trace('trace is worked from E')) 284 | .pipe(({ debug }: LoggerService) => debug('debug is worked from E')) 285 | .pipe(({ info }: LoggerService) => info('info is worked from E')) 286 | .pipe(({ warn }: LoggerService) => warn('warn is worked from E')) 287 | .pipe(({ error }: LoggerService) => error('error is worked from E')) 288 | .close(); 289 | 290 | expect(fakeConsole.stack()).toEqual( 291 | fakeConsole.createStack( 292 | { [TestLoggerGroupType.GROUP_OPEN]: [`C`] }, 293 | { [TestLoggerLineType.INFO]: ['info is worked from C'] }, 294 | { [TestLoggerLineType.WARN]: ['warn is worked from C'] }, 295 | { [TestLoggerLineType.ERROR]: ['error is worked from C'] }, 296 | { [TestLoggerGroupType.GROUP_END]: [] }, 297 | 298 | { [TestLoggerGroupType.GROUP_OPEN]: [`D`] }, 299 | { [TestLoggerLineType.INFO]: ['info is worked from D'] }, 300 | { [TestLoggerLineType.WARN]: ['warn is worked from D'] }, 301 | { [TestLoggerLineType.ERROR]: ['error is worked from D'] }, 302 | { [TestLoggerGroupType.GROUP_END]: [] }, 303 | 304 | { [TestLoggerGroupType.GROUP_OPEN]: [`E`] }, 305 | { [TestLoggerLineType.INFO]: ['info is worked from E'] }, 306 | { [TestLoggerLineType.WARN]: ['warn is worked from E'] }, 307 | { [TestLoggerLineType.ERROR]: ['error is worked from E'] }, 308 | { [TestLoggerGroupType.GROUP_END]: [] } 309 | ) 310 | ); 311 | }); 312 | }); 313 | 314 | describe('[TEST]: ConsoleService based', () => { 315 | const fakeConsole: ConsoleFake = new ConsoleFake(); 316 | let logger: LoggerService; 317 | 318 | beforeAll(() => { 319 | TestBed.configureTestingModule({ 320 | imports: [ 321 | LoggerModule.forRoot({ 322 | instance: fakeConsole, 323 | useLevelGroup: false 324 | }) 325 | ] 326 | }); 327 | 328 | logger = TestBed.get(LoggerService); 329 | }); 330 | 331 | it(`should be throw logger`, () => { 332 | try { 333 | logger.info.group('hello world'); 334 | } catch (e) { 335 | expect(e.message).toEqual('logger.info.group is not a function'); 336 | } 337 | }); 338 | }); 339 | -------------------------------------------------------------------------------- /integration/tests/console.spec.ts: -------------------------------------------------------------------------------- 1 | import { ConsoleFake } from './helpers/console-fake'; 2 | import { TestBed } from '@angular/core/testing'; 3 | import { LoggerModule } from '../../lib/src/logger.module'; 4 | import { ConsoleService } from '../../lib/src/services/console.service'; 5 | import { LoggerService } from '../../lib/src/logger.service'; 6 | import { LoggerLevel } from '../../lib/src/interfaces/logger.external'; 7 | 8 | describe('[TEST]: ConsoleService', () => { 9 | let consoleInternal: ConsoleService; 10 | const fakeConsole: ConsoleFake = new ConsoleFake(); 11 | 12 | beforeAll(() => { 13 | TestBed.configureTestingModule({ 14 | imports: [ 15 | LoggerModule.forRoot({ 16 | instance: fakeConsole 17 | }) 18 | ] 19 | }); 20 | 21 | consoleInternal = TestBed.get(ConsoleService); 22 | }); 23 | 24 | it(`check console instance`, () => { 25 | consoleInternal.console = console; 26 | expect(consoleInternal.console).toEqual(console); 27 | }); 28 | }); 29 | 30 | describe('[TEST]: ConsoleService without options', () => { 31 | let logger: LoggerService; 32 | let consoleService: ConsoleService; 33 | 34 | beforeAll(() => { 35 | TestBed.configureTestingModule({ 36 | imports: [LoggerModule.forRoot()] 37 | }); 38 | 39 | logger = TestBed.get(LoggerService); 40 | consoleService = TestBed.get(ConsoleService); 41 | }); 42 | 43 | it(`should be truthy logger`, () => { 44 | expect(logger).toBeTruthy(); 45 | }); 46 | 47 | it(`should be correct minLevel and instance`, () => { 48 | expect(consoleService.minLevel).toEqual(LoggerLevel.ALL); 49 | expect(consoleService.instance).toEqual(console); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /integration/tests/css-parcer.spec.ts: -------------------------------------------------------------------------------- 1 | import { LoggerService } from '../../lib/src/logger.service'; 2 | import { ConsoleFake, TestLoggerLineType } from './helpers/console-fake'; 3 | import { TestBed } from '@angular/core/testing'; 4 | import { LoggerModule } from '../../lib/src/logger.module'; 5 | import { LoggerLevel } from '../../lib/src/interfaces/logger.external'; 6 | 7 | describe('[TEST]: Check style', () => { 8 | let logger: LoggerService; 9 | const fakeConsole: ConsoleFake = new ConsoleFake(); 10 | 11 | const testString: string = 'test string'; 12 | 13 | beforeAll(() => { 14 | TestBed.configureTestingModule({ 15 | imports: [ 16 | LoggerModule.forRoot({ 17 | instance: fakeConsole, 18 | cssClassMap: { 19 | // tslint:disable-next-line:no-duplicate-string 20 | 'class-1': 'font-weight: bold', 21 | // tslint:disable-next-line:no-duplicate-string 22 | 'class-2': 'text-decoration: line-through', 23 | 'class-3': 'color: #666' 24 | } 25 | }) 26 | ] 27 | }); 28 | 29 | logger = TestBed.get(LoggerService); 30 | }); 31 | 32 | beforeEach(() => logger.clear()); 33 | 34 | it(`Set style another console line `, () => { 35 | logger.level = LoggerLevel.ALL; 36 | 37 | logger.css('color: red; text-decoration: underline; font-weight: bold').info(`It's awesome`); 38 | 39 | expect(fakeConsole.stack()).toEqual( 40 | fakeConsole.createStack({ 41 | [TestLoggerLineType.INFO]: [ 42 | 'color: red; text-decoration: underline; font-weight: bold;', 43 | `It's awesome` 44 | ] 45 | }) 46 | ); 47 | }); 48 | 49 | it('Add css class', () => { 50 | logger.cssClass('class-1 class-3').log('Hello world'); 51 | 52 | expect(fakeConsole.stack()).toEqual( 53 | fakeConsole.createStack({ 54 | [TestLoggerLineType.LOG]: ['%c%s', 'font-weight: bold; color: #666;', 'Hello world'] 55 | }) 56 | ); 57 | 58 | logger.clear(); 59 | 60 | logger.cssClass('class-2').debug('Test 2'); 61 | expect(fakeConsole.stack()).toEqual( 62 | fakeConsole.createStack({ [TestLoggerLineType.DEBUG]: ['text-decoration: line-through;', 'Test 2'] }) 63 | ); 64 | }); 65 | 66 | it('Clear line style', () => { 67 | // with style 68 | logger.css('font-weight: bold'); 69 | expect(logger.getCurrentLineStyle()).toEqual('font-weight: bold;'); 70 | 71 | // without style 72 | logger.css('font-weight: bold'); 73 | logger.clearCssCurrentLine(); 74 | expect(logger.getCurrentLineStyle()).toEqual(''); 75 | }); 76 | 77 | it('Get current line style', () => { 78 | logger.css('text-transform: uppercase, font-weight: bold, font-size: 12px, margin: 10px, padding: 10px'); 79 | 80 | expect(logger.getCurrentLineStyle()).toEqual( 81 | 'text-transform: uppercase, font-weight: bold, font-size: 12px, margin: 10px, padding: 10px;' 82 | ); 83 | }); 84 | 85 | it('should work with empty cssClass', () => { 86 | logger.cssClass('').debug(testString); 87 | 88 | expect(logger.getCurrentLineStyle()).toEqual(''); 89 | }); 90 | }); 91 | 92 | describe('[TEST]: Check global styles', () => { 93 | let logger: LoggerService; 94 | const fakeConsole: ConsoleFake = new ConsoleFake(); 95 | const testString: string = 'test string'; 96 | 97 | beforeAll(() => { 98 | TestBed.configureTestingModule({ 99 | imports: [ 100 | LoggerModule.forRoot({ 101 | instance: fakeConsole, 102 | globalLineStyle: 'color: violet; font-weight: bold; font-size: 12px' 103 | }) 104 | ] 105 | }); 106 | 107 | logger = TestBed.get(LoggerService); 108 | }); 109 | 110 | beforeEach(() => logger.clear()); 111 | 112 | it('should use global styles', () => { 113 | logger.log(testString); 114 | expect(fakeConsole.stack()).toEqual( 115 | '[{"log":["%c%s","color: violet; font-weight: bold; font-size: 12px;","test string"]}]' 116 | ); 117 | }); 118 | 119 | it('should use global styles and work with empty css', () => { 120 | logger.css('').log(testString); 121 | expect(fakeConsole.stack()).toEqual( 122 | '[{"log":["%c%s","color: violet; font-weight: bold; font-size: 12px;","test string"]}]' 123 | ); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /integration/tests/decorators.spec.ts: -------------------------------------------------------------------------------- 1 | import { ConsoleFake, TestLoggerLineType } from './helpers/console-fake'; 2 | import { LoggerModule } from '../../lib/src/logger.module'; 3 | import { LoggerService } from '../../lib/src/logger.service'; 4 | import { MyTestComponent } from './helpers/test.component'; 5 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 6 | import { LoggerLevel } from '../../lib/src/interfaces/logger.external'; 7 | import { Fn } from '../../lib/src/interfaces/logger.internal'; 8 | 9 | describe('[TEST]: Decorator API', () => { 10 | let logger: LoggerService; 11 | const fakeConsole: ConsoleFake = new ConsoleFake(); 12 | const logIsWork: string = 'log is worked'; 13 | const traceIsWork: string = 'trace is worked'; 14 | const debugIsWork: string = 'debug is worked'; 15 | const infoIsWork: string = 'info is worked'; 16 | const warnIsWork: string = 'warn is worked'; 17 | const errorIsWork: string = 'error is worked'; 18 | const groupIsWork: string = 'group is worked'; 19 | const groupCollapsedIsWork: string = 'groupCollapsed is worked'; 20 | 21 | let fixture: ComponentFixture; 22 | let component: MyTestComponent; 23 | 24 | beforeEach(() => { 25 | TestBed.configureTestingModule({ 26 | imports: [LoggerModule.forRoot({ instance: fakeConsole })], 27 | declarations: [MyTestComponent] 28 | }).compileComponents(); 29 | 30 | fixture = TestBed.createComponent(MyTestComponent); 31 | component = fixture.componentInstance; 32 | logger = TestBed.get(LoggerService); 33 | logger.clear(); 34 | }); 35 | 36 | it('Logger decorator should correct work', () => { 37 | component.logger.log('Hello world'); 38 | expect(fakeConsole.stack()).toEqual(fakeConsole.createStack({ [TestLoggerLineType.LOG]: ['Hello world'] })); 39 | }); 40 | 41 | it('Group decorator should correct work', () => { 42 | const result: string = component.print(groupIsWork); 43 | 44 | expect(result).toEqual(groupIsWork); 45 | expect(fakeConsole.stack()).toEqual( 46 | fakeConsole.createStack({ group_open: ['Test group'] }, { log: ['group is worked'] }, { group_end: [] }) 47 | ); 48 | }); 49 | 50 | it('Group decorator with level should correct work', () => { 51 | const result: string = component.printLevel(groupIsWork); 52 | 53 | expect(result).toEqual(groupIsWork); 54 | expect(fakeConsole.stack()).toEqual( 55 | fakeConsole.createStack({ group_open: ['Test group'] }, { log: [groupIsWork] }, { group_end: [] }) 56 | ); 57 | }); 58 | 59 | it('GroupCollapced decorator should correct work', () => { 60 | const result: string = component.printCollapsed(groupCollapsedIsWork); 61 | 62 | expect(result).toEqual(groupCollapsedIsWork); 63 | expect(fakeConsole.stack()).toEqual( 64 | fakeConsole.createStack( 65 | { group_collapsed_open: ['Test group-collapsed'] }, 66 | { log: [groupCollapsedIsWork] }, 67 | { group_end: [] } 68 | ) 69 | ); 70 | }); 71 | 72 | it('GroupCollapsed decorator with level should correct work', () => { 73 | const result: string = component.printCollapsedLevel(groupCollapsedIsWork); 74 | 75 | expect(result).toEqual(groupCollapsedIsWork); 76 | expect(fakeConsole.stack()).toEqual( 77 | fakeConsole.createStack( 78 | { group_collapsed_open: ['Test group-collapsed'] }, 79 | { log: [groupCollapsedIsWork] }, 80 | { group_end: [] } 81 | ) 82 | ); 83 | }); 84 | 85 | it('Method decorators should correct work', () => { 86 | component.log(logIsWork); 87 | component.trace(traceIsWork); 88 | component.debug(debugIsWork); 89 | component.info(infoIsWork); 90 | component.error(errorIsWork); 91 | component.warn(warnIsWork); 92 | expect(fakeConsole.stack()).toEqual( 93 | fakeConsole.createStack( 94 | { [TestLoggerLineType.LOG]: [logIsWork] }, 95 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork] }, 96 | { [TestLoggerLineType.DEBUG]: [debugIsWork] }, 97 | { [TestLoggerLineType.INFO]: [infoIsWork] }, 98 | { [TestLoggerLineType.ERROR]: [errorIsWork] }, 99 | { [TestLoggerLineType.WARN]: [warnIsWork] } 100 | ) 101 | ); 102 | }); 103 | 104 | it('should be correct invoke methods', () => { 105 | component.init(); 106 | expect(component.count).toEqual(1); 107 | expect(fakeConsole.stack()).toEqual('[]'); 108 | }); 109 | 110 | it('should be correct title methods', () => { 111 | const result: string = component.method('hello world'); 112 | 113 | expect(result).toEqual('hello world'); 114 | 115 | expect(fakeConsole.stack()).toEqual( 116 | fakeConsole.createStack( 117 | { group_open: ['Test group with hello world'] }, 118 | { log: [groupIsWork] }, 119 | { group_end: [] } 120 | ) 121 | ); 122 | }); 123 | 124 | it('timer invoke', () => { 125 | logger.level = LoggerLevel.ALL; 126 | component.ngOnInit(); 127 | expect(fakeConsole.stack().includes('TimerLog: mock:ngOnInit')).toEqual(true); 128 | }); 129 | 130 | it('can not execute', () => { 131 | logger.level = LoggerLevel.ERROR; 132 | component.ngOnInit(); 133 | expect(fakeConsole.stack()).toEqual(fakeConsole.createStack()); 134 | }); 135 | 136 | it('query by second timer', (done: Fn) => { 137 | component.longQueryBySecond(3, done); 138 | expect(fakeConsole.stack()).toEqual( 139 | fakeConsole.createStack({ info: ['TimerLog: longQueryBySecond', 'took 3s to execute'] }) 140 | ); 141 | }); 142 | 143 | it('query by ms timer', (done: Fn) => { 144 | component.longQueryBySecondMs(3, done); 145 | expect(fakeConsole.stack().includes('TimerLog: longQueryBySecondMs')).toEqual(true); 146 | }); 147 | 148 | it('should correct work with errors', () => { 149 | try { 150 | component.badRequest(); 151 | } catch (e) { 152 | expect(e.message).toEqual('error'); 153 | } 154 | }); 155 | }); 156 | -------------------------------------------------------------------------------- /integration/tests/helpers/console-fake.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { ObjectKeyMap } from '../../../lib/src/interfaces/logger.internal'; 3 | 4 | export enum TestLoggerLineType { 5 | TABLE = 'table', 6 | ASSERT = 'assert', 7 | TRACE_OR_DEBUG = 'debug', 8 | LOG = 'log', 9 | DEBUG = 'info', 10 | INFO = 'info', 11 | WARN = 'warn', 12 | ERROR = 'error' 13 | } 14 | 15 | export enum TestLoggerGroupType { 16 | GROUP_OPEN = 'group_open', 17 | GROUP_COLLAPSED_OPEN = 'group_collapsed_open', 18 | GROUP_END = 'group_end' 19 | } 20 | 21 | export class ConsoleFake implements Console { 22 | // tslint:disable-next-line:no-any 23 | public Console: any; 24 | private _stack: ObjectKeyMap[] = []; 25 | 26 | public log(...args: string[]): void { 27 | args.unshift(null!, null!); 28 | this._stack.push({ [TestLoggerLineType.LOG]: args }); 29 | } 30 | 31 | public debug(...args: string[]): void { 32 | this._stack.push({ [TestLoggerLineType.TRACE_OR_DEBUG]: args }); 33 | } 34 | 35 | public info(...args: string[]): void { 36 | this._stack.push({ [TestLoggerLineType.INFO]: args }); 37 | } 38 | 39 | public assert(condition: boolean, output: string): void { 40 | if (!condition) { 41 | this._stack.push({ [TestLoggerLineType.ASSERT]: [output] }); 42 | } 43 | } 44 | 45 | public table(data: unknown): void { 46 | this._stack.push({ [TestLoggerLineType.TABLE]: [data] }); 47 | } 48 | 49 | public warn(...args: string[]): void { 50 | this._stack.push({ [TestLoggerLineType.WARN]: args }); 51 | } 52 | 53 | public error(...args: string[]): void { 54 | this._stack.push({ [TestLoggerLineType.ERROR]: args }); 55 | } 56 | 57 | public group(...args: string[]): void { 58 | this._stack.push({ [TestLoggerGroupType.GROUP_OPEN]: args }); 59 | } 60 | 61 | public groupCollapsed(...args: string[]): void { 62 | this._stack.push({ [TestLoggerGroupType.GROUP_COLLAPSED_OPEN]: args }); 63 | } 64 | 65 | public groupEnd(): void { 66 | this._stack.push({ [TestLoggerGroupType.GROUP_END]: [] }); 67 | } 68 | 69 | public createStack(...args: ObjectKeyMap[]): string { 70 | return JSON.stringify(args); 71 | } 72 | 73 | public stack(withoutLabel: number = 2): string { 74 | const history: ObjectKeyMap = [...this._stack]; 75 | history.forEach((line: object, index: number) => { 76 | for (const arg in line) { 77 | if (line.hasOwnProperty(arg)) { 78 | const isArray: boolean = Array.isArray((line as any)[arg]); 79 | history[index] = { [arg]: isArray ? (line as any)[arg].slice(withoutLabel) : (line as any)[arg] }; 80 | } 81 | } 82 | }); 83 | 84 | return JSON.stringify(history); 85 | } 86 | 87 | public stackList(stack: string): string[] { 88 | const stackObject: ObjectKeyMap = JSON.parse(stack); 89 | const stackList: string[] = []; 90 | 91 | stackObject.forEach((line: string[]) => { 92 | for (const levelLog in line) { 93 | if (line.hasOwnProperty(levelLog)) { 94 | stackList.push(line[levelLog]); 95 | } 96 | } 97 | }); 98 | 99 | return stackList; 100 | } 101 | public stackOptionsList(usageNext: boolean = false): ObjectKeyMap { 102 | const stackList: string[] = this.stackList(this.stack(0)); 103 | const stackOptionsList: ObjectKeyMap = []; 104 | 105 | stackList.forEach((line: string) => { 106 | stackOptionsList.push({ 107 | label: String(line[0]).replace('%c', ''), 108 | styles: this.parseCssString(line[usageNext ? 2 : 1]) 109 | }); 110 | }); 111 | 112 | return stackOptionsList; 113 | } 114 | 115 | private parseCssString(css: string): ObjectKeyMap { 116 | const result: ObjectKeyMap = {}; 117 | const attributes: string[] = css.split(';'); 118 | 119 | attributes.forEach((attribute: string) => { 120 | const entry: string[] = attribute.split(':'); 121 | const property: string = String(entry.splice(0, 1)[0]).trim(); 122 | const options: string = entry.join(':').trim(); 123 | if (property.length) { 124 | result[property] = options; 125 | } 126 | }); 127 | 128 | return result; 129 | } 130 | 131 | public clear(): void { 132 | this._stack = []; 133 | } 134 | 135 | public count(): void { 136 | // noop; 137 | } 138 | 139 | public memory(): void { 140 | // noop; 141 | } 142 | 143 | public dir(): void { 144 | // noop; 145 | } 146 | 147 | public dirxml(): void { 148 | // noop; 149 | } 150 | 151 | public exception(): void { 152 | // noop; 153 | } 154 | 155 | public markTimeline(): void { 156 | // noop; 157 | } 158 | 159 | public profile(): void { 160 | // noop; 161 | } 162 | 163 | public profileEnd(): void { 164 | // noop; 165 | } 166 | 167 | public time(): void { 168 | // noop; 169 | } 170 | 171 | public timeEnd(): void { 172 | // noop; 173 | } 174 | 175 | public timeStamp(): void { 176 | // noop; 177 | } 178 | 179 | public timeline(): void { 180 | // noop; 181 | } 182 | 183 | public timelineEnd(): void { 184 | // noop; 185 | } 186 | 187 | public trace(): void { 188 | // noop; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /integration/tests/helpers/custom-colors.enum.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/naming-convention,no-restricted-syntax 2 | export enum CUSTOM_COLORS { 3 | TRACE = 'BlueViolet', 4 | DEBUG = 'CornflowerBlue', 5 | INFO = 'DarkGreen', 6 | WARN = 'Coral', 7 | ERROR = 'Crimson' 8 | } 9 | 10 | // eslint-disable-next-line no-restricted-syntax,@typescript-eslint/naming-convention 11 | export enum CUSTOM_LABELS { 12 | TRACE = 'trace:', 13 | DEBUG = 'debug:', 14 | INFO = 'info:', 15 | WARN = 'warning:', 16 | ERROR = 'error:' 17 | } 18 | -------------------------------------------------------------------------------- /integration/tests/helpers/test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { DebugLog } from '../../../lib/src/decorators/debug.decorator'; 4 | import { ErrorLog } from '../../../lib/src/decorators/error.decorator'; 5 | import { GroupCollapsed } from '../../../lib/src/decorators/groups/group-collapsed.decorator'; 6 | import { Group } from '../../../lib/src/decorators/groups/group.decorator'; 7 | import { InfoLog } from '../../../lib/src/decorators/info.decorator'; 8 | import { Log } from '../../../lib/src/decorators/log.decorator'; 9 | import { Logger } from '../../../lib/src/decorators/logger.decorator'; 10 | import { TimerLog } from '../../../lib/src/decorators/timer.decorator'; 11 | import { TraceLog } from '../../../lib/src/decorators/trace.decorator'; 12 | import { WarnLog } from '../../../lib/src/decorators/warn.decorator'; 13 | import { LogFn, LoggerLevel, TimerInfo } from '../../../lib/src/interfaces/logger.external'; 14 | import { Fn } from '../../../lib/src/interfaces/logger.internal'; 15 | import { LoggerService } from '../../../lib/src/logger.service'; 16 | 17 | interface HttpDebugInterface { 18 | method: string; 19 | url: string; 20 | queryParams: string; 21 | data: unknown; 22 | body: unknown; 23 | errorData: unknown; 24 | } 25 | 26 | // noinspection AngularMissingOrInvalidDeclarationInModule 27 | @Component({ selector: 'lib-hello-test', template: '' }) 28 | export class MyTestComponent implements OnInit { 29 | @Logger() public logger!: LoggerService; 30 | @TraceLog() public trace!: LogFn; 31 | @DebugLog() public debug!: LogFn; 32 | @InfoLog() public info!: LogFn; 33 | @ErrorLog() public error!: LogFn; 34 | @WarnLog() public warn!: LogFn; 35 | @Log() public log!: LogFn; 36 | 37 | public count: number = 0; 38 | public hook: string | null = null; 39 | public doneHeavy: boolean = false; 40 | public name: string = 'MockLoggerComponent'; 41 | 42 | public static getUrlInfo({ method, url, queryParams }: Partial): string { 43 | const params: string = queryParams ? `?${queryParams}` : ''; 44 | return `[${method}] - ${url}${params}`; 45 | } 46 | 47 | @Group('Test group') 48 | public print(val: string): string { 49 | this.logger.log(val); 50 | return val; 51 | } 52 | 53 | @Group('Test group', LoggerLevel.WARN) 54 | public printLevel(val: string): string { 55 | this.logger.log(val); 56 | return val; 57 | } 58 | 59 | @GroupCollapsed('Test group-collapsed') 60 | public printCollapsed(val: string): string { 61 | this.logger.log(val); 62 | return val; 63 | } 64 | 65 | @GroupCollapsed('Test group-collapsed', LoggerLevel.WARN) 66 | public printCollapsedLevel(val: string): string { 67 | this.logger.log(val); 68 | return val; 69 | } 70 | 71 | public init(): void { 72 | this.logger.level = LoggerLevel.INFO; 73 | this.increment(); 74 | } 75 | 76 | @Group('INCREMENT', LoggerLevel.DEBUG) 77 | public increment(): void { 78 | this.logger.debug('count', this.count); 79 | this.count++; 80 | } 81 | 82 | @Group((name: string): string => `Test group with ${name}`) 83 | public method(name: string): string { 84 | this.logger.log('group is worked'); 85 | return name; 86 | } 87 | 88 | @Group((options: Partial): string => MyTestComponent.getUrlInfo(options)) 89 | public hello(name: string): string { 90 | this.logger.log('group is worked'); 91 | return name; 92 | } 93 | 94 | @TimerLog('mock:ngOnInit') 95 | public ngOnInit(): void { 96 | this.hook = 'ngOnInit'; 97 | } 98 | 99 | @TimerLog('longQueryBySecond', LoggerLevel.INFO, false) 100 | public longQueryBySecond(seconds: number, done: Fn): void { 101 | this.extracted(seconds, done); 102 | } 103 | 104 | public longQueryBySecondMs(seconds: number, done: Fn): void { 105 | const info: TimerInfo | null = this.logger.startTime('longQueryBySecondMs'); 106 | this.extracted(seconds, done); 107 | this.logger.endTime(info); 108 | } 109 | 110 | @TimerLog('badRequest', LoggerLevel.DEBUG, false) 111 | public badRequest(): void { 112 | throw new Error('error'); 113 | } 114 | 115 | private extracted(seconds: number, done: Fn): void { 116 | // eslint-disable-next-line @typescript-eslint/no-magic-numbers 117 | const e: number = new Date().getTime() + seconds * 1000; 118 | while (new Date().getTime() <= e) { 119 | this.doneHeavy = true; 120 | } 121 | done(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /integration/tests/injector.spec.ts: -------------------------------------------------------------------------------- 1 | import { LoggerInjector } from '../../lib/src/logger.injector'; 2 | import { LoggerService } from '../../lib/src/logger.service'; 3 | 4 | describe('[TEST]: Check injector error', () => { 5 | it('should return error', (): void => { 6 | let message: string | null = null; 7 | 8 | try { 9 | LoggerInjector.getInjector().get(LoggerService).log; 10 | } catch (e) { 11 | message = e.message; 12 | } 13 | 14 | expect(message).toEqual(`You've forgotten to import \`LoggerModule\``); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /integration/tests/json.spec.ts: -------------------------------------------------------------------------------- 1 | import { LoggerService } from '../../lib/src/logger.service'; 2 | import { ConsoleFake, TestLoggerLineType } from './helpers/console-fake'; 3 | import { TestBed } from '@angular/core/testing'; 4 | import { LoggerModule } from '../../lib/src/logger.module'; 5 | 6 | describe('[TEST]: Check JSON', () => { 7 | let logger: LoggerService; 8 | const fakeConsole: ConsoleFake = new ConsoleFake(); 9 | 10 | beforeAll(() => { 11 | TestBed.configureTestingModule({ 12 | imports: [LoggerModule.forRoot({ instance: fakeConsole })] 13 | }); 14 | 15 | logger = TestBed.get(LoggerService); 16 | }); 17 | 18 | it('should be pretty json', () => { 19 | logger.clear(); 20 | logger.log(logger.prettyJSON({ a: true, b: [1, 2], c: 'test string', d: null })); 21 | 22 | expect(fakeConsole.stack()).toEqual( 23 | fakeConsole.createStack({ 24 | [TestLoggerLineType.LOG]: [ 25 | [ 26 | // tslint:disable-next-line:max-line-length 27 | '{\n %c"a":%c %ctrue%c,\n %c"b":%c [\n %c1%c,\n %c2%c\n ],\n %c"c":%c %c"test string"%c,\n %c"d":%c %cnull%c\n}', 28 | 'color:red', 29 | '', 30 | 'color:blue', 31 | '', 32 | 'color:red', 33 | '', 34 | 'color:darkorange', 35 | '', 36 | 'color:darkorange', 37 | '', 38 | 'color:red', 39 | '', 40 | 'color:green', 41 | '', 42 | 'color:red', 43 | '', 44 | 'color:magenta', 45 | '' 46 | ] 47 | ] 48 | }) 49 | ); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /integration/tests/logger.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { LoggerModule } from '../../lib/src/logger.module'; 3 | import { LoggerService } from '../../lib/src/logger.service'; 4 | import { ConsoleFake, TestLoggerLineType } from './helpers/console-fake'; 5 | import { LoggerLevel } from '../../lib/src/interfaces/logger.external'; 6 | import { ObjectKeyMap } from '../../lib/src/interfaces/logger.internal'; 7 | 8 | describe('[TEST]: Execute method by Level', () => { 9 | let logger: LoggerService; 10 | const fakeConsole: ConsoleFake = new ConsoleFake(); 11 | 12 | const traceIsWork: string = 'trace is worked'; 13 | const debugIsWork: string = 'debug is worked'; 14 | const infoIsWork: string = 'info is worked'; 15 | const warnIsWork: string = 'warn is worked'; 16 | const errorIsWork: string = 'error is worked'; 17 | const customLogOutput: string = 'custom log output'; 18 | 19 | beforeAll(() => { 20 | TestBed.configureTestingModule({ 21 | imports: [LoggerModule.forRoot({ instance: fakeConsole })] 22 | }); 23 | 24 | logger = TestBed.get(LoggerService); 25 | }); 26 | 27 | beforeEach(() => logger.clear()); 28 | 29 | it(`All data must go to the console, minimal level: TRACE`, () => { 30 | logger.level = LoggerLevel.TRACE; 31 | 32 | logger.log(customLogOutput); 33 | logger.trace(traceIsWork, 1, { a: 1 }); 34 | logger.debug(debugIsWork, 2, {}); 35 | logger.info(infoIsWork, 3, Object); 36 | logger.warn(warnIsWork, 4, String); 37 | logger.error(errorIsWork, 5, (2.55).toFixed()); 38 | 39 | expect(fakeConsole.stack()).toEqual( 40 | fakeConsole.createStack( 41 | { [TestLoggerLineType.LOG]: [customLogOutput] }, 42 | { [TestLoggerLineType.TRACE_OR_DEBUG]: [traceIsWork, 1, { a: 1 }] }, 43 | { [TestLoggerLineType.DEBUG]: [debugIsWork, 2, {}] }, 44 | { [TestLoggerLineType.INFO]: [infoIsWork, 3, Object] }, 45 | { [TestLoggerLineType.WARN]: [warnIsWork, 4, String] }, 46 | { [TestLoggerLineType.ERROR]: [errorIsWork, 5, (2.55).toFixed()] } 47 | ) 48 | ); 49 | }); 50 | 51 | it(`Show console stack when minimal level: DEBUG`, () => { 52 | logger.level = LoggerLevel.DEBUG; 53 | 54 | logger.log(customLogOutput); 55 | logger.trace(traceIsWork, 1, { a: 1 }); 56 | logger.debug(debugIsWork, 2, { b: 2 }); 57 | logger.info(infoIsWork, 3, Object); 58 | logger.warn(warnIsWork, 4, String); 59 | logger.error(errorIsWork, 5, (2.55).toFixed()); 60 | 61 | expect(fakeConsole.stack()).toEqual( 62 | fakeConsole.createStack( 63 | { [TestLoggerLineType.LOG]: [customLogOutput] }, 64 | { [TestLoggerLineType.DEBUG]: [debugIsWork, 2, { b: 2 }] }, 65 | { [TestLoggerLineType.INFO]: [infoIsWork, 3, Object] }, 66 | { [TestLoggerLineType.WARN]: [warnIsWork, 4, String] }, 67 | { [TestLoggerLineType.ERROR]: [errorIsWork, 5, (2.55).toFixed()] } 68 | ) 69 | ); 70 | }); 71 | 72 | it(`Show console stack when minimal level: DEBUG`, () => { 73 | logger.level = LoggerLevel.DEBUG; 74 | 75 | logger.log(customLogOutput); 76 | logger.trace(traceIsWork, 1, { a: 1 }); 77 | logger.debug(debugIsWork, 2, {}); 78 | logger.info(infoIsWork, 3, Object); 79 | logger.warn(warnIsWork, 4, String); 80 | logger.error(errorIsWork, 5, (2.55).toFixed()); 81 | 82 | expect(fakeConsole.stack()).toEqual( 83 | fakeConsole.createStack( 84 | { [TestLoggerLineType.LOG]: [customLogOutput] }, 85 | { [TestLoggerLineType.DEBUG]: [debugIsWork, 2, {}] }, 86 | { [TestLoggerLineType.INFO]: [infoIsWork, 3, Object] }, 87 | { [TestLoggerLineType.WARN]: [warnIsWork, 4, String] }, 88 | { [TestLoggerLineType.ERROR]: [errorIsWork, 5, (2.55).toFixed()] } 89 | ) 90 | ); 91 | }); 92 | 93 | it(`Show console stack when minimal level: INFO`, () => { 94 | logger.level = LoggerLevel.INFO; 95 | 96 | logger.log(customLogOutput); 97 | logger.trace(traceIsWork, 1, { a: 1 }); 98 | logger.debug(debugIsWork, 2, console); 99 | logger.info(infoIsWork, 3, Object); 100 | logger.warn(warnIsWork, 4, String); 101 | logger.error(errorIsWork, 5, (2.55).toFixed()); 102 | 103 | expect(fakeConsole.stack()).toEqual( 104 | fakeConsole.createStack( 105 | { [TestLoggerLineType.LOG]: [customLogOutput] }, 106 | { [TestLoggerLineType.INFO]: [infoIsWork, 3, Object] }, 107 | { [TestLoggerLineType.WARN]: [warnIsWork, 4, String] }, 108 | { [TestLoggerLineType.ERROR]: [errorIsWork, 5, (2.55).toFixed()] } 109 | ) 110 | ); 111 | }); 112 | 113 | it(`Show console stack when minimal level: WARNING`, () => { 114 | logger.level = LoggerLevel.WARN; 115 | 116 | logger.log(customLogOutput); 117 | logger.trace(traceIsWork, 1, { a: 1 }); 118 | logger.debug(debugIsWork, 2, console); 119 | logger.info(infoIsWork, 3, Object); 120 | logger.warn(warnIsWork, 4, String); 121 | logger.error(errorIsWork, 5, (2.55).toFixed()); 122 | 123 | expect(fakeConsole.stack()).toEqual( 124 | fakeConsole.createStack( 125 | { [TestLoggerLineType.WARN]: [warnIsWork, 4, String] }, 126 | { [TestLoggerLineType.ERROR]: [errorIsWork, 5, (2.55).toFixed()] } 127 | ) 128 | ); 129 | }); 130 | 131 | it(`Show console stack when minimal level: ERROR`, () => { 132 | logger.level = LoggerLevel.ERROR; 133 | 134 | logger.log(customLogOutput); 135 | logger.trace(traceIsWork, 1, { a: 1 }); 136 | logger.debug(debugIsWork, 2, console); 137 | logger.info(infoIsWork, 3, Object); 138 | logger.warn(warnIsWork, 4, String); 139 | logger.error(errorIsWork, 5, (2.55).toFixed()); 140 | 141 | expect(fakeConsole.stack()).toEqual( 142 | fakeConsole.createStack({ [TestLoggerLineType.ERROR]: ['error is worked', 5, (2.55).toFixed()] }) 143 | ); 144 | }); 145 | 146 | it(`Not showing data in console, level: OFF`, () => { 147 | logger.level = LoggerLevel.OFF; 148 | 149 | logger.log(customLogOutput); 150 | logger.trace(traceIsWork, 1, { a: 1 }); 151 | logger.debug(debugIsWork, 2, console); 152 | logger.info(infoIsWork, 3, Object); 153 | logger.warn(warnIsWork, 4, String); 154 | logger.error(errorIsWork, 5, (2.55).toFixed()); 155 | 156 | expect(fakeConsole.stack()).toEqual(fakeConsole.createStack()); 157 | }); 158 | 159 | it(`Clear console stack is worked`, () => { 160 | logger.level = LoggerLevel.ALL; 161 | expect(fakeConsole.stack()).toEqual(fakeConsole.createStack()); 162 | }); 163 | 164 | it(`Set minimal level: INFO`, () => { 165 | logger.level = LoggerLevel.INFO; 166 | expect(logger.level).toEqual(LoggerLevel.INFO); 167 | }); 168 | 169 | it(`Assert: 5 is not grater than 6`, () => { 170 | logger.assert(5 > 6, '5 is not grater than 6'); 171 | expect(fakeConsole.stack(0)).toEqual( 172 | fakeConsole.createStack({ [TestLoggerLineType.ASSERT]: [`5 is not grater than 6`] }) 173 | ); 174 | }); 175 | 176 | it(`Assert: 10 is grater than 6`, () => { 177 | logger.assert(10 > 6, '10 is not grater than 6'); 178 | expect(fakeConsole.stack(0)).toEqual(fakeConsole.createStack()); 179 | }); 180 | 181 | it(`Table`, () => { 182 | const data: ObjectKeyMap = [ 183 | { name: 'Yusuf', age: 26 }, 184 | { age: 34, name: 'Chen' } 185 | ]; 186 | 187 | logger.table(data); 188 | expect(fakeConsole.stack(0)).toEqual(fakeConsole.createStack({ [TestLoggerLineType.TABLE]: [data] })); 189 | }); 190 | 191 | it('should be equals reference context (this)', () => { 192 | const { pipe }: LoggerService = logger; 193 | expect(pipe() === logger).toEqual(true); 194 | }); 195 | }); 196 | -------------------------------------------------------------------------------- /integration/tests/setupJest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular'; 2 | -------------------------------------------------------------------------------- /integration/tests/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": ["jest", "node"] 6 | }, 7 | "include": ["**/*.spec.ts", "**/*.d.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const { createTsJestConfig } = require('@angular-ru/jest-utils'); 2 | const path = require('path'); 3 | 4 | module.exports = createTsJestConfig({ 5 | maxWorkers: 2, 6 | maxConcurrency: 2, 7 | displayName: 'Logger', 8 | rootDir: path.resolve('.'), 9 | modulePathIgnorePatterns: ['/dist/'], 10 | collectCoverageFrom: ['/lib/**/*.ts'], 11 | testMatch: ['/integration/tests/**/*.spec.ts'], 12 | tsConfigSpecPath: '/integration/tests/tsconfig.spec.json', 13 | setupFilesAfterEnv: ['/integration/tests/setupJest.ts'], 14 | tsConfigRootPath: path.resolve('./tsconfig.json') 15 | }); 16 | -------------------------------------------------------------------------------- /lib/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { "entryFile": "src/public-api.ts" }, 4 | "dest": "../dist/logger" 5 | } 6 | -------------------------------------------------------------------------------- /lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angular-ru/logger", 3 | "version": "1.11.0", 4 | "license": "MIT", 5 | "homepage": "https://github.com/Angular-RU/angular-logger", 6 | "repository": "https://github.com/Angular-RU/angular-logger", 7 | "peerDependencies": { 8 | "@angular/common": ">=7.0.0", 9 | "@angular/core": ">=7.0.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/decorators/autobind.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Any, Fn, ObjectKeyMap } from '../interfaces/logger.internal'; 2 | 3 | const { defineProperty, getOwnPropertyDescriptor, getOwnPropertyNames, getOwnPropertySymbols }: ObjectKeyMap = Object; 4 | 5 | function bind(fn: Fn, context: Any): Fn { 6 | return fn.bind(context); 7 | } 8 | 9 | function getOwnKeys(descriptors: ObjectKeyMap): string[] { 10 | return getOwnPropertyNames(descriptors).concat(getOwnPropertySymbols(descriptors)); 11 | } 12 | 13 | function autoBindClass(target: ObjectKeyMap): Any { 14 | const descriptors: ObjectKeyMap = getOwnPropertyDescriptors(target.prototype); 15 | const keys: string[] = getOwnKeys(descriptors); 16 | 17 | for (let i: number = 0, l: number = keys.length; i < l; i++) { 18 | const key: string = keys[i]; 19 | const descriptor: Any = descriptors[key]; 20 | 21 | if (typeof descriptor.value !== 'function' || key === 'constructor') { 22 | continue; 23 | } 24 | 25 | defineProperty(target.prototype, key, autoBindMethod(target.prototype, key, descriptor)); 26 | } 27 | } 28 | 29 | function getOwnPropertyDescriptors(target: ObjectKeyMap): ObjectKeyMap { 30 | const descriptors: ObjectKeyMap = {}; 31 | 32 | getOwnKeys(target).forEach((key: string): void => { 33 | descriptors[key] = getOwnPropertyDescriptor(target, key); 34 | }); 35 | 36 | return descriptors; 37 | } 38 | 39 | // eslint-disable-next-line max-lines-per-function 40 | function autoBindMethod( 41 | target: ObjectKeyMap, 42 | key: string, 43 | { value: fn, configurable, enumerable }: Any 44 | ): PropertyDescriptor { 45 | return { 46 | configurable, 47 | enumerable, 48 | 49 | get(): Fn { 50 | if (this === target) { 51 | return fn; 52 | } 53 | 54 | const boundFn: Fn = bind(fn, this); 55 | 56 | defineProperty(this, key, { 57 | configurable: true, 58 | writable: true, 59 | 60 | enumerable: false, 61 | value: boundFn 62 | }); 63 | 64 | return boundFn; 65 | }, 66 | set: createDefaultSetter(key) 67 | }; 68 | } 69 | 70 | function handle(args: Any[]): Any { 71 | if (args.length === 1) { 72 | return autoBindClass(args[0]); 73 | } else { 74 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 75 | // @ts-ignore 76 | return autoBindMethod(...args); 77 | } 78 | } 79 | 80 | export function autoBind(...args: Any[]): Any { 81 | if (args.length === 0) { 82 | return function (...argsClass: Any[]): Fn { 83 | return handle(argsClass); 84 | }; 85 | } else { 86 | return handle(args); 87 | } 88 | } 89 | 90 | function createDefaultSetter(key: Any): Fn { 91 | return function set(this: Any, newValue: unknown): unknown { 92 | Object.defineProperty(this, key, { 93 | configurable: true, 94 | writable: true, 95 | enumerable: true, 96 | value: newValue 97 | }); 98 | 99 | return newValue; 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /lib/src/decorators/debug.decorator.ts: -------------------------------------------------------------------------------- 1 | import { LogFn } from '../interfaces/logger.external'; 2 | import { LoggerInjector } from '../logger.injector'; 3 | import { LoggerService } from '../logger.service'; 4 | 5 | export function DebugLog(): PropertyDecorator { 6 | return (target: unknown, propertyName: string | symbol): void => { 7 | Object.defineProperty(target, propertyName, { 8 | configurable: false, 9 | get(): LogFn { 10 | return LoggerInjector.getInjector().get(LoggerService).debug; 11 | } 12 | }); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/decorators/error.decorator.ts: -------------------------------------------------------------------------------- 1 | import { LogFn } from '../interfaces/logger.external'; 2 | import { LoggerInjector } from '../logger.injector'; 3 | import { LoggerService } from '../logger.service'; 4 | 5 | export function ErrorLog(): PropertyDecorator { 6 | return (target: unknown, propertyName: string | symbol): void => { 7 | Object.defineProperty(target, propertyName, { 8 | configurable: false, 9 | get(): LogFn { 10 | return LoggerInjector.getInjector().get(LoggerService).error; 11 | } 12 | }); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/decorators/groups/group-collapsed.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { GroupLevel, LoggerLevel } from '../../interfaces/logger.external'; 4 | import { Any, Callback, DecoratorMethod, Fn } from '../../interfaces/logger.internal'; 5 | import { groupDecoratorFactory } from './group.common'; 6 | 7 | export function GroupCollapsed(title: string | Callback, level: LoggerLevel = LoggerLevel.INFO): DecoratorMethod { 8 | return (_target: Type, _key: string, descriptor: PropertyDescriptor): PropertyDescriptor => { 9 | const method: Callback = descriptor.value; 10 | 11 | descriptor.value = function (...args: Any[]): unknown { 12 | return groupDecoratorFactory(level, GroupLevel.GROUP_COLLAPSED, method as Fn, title, args, this as Any); 13 | }; 14 | 15 | return descriptor; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/decorators/groups/group.common.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { GroupLevel, GroupMethod, LoggerLevel } from '../../interfaces/logger.external'; 4 | import { Any, Callback, Fn } from '../../interfaces/logger.internal'; 5 | import { LoggerInjector } from '../../logger.injector'; 6 | import { LoggerService } from '../../logger.service'; 7 | import { GroupFactory } from '../../services/group-factory.service'; 8 | 9 | // eslint-disable-next-line max-params 10 | export function groupDecoratorFactory( 11 | level: LoggerLevel, 12 | groupType: GroupLevel, 13 | method: Fn, 14 | title: string | Callback, 15 | args: Any[], 16 | target: Type 17 | ): unknown { 18 | const logger: LoggerService = LoggerInjector.getInjector().get(LoggerService); 19 | const groupFactory: GroupFactory = LoggerInjector.getInjector().get(GroupFactory); 20 | const groupMethod: GroupMethod = groupFactory[groupType].bind(groupFactory) as GroupMethod; 21 | const label: string = typeof title === 'string' ? title : title(...args); 22 | 23 | groupMethod(label, null, logger, level); 24 | const result: unknown = method.apply(target, args); 25 | 26 | if (logger.level <= level) { 27 | logger.close(); 28 | } 29 | 30 | return result; 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/decorators/groups/group.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { GroupLevel, LoggerLevel } from '../../interfaces/logger.external'; 4 | import { Any, Callback, DecoratorMethod, Fn } from '../../interfaces/logger.internal'; 5 | import { groupDecoratorFactory } from './group.common'; 6 | 7 | export function Group(title: string | Callback, level: LoggerLevel = LoggerLevel.INFO): DecoratorMethod { 8 | return (_target: Type, _key: string, descriptor: PropertyDescriptor): PropertyDescriptor => { 9 | const method: Callback = descriptor.value; 10 | 11 | descriptor.value = function (...args: Any[]): unknown { 12 | return groupDecoratorFactory(level, GroupLevel.GROUP, method as Fn, title, args, this as Any); 13 | }; 14 | 15 | return descriptor; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/decorators/info.decorator.ts: -------------------------------------------------------------------------------- 1 | import { LogFn } from '../interfaces/logger.external'; 2 | import { LoggerInjector } from '../logger.injector'; 3 | import { LoggerService } from '../logger.service'; 4 | 5 | export function InfoLog(): PropertyDecorator { 6 | return (target: unknown, propertyName: string | symbol): void => { 7 | Object.defineProperty(target, propertyName, { 8 | configurable: false, 9 | get(): LogFn { 10 | return LoggerInjector.getInjector().get(LoggerService).info; 11 | } 12 | }); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/decorators/log.decorator.ts: -------------------------------------------------------------------------------- 1 | import { LogFn } from '../interfaces/logger.external'; 2 | import { LoggerInjector } from '../logger.injector'; 3 | import { LoggerService } from '../logger.service'; 4 | 5 | export function Log(): PropertyDecorator { 6 | return (target: unknown, propertyName: string | symbol): void => { 7 | Object.defineProperty(target, propertyName, { 8 | configurable: false, 9 | get(): LogFn { 10 | return LoggerInjector.getInjector().get(LoggerService).log; 11 | } 12 | }); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/decorators/logger.decorator.ts: -------------------------------------------------------------------------------- 1 | import { LoggerInjector } from '../logger.injector'; 2 | import { LoggerService } from '../logger.service'; 3 | 4 | export function Logger(): PropertyDecorator { 5 | return (target: unknown, propertyName: string | symbol): void => { 6 | Object.defineProperty(target, propertyName, { 7 | configurable: false, 8 | get(): LoggerService { 9 | return LoggerInjector.getInjector().get(LoggerService); 10 | } 11 | }); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/decorators/timer.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | 3 | import { LoggerLevel, TimerInfo, TimerLevels } from '../interfaces/logger.external'; 4 | import { Any, DecoratorMethod, Fn } from '../interfaces/logger.internal'; 5 | import { LoggerInjector } from '../logger.injector'; 6 | import { LoggerService } from '../logger.service'; 7 | 8 | // eslint-disable-next-line max-lines-per-function 9 | export function TimerLog( 10 | title: string, 11 | level: TimerLevels = LoggerLevel.DEBUG, 12 | isMillisecond: boolean = true 13 | ): DecoratorMethod { 14 | return (_target: Type, _key: string, descriptor: PropertyDescriptor): PropertyDescriptor => { 15 | let result: PropertyDescriptor; 16 | const method: Fn = descriptor.value; 17 | descriptor.value = function (...args: Any[]): PropertyDescriptor { 18 | const info: TimerInfo | null = LoggerInjector.getInjector() 19 | .get(LoggerService) 20 | .startTime(title, level); 21 | 22 | result = method.apply(this, args) as PropertyDescriptor; 23 | 24 | LoggerInjector.getInjector().get(LoggerService).endTime(info!, level, isMillisecond); 25 | return result; 26 | }; 27 | return descriptor; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/decorators/trace.decorator.ts: -------------------------------------------------------------------------------- 1 | import { LogFn } from '../interfaces/logger.external'; 2 | import { LoggerInjector } from '../logger.injector'; 3 | import { LoggerService } from '../logger.service'; 4 | 5 | export function TraceLog(): PropertyDecorator { 6 | return (target: unknown, propertyName: string | symbol): void => { 7 | Object.defineProperty(target, propertyName, { 8 | configurable: false, 9 | get(): LogFn { 10 | return LoggerInjector.getInjector().get(LoggerService).trace; 11 | } 12 | }); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/decorators/warn.decorator.ts: -------------------------------------------------------------------------------- 1 | import { LogFn } from '../interfaces/logger.external'; 2 | import { LoggerInjector } from '../logger.injector'; 3 | import { LoggerService } from '../logger.service'; 4 | 5 | export function WarnLog(): PropertyDecorator { 6 | return (target: unknown, propertyName: string | symbol): void => { 7 | Object.defineProperty(target, propertyName, { 8 | configurable: false, 9 | get(): LogFn { 10 | return LoggerInjector.getInjector().get(LoggerService).warn; 11 | } 12 | }); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/interfaces/logger.external.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { LoggerService } from '../logger.service'; 4 | import { ObjectKeyMap } from './logger.internal'; 5 | 6 | export type ConsoleOperation = (message?: T, ...optionalParams: P[]) => void; 7 | 8 | export interface GroupMethods extends Function { 9 | group(label: string, pipeline?: Pipeline): LoggerService; 10 | 11 | groupCollapsed(label: string, pipeline?: Pipeline): LoggerService; 12 | } 13 | 14 | export const LOGGER_OPTIONS: InjectionToken = new InjectionToken('LOGGER_OPTIONS'); 15 | export type TimerLevels = 16 | | LoggerLevel.TRACE 17 | | LoggerLevel.DEBUG 18 | | LoggerLevel.INFO 19 | | LoggerLevel.WARN 20 | | LoggerLevel.ERROR; 21 | export type LogFn = GroupMethods & ConsoleOperation; 22 | 23 | export interface FormatOutput { 24 | label: string; 25 | style: string; 26 | } 27 | 28 | // eslint-disable-next-line no-restricted-syntax 29 | export enum LoggerLevel { 30 | ALL = 1, 31 | TRACE, 32 | DEBUG, 33 | INFO, 34 | LOG, 35 | WARN, 36 | ERROR, 37 | OFF 38 | } 39 | 40 | // eslint-disable-next-line no-restricted-syntax 41 | export enum GroupLevel { 42 | GROUP = 'group', 43 | GROUP_COLLAPSED = 'groupCollapsed' 44 | } 45 | 46 | export type Pipeline = (logger: LoggerService) => T; 47 | 48 | export interface TimerInfo { 49 | title: string; 50 | startTime: number; 51 | } 52 | 53 | export type PipeOperation = GroupMethods | ConsoleOperation; 54 | export type GroupMethod = (groupTitle?: string, ...optionalParams: T[]) => unknown; 55 | export type GroupFactoryMethod = ( 56 | title: string, 57 | pipeline: Pipeline | undefined, 58 | logger: LoggerService, 59 | level: LoggerLevel 60 | ) => void; 61 | 62 | export interface LoggerOptions { 63 | instance: T extends Console ? T : unknown; 64 | minLevel: LoggerLevel; 65 | globalLineStyle: string; 66 | cssClassMap: object; 67 | useLevelGroup: boolean; 68 | labelColors: ObjectKeyMap; 69 | labelNames: ObjectKeyMap; 70 | 71 | format?(label: string, style: string): FormatOutput; 72 | 73 | options?(config: Partial): LoggerOptions; 74 | } 75 | -------------------------------------------------------------------------------- /lib/src/interfaces/logger.internal.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | export type Any = any; // NOSONAR 3 | export type Fn = (...args: T[]) => U; 4 | export type Arguments = T[]; 5 | 6 | export interface ObjectKeyMap { 7 | [key: string]: T; 8 | } 9 | 10 | export type DecoratorMethod = (target: Any, key: string, descriptor: PropertyDescriptor) => PropertyDescriptor; 11 | 12 | export interface ClipboardData { 13 | setData: (type: string, value: string) => void | boolean; 14 | } 15 | 16 | export type Callback = (...args: T[]) => T; 17 | export type Descriptor = PropertyDescriptor & ThisType; 18 | 19 | export interface ConsoleServiceInterface { 20 | getTemplateWithoutLabel(): string; 21 | } 22 | 23 | export interface Clipboard { 24 | readonly clipboardSetData: SetDataType; 25 | readonly queryCommandCopy: boolean; 26 | 27 | copyOnBuffer(data: unknown): boolean; 28 | 29 | textAreaSelectData(value: string): boolean; 30 | } 31 | 32 | export type SetDataType = (format: string, data: string) => void | boolean; 33 | -------------------------------------------------------------------------------- /lib/src/logger.config.ts: -------------------------------------------------------------------------------- 1 | import { LoggerLevel } from './interfaces/logger.external'; 2 | import { ObjectKeyMap } from './interfaces/logger.internal'; 3 | 4 | // eslint-disable-next-line no-restricted-syntax,@typescript-eslint/naming-convention 5 | export enum LABELS { 6 | TRACE = 'TRACE', 7 | DEBUG = 'DEBUG', 8 | INFO = 'INFO', 9 | WARN = 'WARN', 10 | ERROR = 'ERROR' 11 | } 12 | 13 | // eslint-disable-next-line no-restricted-syntax,@typescript-eslint/naming-convention 14 | export enum COLORS { 15 | TRACE = '#000080', 16 | DEBUG = '#00BFFE', 17 | INFO = '#000000', 18 | WARN = '#FF6419', 19 | ERROR = '#F1062D' 20 | } 21 | 22 | export const DEFAULT_METHODS: ObjectKeyMap = { 23 | /** 24 | * Used `debug` instead `trace` method because need 25 | * output without stack trace in console 26 | * LoggerLevel.TRACE -> console.debug 27 | */ 28 | [LoggerLevel.TRACE]: 'debug', 29 | [LoggerLevel.LOG]: 'log', 30 | [LoggerLevel.DEBUG]: 'info', 31 | [LoggerLevel.INFO]: 'info', 32 | [LoggerLevel.WARN]: 'warn', 33 | [LoggerLevel.ERROR]: 'error' 34 | }; 35 | 36 | // tslint:disable-next-line:max-line-length 37 | export const LexerJSON: RegExp = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g; 38 | -------------------------------------------------------------------------------- /lib/src/logger.injector.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Injector } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class LoggerInjector { 5 | private static injector: Injector | null = null; 6 | 7 | constructor(injector: Injector) { 8 | LoggerInjector.injector = injector; 9 | } 10 | 11 | public static getInjector(): never | Injector { 12 | if (!LoggerInjector.injector) { 13 | throw new Error(`You've forgotten to import \`LoggerModule\``); 14 | } 15 | 16 | return LoggerInjector.injector; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/logger.module.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken, ModuleWithProviders, NgModule, Self } from '@angular/core'; 2 | 3 | import { LOGGER_OPTIONS, LoggerOptions } from './interfaces/logger.external'; 4 | import { LoggerInjector } from './logger.injector'; 5 | import { LoggerOptionsImpl } from './logger.options'; 6 | import { LoggerService } from './logger.service'; 7 | import { ClipboardFactory } from './services/clipboard-factory.service'; 8 | import { ConsoleService } from './services/console.service'; 9 | import { CssFactory } from './services/css-factory.service'; 10 | import { LoggerFactory } from './services/factory.service'; 11 | import { GroupFactory } from './services/group-factory.service'; 12 | import { JsonFactory } from './services/json-factory.service'; 13 | import { TimerFactory } from './services/timer-factory.service'; 14 | 15 | @NgModule({ 16 | providers: [ 17 | LoggerService, 18 | LoggerFactory, 19 | ConsoleService, 20 | GroupFactory, 21 | CssFactory, 22 | JsonFactory, 23 | ClipboardFactory, 24 | TimerFactory, 25 | LoggerInjector 26 | ] 27 | }) 28 | export class LoggerModule { 29 | private static readonly ROOT_OPTIONS: InjectionToken = new InjectionToken('ROOT_OPTIONS'); 30 | 31 | constructor(@Self() public loggerInjector: LoggerInjector) {} 32 | 33 | public static forRoot(config: Partial = {}): ModuleWithProviders { 34 | return { 35 | ngModule: LoggerModule, 36 | providers: [ 37 | { 38 | provide: LoggerModule.ROOT_OPTIONS, 39 | useValue: config 40 | }, 41 | { 42 | provide: LOGGER_OPTIONS, 43 | useFactory: LoggerModule.loggerConfigFactory, 44 | deps: [LoggerModule.ROOT_OPTIONS] 45 | } 46 | ] 47 | }; 48 | } 49 | 50 | private static loggerConfigFactory(config: Partial): LoggerOptionsImpl { 51 | return Object.assign(new LoggerOptionsImpl(), config); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/logger.options.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { FormatOutput, LoggerLevel, LoggerOptions } from './interfaces/logger.external'; 4 | import { ObjectKeyMap } from './interfaces/logger.internal'; 5 | import { COLORS, LABELS } from './logger.config'; 6 | 7 | @Injectable() 8 | export class LoggerOptionsImpl implements LoggerOptions { 9 | public instance: Console = console; 10 | public minLevel: LoggerLevel = LoggerLevel.ALL; 11 | public globalLineStyle: string = ''; 12 | public cssClassMap: ObjectKeyMap = {}; 13 | public useLevelGroup: boolean = true; 14 | public labelColors: ObjectKeyMap = { 15 | [LoggerLevel.TRACE]: COLORS.TRACE, 16 | [LoggerLevel.DEBUG]: COLORS.DEBUG, 17 | [LoggerLevel.INFO]: COLORS.INFO, 18 | [LoggerLevel.WARN]: COLORS.WARN, 19 | [LoggerLevel.ERROR]: COLORS.ERROR 20 | }; 21 | 22 | public labelNames: ObjectKeyMap = { 23 | [LoggerLevel.TRACE]: LABELS.TRACE, 24 | [LoggerLevel.DEBUG]: LABELS.DEBUG, 25 | [LoggerLevel.INFO]: LABELS.INFO, 26 | [LoggerLevel.WARN]: LABELS.WARN, 27 | [LoggerLevel.ERROR]: LABELS.ERROR 28 | }; 29 | 30 | public format(label: string, style: string): FormatOutput { 31 | return { label: `[${label}]:`, style }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@angular/core'; 2 | 3 | import { autoBind } from './decorators/autobind.decorator'; 4 | import { LogFn, LOGGER_OPTIONS, LoggerLevel, Pipeline, TimerInfo } from './interfaces/logger.external'; 5 | import { ObjectKeyMap } from './interfaces/logger.internal'; 6 | import { LoggerOptionsImpl } from './logger.options'; 7 | import { ClipboardFactory } from './services/clipboard-factory.service'; 8 | import { ConsoleService } from './services/console.service'; 9 | import { CssFactory } from './services/css-factory.service'; 10 | import { LoggerFactory } from './services/factory.service'; 11 | import { GroupFactory } from './services/group-factory.service'; 12 | import { JsonFactory } from './services/json-factory.service'; 13 | import { TimerFactory } from './services/timer-factory.service'; 14 | 15 | @autoBind 16 | @Injectable() 17 | export class LoggerService { 18 | private readonly DEFAULT_DEPTH: number = 2; 19 | 20 | // eslint-disable-next-line max-params 21 | constructor( 22 | private readonly clipboard: ClipboardFactory, 23 | private readonly cssFactory: CssFactory, 24 | private readonly console: ConsoleService, 25 | private readonly factory: LoggerFactory, 26 | private readonly groupFactory: GroupFactory, 27 | private readonly jsonFactory: JsonFactory, 28 | private readonly timerFactory: TimerFactory, 29 | @Inject(LOGGER_OPTIONS) private readonly options: LoggerOptionsImpl 30 | ) {} 31 | 32 | public get clear(): LogFn { 33 | return this.console.instance.clear.bind(this.console.instance) as LogFn; 34 | } 35 | 36 | public get table(): LogFn { 37 | return this.console.instance.table.bind(this.console.instance) as LogFn; 38 | } 39 | 40 | public get log(): LogFn { 41 | return this.factory.createLogger(LoggerLevel.LOG, this); 42 | } 43 | 44 | public get trace(): LogFn { 45 | return this.factory.createLogger(LoggerLevel.TRACE, this); 46 | } 47 | 48 | public get assert(): LogFn { 49 | return this.console.instance.assert.bind(this.console.instance) as LogFn; 50 | } 51 | 52 | public get debug(): LogFn { 53 | return this.factory.createLogger(LoggerLevel.DEBUG, this); 54 | } 55 | 56 | public get info(): LogFn { 57 | return this.factory.createLogger(LoggerLevel.INFO, this); 58 | } 59 | 60 | public get warn(): LogFn { 61 | return this.factory.createLogger(LoggerLevel.WARN, this); 62 | } 63 | 64 | public get error(): LogFn { 65 | return this.factory.createLogger(LoggerLevel.ERROR, this); 66 | } 67 | 68 | public get level(): LoggerLevel { 69 | return this.console.minLevel; 70 | } 71 | 72 | public set level(level: LoggerLevel) { 73 | this.console.minLevel = level; 74 | } 75 | 76 | public getCurrentLineStyle(): string { 77 | return this.cssFactory.style; 78 | } 79 | 80 | public clearCssCurrentLine(): void { 81 | this.cssFactory.style = ''; 82 | } 83 | 84 | public setLabels(labels: ObjectKeyMap): void { 85 | this.options.labelNames = { ...this.options.labelNames, ...labels }; 86 | } 87 | 88 | public setColors(colors: ObjectKeyMap): void { 89 | this.options.labelColors = { ...this.options.labelColors, ...colors }; 90 | } 91 | 92 | public pipe(...pipelines: Pipeline[]): LoggerService { 93 | if (this.groupFactory.executePipesGroup) { 94 | pipelines.forEach((pipeline: Pipeline): unknown => pipeline(this)); 95 | } 96 | 97 | return this; 98 | } 99 | 100 | public groupCollapsed(title: string, pipeline?: Pipeline): LoggerService { 101 | this.groupFactory.groupCollapsed(title, pipeline, this, LoggerLevel.INFO); 102 | return this; 103 | } 104 | 105 | public close(): LoggerService { 106 | this.groupFactory.close(); 107 | return this; 108 | } 109 | 110 | public closeAll(): LoggerService { 111 | this.groupFactory.closeAll(); 112 | return this; 113 | } 114 | 115 | public group(title: string, pipeline?: Pipeline): LoggerService { 116 | this.groupFactory.group(title, pipeline, this, LoggerLevel.INFO); 117 | return this; 118 | } 119 | 120 | public css(style: string): LoggerService { 121 | this.cssFactory.style = style; 122 | return this; 123 | } 124 | 125 | public prettyJSON(json: ObjectKeyMap): string[] { 126 | return this.jsonFactory.colorsJSON(JSON.stringify(json, null, this.DEFAULT_DEPTH)); 127 | } 128 | 129 | public cssClass(cssClassName: string): LoggerService { 130 | this.cssFactory.setClass(cssClassName); 131 | return this; 132 | } 133 | 134 | public copy(example: unknown): boolean { 135 | return this.clipboard.copyOnBuffer(example); 136 | } 137 | 138 | public startTime(title: string, level: LoggerLevel = LoggerLevel.DEBUG): TimerInfo | null { 139 | return this.timerFactory.startTime(title, level); 140 | } 141 | 142 | public endTime( 143 | info: TimerInfo | null, 144 | level: LoggerLevel = LoggerLevel.DEBUG, 145 | isMillisecond: boolean = true 146 | ): void { 147 | if (info) { 148 | this.timerFactory.endTime(info, level, isMillisecond, this); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /lib/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of logger 3 | */ 4 | 5 | export * from './logger.service'; 6 | export * from './logger.module'; 7 | export * from './logger.config'; 8 | export * from './decorators/logger.decorator'; 9 | export * from './decorators/trace.decorator'; 10 | export * from './decorators/debug.decorator'; 11 | export * from './decorators/error.decorator'; 12 | export * from './decorators/log.decorator'; 13 | export * from './decorators/info.decorator'; 14 | export * from './decorators/warn.decorator'; 15 | export * from './decorators/groups/group.decorator'; 16 | export * from './decorators/timer.decorator'; 17 | export * from './decorators/groups/group-collapsed.decorator'; 18 | export * from './interfaces/logger.external'; 19 | -------------------------------------------------------------------------------- /lib/src/services/clipboard-factory.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { Clipboard, ClipboardData, SetDataType } from '../interfaces/logger.internal'; 4 | 5 | declare global { 6 | interface Window { 7 | clipboardData: ClipboardData; 8 | } 9 | } 10 | declare const window: Window; 11 | 12 | @Injectable() 13 | export class ClipboardFactory implements Clipboard { 14 | private readonly DEFAULT_DEPTH: number = 4; 15 | public get clipboardSetData(): SetDataType { 16 | const clipboardData: ClipboardData = window.clipboardData; 17 | return clipboardData && clipboardData.setData; 18 | } 19 | 20 | public get queryCommandCopy(): boolean { 21 | return document.queryCommandSupported && document.queryCommandSupported('copy'); 22 | } 23 | 24 | public copyOnBuffer(data: unknown): boolean { 25 | const text: string = typeof data === 'string' ? data : JSON.stringify(data, null, this.DEFAULT_DEPTH); 26 | let isExec: boolean = false; 27 | 28 | if (this.clipboardSetData) { 29 | isExec = this.clipboardSetData('Text', text) as boolean; 30 | } else if (this.queryCommandCopy) { 31 | isExec = this.textAreaSelectData(text); 32 | } 33 | 34 | return isExec; 35 | } 36 | 37 | public textAreaSelectData(value: string): boolean { 38 | const textarea: HTMLTextAreaElement = document.createElement('textarea'); 39 | textarea.textContent = value; 40 | textarea.style.position = 'fixed'; 41 | document.body.appendChild(textarea); 42 | textarea.select(); 43 | try { 44 | return document.execCommand('copy'); 45 | } catch (ex) { 46 | return false; 47 | } finally { 48 | document.body.removeChild(textarea); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/services/console.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@angular/core'; 2 | 3 | import { ConsoleOperation, LOGGER_OPTIONS, LoggerLevel } from '../interfaces/logger.external'; 4 | import { ConsoleServiceInterface } from '../interfaces/logger.internal'; 5 | import { LoggerOptionsImpl } from '../logger.options'; 6 | 7 | @Injectable() 8 | export class ConsoleService implements ConsoleServiceInterface { 9 | public instance: Console; 10 | public minLevel: LoggerLevel; 11 | 12 | constructor(@Inject(LOGGER_OPTIONS) public readonly options: LoggerOptionsImpl) { 13 | this.minLevel = options.minLevel; 14 | this.instance = options.instance; 15 | } 16 | 17 | public get console(): Console { 18 | return this.instance; 19 | } 20 | 21 | public set console(instance: Console) { 22 | this.instance = instance; 23 | } 24 | 25 | public get noop(): ConsoleOperation { 26 | // eslint-disable-next-line @typescript-eslint/no-empty-function 27 | return ((): void => {}) as ConsoleOperation; 28 | } 29 | 30 | public getTemplateLabel(text: string): string { 31 | return `%c${text}`; 32 | } 33 | 34 | public getFormatTemplateLabel(text: string): string { 35 | return `%c${text} %c%s`; 36 | } 37 | 38 | public getTemplateWithoutLabel(): string { 39 | return `%c%s`; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/services/css-factory.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@angular/core'; 2 | 3 | import { LOGGER_OPTIONS, LoggerLevel } from '../interfaces/logger.external'; 4 | import { LoggerOptionsImpl } from '../logger.options'; 5 | 6 | @Injectable() 7 | export class CssFactory { 8 | private lineStyle: string | null = null; 9 | 10 | constructor(@Inject(LOGGER_OPTIONS) private readonly options: LoggerOptionsImpl) {} 11 | 12 | public get style(): string { 13 | const style: string = this.localStyle; 14 | this.clearLocalStyle(); 15 | return `${this.globalStyles}${style}`; 16 | } 17 | 18 | public set style(css: string) { 19 | this.lineStyle = css; 20 | } 21 | 22 | private get globalStyles(): string { 23 | return this.options.globalLineStyle ? `${this.options.globalLineStyle};` : ''; 24 | } 25 | 26 | private get localStyle(): string { 27 | return this.lineStyle ? `${this.lineStyle};` : ''; 28 | } 29 | 30 | public getStyleLabel(level: LoggerLevel): string { 31 | const color: string = this.options.labelColors[level]; 32 | return `color: ${color}; font-weight: bold`; 33 | } 34 | 35 | public setClass(cssClassName: string): void { 36 | const classList: string[] = cssClassName.split(/\s+/g); 37 | const styles: string[] = []; 38 | 39 | classList.forEach((className: string): void => { 40 | const style: string | undefined = this.options.cssClassMap[className]; 41 | if (style) { 42 | styles.push(style); 43 | } 44 | }); 45 | 46 | const localStyles: string = styles.length ? styles.join('; ') : ''; 47 | this.lineStyle = `${this.globalStyles}${localStyles}`; 48 | } 49 | 50 | private clearLocalStyle(): void { 51 | this.lineStyle = ''; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/services/factory.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@angular/core'; 2 | 3 | import { 4 | ConsoleOperation as Operation, 5 | GroupFactoryMethod, 6 | GroupLevel, 7 | LOGGER_OPTIONS, 8 | LoggerLevel, 9 | Pipeline, 10 | PipeOperation 11 | } from '../interfaces/logger.external'; 12 | import { Any, Arguments, Descriptor, ObjectKeyMap } from '../interfaces/logger.internal'; 13 | import { DEFAULT_METHODS } from '../logger.config'; 14 | import { LoggerOptionsImpl } from '../logger.options'; 15 | import { LoggerService } from '../logger.service'; 16 | import { ConsoleService } from './console.service'; 17 | import { CssFactory } from './css-factory.service'; 18 | import { GroupFactory } from './group-factory.service'; 19 | 20 | @Injectable() 21 | export class LoggerFactory { 22 | // eslint-disable-next-line max-params 23 | constructor( 24 | @Inject(LOGGER_OPTIONS) private readonly options: LoggerOptionsImpl, 25 | private readonly console: ConsoleService, 26 | private readonly cssFactory: CssFactory, 27 | private readonly groupFactory: GroupFactory 28 | ) {} 29 | 30 | public createLogger(level: LoggerLevel, logger: LoggerService): T { 31 | const args: Arguments = this.getArgumentsByType(level); 32 | const methodName: string = DEFAULT_METHODS[level]; 33 | 34 | const operation: Operation = 35 | this.console.minLevel <= level 36 | ? (this.console.instance as Any)[methodName].bind(...args) 37 | : this.console.noop; 38 | 39 | const pipeOperation: PipeOperation = this.options.useLevelGroup 40 | ? this.defineLevelGroups(level, operation, logger) 41 | : operation; 42 | 43 | return (pipeOperation as unknown) as T; 44 | } 45 | 46 | private defineLevelGroups(level: LoggerLevel, operation: Operation, logger: LoggerService): Operation { 47 | const { GROUP, GROUP_COLLAPSED }: typeof GroupLevel = GroupLevel; 48 | 49 | Object.defineProperties(operation, { 50 | [GROUP]: this.setGroupMethod(GROUP, level, logger), 51 | [GROUP_COLLAPSED]: this.setGroupMethod(GROUP_COLLAPSED, level, logger) 52 | }); 53 | 54 | return operation; 55 | } 56 | 57 | private setGroupMethod(methodName: GroupLevel, level: LoggerLevel, logger: LoggerService): Descriptor { 58 | return { 59 | enumerable: true, 60 | configurable: true, 61 | value: (label: string, pipeLine?: Pipeline): LoggerService => { 62 | const groupMethod: GroupFactoryMethod = this.groupFactory[methodName].bind(this.groupFactory); 63 | groupMethod(label, pipeLine, logger, level); 64 | 65 | return logger; 66 | } 67 | }; 68 | } 69 | 70 | // eslint-disable-next-line max-lines-per-function 71 | private getArgumentsByType(level: LoggerLevel): Arguments { 72 | const styleLabel: string = this.cssFactory.getStyleLabel(level); 73 | const lineStyle: string = this.cssFactory.style; 74 | const args: Arguments = [this.console.instance]; 75 | const withLabel: boolean = level !== LoggerLevel.LOG; 76 | 77 | if (withLabel) { 78 | const { label: formatLabel, style }: ObjectKeyMap = this.options.format( 79 | this.options.labelNames[level], 80 | styleLabel 81 | ); 82 | if (lineStyle) { 83 | const label: string = this.console.getFormatTemplateLabel(formatLabel); 84 | args.push(label, style, lineStyle); 85 | } else { 86 | const label: string = this.console.getTemplateLabel(formatLabel); 87 | args.push(label, style); 88 | } 89 | } else if (lineStyle) { 90 | args.push(this.console.getTemplateWithoutLabel(), lineStyle); 91 | } 92 | 93 | return args; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/services/group-factory.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@angular/core'; 2 | 3 | import { FormatOutput, GroupMethod, LOGGER_OPTIONS, LoggerLevel, Pipeline } from '../interfaces/logger.external'; 4 | import { Any } from '../interfaces/logger.internal'; 5 | import { LoggerOptionsImpl } from '../logger.options'; 6 | import { LoggerService } from '../logger.service'; 7 | import { ConsoleService } from './console.service'; 8 | import { CssFactory } from './css-factory.service'; 9 | 10 | @Injectable() 11 | export class GroupFactory { 12 | public executePipesGroup: boolean = false; 13 | private counterOpenedGroup: number = 0; 14 | 15 | constructor( 16 | private readonly console: ConsoleService, 17 | private readonly cssFactory: CssFactory, 18 | @Inject(LOGGER_OPTIONS) public readonly options: LoggerOptionsImpl 19 | ) {} 20 | 21 | public close(): void { 22 | if (this.executePipesGroup) { 23 | this.counterOpenedGroup--; 24 | this.console.instance.groupEnd(); 25 | } 26 | } 27 | 28 | public closeAll(): void { 29 | while (this.counterOpenedGroup > 0) { 30 | this.close(); 31 | } 32 | } 33 | 34 | // eslint-disable-next-line max-params 35 | public group(title: string, pipeline: Pipeline | undefined, logger: LoggerService, level: LoggerLevel): T { 36 | const group: GroupMethod = this.console.instance.group.bind(this.console.instance); 37 | return this.createGroupLogger(group, title, pipeline, logger, level); 38 | } 39 | 40 | // eslint-disable-next-line max-params 41 | public groupCollapsed( 42 | title: string, 43 | pipeline: Pipeline | undefined, 44 | logger: LoggerService, 45 | level: LoggerLevel 46 | ): T { 47 | const groupCollapsed: GroupMethod = this.console.instance.groupCollapsed.bind(this.console.instance); 48 | return this.createGroupLogger(groupCollapsed, title, pipeline, logger, level); 49 | } 50 | 51 | // eslint-disable-next-line max-lines-per-function,max-params 52 | private createGroupLogger( 53 | groupType: GroupMethod, 54 | title: string, 55 | pipeline: Pipeline | undefined, 56 | logger: LoggerService, 57 | level: LoggerLevel 58 | ): T { 59 | const showGroup: boolean = this.console.minLevel <= level; 60 | let pipeLineResult: T; 61 | 62 | if (showGroup) { 63 | this.executePipesGroup = true; 64 | this.counterOpenedGroup++; 65 | 66 | const lineStyle: string = this.cssFactory.getStyleLabel(level); 67 | const { label: formatLabel, style: formatStyle }: FormatOutput = this.getLabel( 68 | LoggerLevel[level], 69 | lineStyle 70 | ); 71 | 72 | groupType(`%c${formatLabel}`, formatStyle, title); 73 | if (pipeline) { 74 | const result: Any = pipeline(logger); 75 | this.close(); 76 | pipeLineResult = result; 77 | } 78 | } else { 79 | this.executePipesGroup = false; 80 | } 81 | 82 | // eslint-disable-next-line @typescript-eslint/tslint/config 83 | return pipeLineResult!; 84 | } 85 | 86 | private getLabel(level: string, style: string): FormatOutput { 87 | return this.options.format(level, style); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/src/services/json-factory.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { LexerJSON } from '../logger.config'; 4 | 5 | @Injectable() 6 | export class JsonFactory { 7 | private readonly _string: string = 'color:green'; 8 | private readonly _number: string = 'color:darkorange'; 9 | private readonly _boolean: string = 'color:blue'; 10 | private readonly _null: string = 'color:magenta'; 11 | private readonly _key: string = 'color:red'; 12 | private readonly lexerTypeFinder: RegExp = LexerJSON; 13 | 14 | // eslint-disable-next-line max-lines-per-function 15 | public colorsJSON(json: string): string[] { 16 | const arr: string[] = []; 17 | json = json.replace(this.lexerTypeFinder, (match: string): string => { 18 | let style: string = this._number; 19 | if (/^"/.test(match)) { 20 | if (/:$/.test(match)) { 21 | style = this._key; 22 | } else { 23 | style = this._string; 24 | } 25 | } else if (/true|false/.test(match)) { 26 | style = this._boolean; 27 | } else if (/null/.test(match)) { 28 | style = this._null; 29 | } 30 | arr.push(style); 31 | arr.push(''); 32 | return `%c${match}%c`; 33 | }); 34 | 35 | arr.unshift(json); 36 | 37 | return arr; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/services/timer-factory.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { LoggerLevel, TimerInfo } from '../interfaces/logger.external'; 4 | import { Any } from '../interfaces/logger.internal'; 5 | import { DEFAULT_METHODS } from '../logger.config'; 6 | import { LoggerService } from '../logger.service'; 7 | import { ConsoleService } from './console.service'; 8 | 9 | @Injectable() 10 | export class TimerFactory { 11 | private readonly DIGITS_TO_FIX: number = 4; 12 | private readonly SECONDS: number = 1000; 13 | constructor(private readonly console: ConsoleService) {} 14 | 15 | public startTime(title: string, level: LoggerLevel): TimerInfo | null { 16 | let result: TimerInfo | null = null; 17 | const canExecute: boolean = !(this.console.minLevel > level); 18 | if (canExecute) { 19 | result = { startTime: performance.now(), title }; 20 | } 21 | return result; 22 | } 23 | 24 | // eslint-disable-next-line max-params 25 | public endTime(info: TimerInfo, level: LoggerLevel, isMillisecond: boolean, logger: LoggerService): void { 26 | const canExecute: boolean = !(this.console.minLevel > level); 27 | 28 | if (canExecute) { 29 | const methodName: string = DEFAULT_METHODS[level]; 30 | const time: string = this.ensureTime(info, isMillisecond); 31 | const logMethod: (...args: string[]) => void = (logger as Any)[methodName]; 32 | logMethod(`TimerLog: ${info.title}`, `took ${time} to execute`); 33 | } 34 | } 35 | 36 | private ensureTime(info: TimerInfo, isMillisecond: boolean): string { 37 | const msTime: number = parseFloat((performance.now() - info.startTime).toFixed(this.DIGITS_TO_FIX)); 38 | return isMillisecond ? `${msTime}ms` : `${Math.floor(msTime / this.SECONDS)}s`; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "noUnusedParameters": true, 15 | "noUnusedLocals": true, 16 | "types": [], 17 | "lib": ["dom", "es2018"] 18 | }, 19 | "angularCompilerOptions": { 20 | "fullTemplateTypeCheck": true, 21 | "annotateForClosureCompiler": true, 22 | "strictInjectionParameters": true, 23 | "skipTemplateCodegen": true, 24 | "preserveWhitespaces": true, 25 | "skipMetadataEmit": true 26 | }, 27 | "exclude": ["**/*.spec.ts"] 28 | } 29 | -------------------------------------------------------------------------------- /ngx-logger.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logger", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "jest": "jest", 7 | "start": "ng serve", 8 | "build:lib": "ng build logger", 9 | "build:app": "ng build integration", 10 | "test": "yarn jest --config ./jest.config.js", 11 | "test:ci": "yarn jest --config ./jest.config.js --coverage", 12 | "lint": "eslint --fix \"**/*.ts\"", 13 | "format": "prettier --write \"**/*.{ts,html,css,scss,md,js,json}\"", 14 | "coverage": "jest --config ./jest.app.config.js --coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" 15 | }, 16 | "husky": { 17 | "hooks": { 18 | "pre-commit": "npm run format" 19 | } 20 | }, 21 | "private": true, 22 | "prettier": "@angular-ru/prettier-config", 23 | "devDependencies": { 24 | "@angular/animations": "8.2.14", 25 | "@angular/common": "8.2.14", 26 | "@angular/compiler": "8.2.14", 27 | "@angular/core": "8.2.14", 28 | "@angular/forms": "8.2.14", 29 | "@angular/platform-browser": "8.2.14", 30 | "@angular/platform-browser-dynamic": "8.2.14", 31 | "@angular/router": "8.2.14", 32 | "core-js": "3.6.5", 33 | "devtools-detect": "3.0.0", 34 | "lint-staged": "9.5.0", 35 | "rxjs": "6.5.5", 36 | "zone.js": "0.10.3", 37 | "@angular-ru/jest-utils": "12.19.0", 38 | "@angular-ru/eslint-config": "12.20.1", 39 | "@angular-ru/prettier-config": "12.19.1", 40 | "@angular-ru/tsconfig": "12.19.1", 41 | "@angular-devkit/build-angular": "0.803.27", 42 | "@angular-devkit/build-ng-packagr": "0.803.27", 43 | "@angular/cli": "8.3.21", 44 | "@angular/compiler-cli": "8.2.14", 45 | "@angular/language-service": "8.2.14", 46 | "@types/node": "14.0.13", 47 | "coveralls": "3.1.0", 48 | "husky": "2.7.0", 49 | "ng-packagr": "9.1.5", 50 | "ts-node": "8.10.2", 51 | "tsickle": "0.38.1", 52 | "tslib": "1.13.0", 53 | "typescript": "3.8.3" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "automerge": true, 3 | "rangeStrategy": "bump", 4 | "packageFiles": ["package.json"], 5 | "groupName": "all dependencies", 6 | "separateMajorMinor": false, 7 | "groupSlug": "all", 8 | "major": { 9 | "dependencies": { "enabled": false }, 10 | "devDependencies": { "enabled": false } 11 | }, 12 | "packageRules": [ 13 | { 14 | "packagePatterns": ["*"], 15 | "excludePackagePatterns": ["^@angular/", "^@angular-devkit/", "typescript"], 16 | "groupName": "all dependencies", 17 | "groupSlug": "all-dependencies" 18 | }, 19 | { 20 | "packagePatterns": ["typescript"], 21 | "groupName": "typescript", 22 | "updateTypes": "patch", 23 | "schedule": ["before 3:00am every 2nd week of the month"] 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@angular-ru/tsconfig", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "annotateForClosureCompiler": true, 6 | "strictInjectionParameters": true, 7 | "skipTemplateCodegen": false, 8 | "preserveWhitespaces": true, 9 | "skipMetadataEmit": false, 10 | "disableTypeScriptVersionCheck": true 11 | }, 12 | "compilerOptions": { 13 | "rootDir": ".", 14 | "baseUrl": "./", 15 | "module": "esnext", 16 | "outDir": "./dist/out-tsc", 17 | "target": "es5", 18 | "typeRoots": ["node_modules/@types"], 19 | "lib": ["es2018", "dom"], 20 | "paths": { 21 | "@angular-ru/logger": ["dist/logger"] 22 | } 23 | } 24 | } 25 | --------------------------------------------------------------------------------