Skip to content

Write a Logger

Extend LumberjackLogger (or ScopedLumberjackLogger) so each log message lives once, with a fixed level and optional scope/payload. Call sites stay one-liners and are easy to spy on in tests.

  1. Create a scoped logger class

    import { Injectable } from '@angular/core';
    import { ScopedLumberjackLogger } from '@ngworker/lumberjack';
    
    @Injectable({ providedIn: 'root' })
    export class AppLogger extends ScopedLumberjackLogger {
      readonly scope = 'Forest App';
    
      readonly helloForest = this.createInfoLogger('Hello, Forest!').build();
    
      readonly forestOnFire = this.createCriticalLogger('The forest is on fire!').build();
    }

    build() returns a zero-arg function that emits the log when called.

  2. Inject and call it

    import { Component, inject, OnInit } from '@angular/core';
    
    import { AppLogger } from './app.logger';
    
    @Component({
      selector: 'app-root',
      template: ``,
    })
    export class AppComponent implements OnInit {
      readonly #logger = inject(AppLogger);
    
      ngOnInit(): void {
        this.#logger.helloForest();
      }
    }

Extend LumberjackLogger and set scope per builder:

import { Injectable } from '@angular/core';
import { LumberjackLogger } from '@ngworker/lumberjack';

@Injectable({ providedIn: 'root' })
export class AppLogger extends LumberjackLogger {
  readonly helloForest = this.createInfoLogger('Hello, Forest!').withScope('Forest App').build();
}
import { Injectable, VERSION } from '@angular/core';
import { LumberjackLogPayload, ScopedLumberjackLogger } from '@ngworker/lumberjack';

interface LogPayload extends LumberjackLogPayload {
  readonly angularVersion: string;
}

@Injectable({ providedIn: 'root' })
export class AppLogger extends ScopedLumberjackLogger<LogPayload> {
  readonly scope = 'Forest App';

  readonly helloForest = this.createInfoLogger('Hello, Forest!').withPayload({ angularVersion: VERSION.full }).build();
}

Drivers that understand the payload can read log.payload on LumberjackLogDriverLog. See How log drivers work.

Skip withPayload on a logger typed with a payload. The built function takes the payload as its argument:

import { Injectable, VERSION } from '@angular/core';
import { LumberjackLogPayload, ScopedLumberjackLogger } from '@ngworker/lumberjack';

interface LogPayload extends LumberjackLogPayload {
  readonly angularVersion: string;
}

@Injectable({ providedIn: 'root' })
export class AppLogger extends ScopedLumberjackLogger<LogPayload> {
  readonly scope = 'Forest App';

  readonly helloForest = this.createInfoLogger('Hello, Forest!').build();
}

// call site
this.#logger.helloForest({ angularVersion: VERSION.full });

Trigger the call path and confirm the console (or HTTP store) shows the fixed message and scope.