11

Is there a way to write a global selfmade mylogger function that I could use in Angular2 typescript project for my services or components instead of console.log function ?

My desired result would be something like this:

mylogger.ts

function mylogger(msg){
    console.log(msg);
};

user.service.ts

import 'commons/mylogger';
export class UserService{
  loadUserData(){
    mylogger('About to get something');
    return 'something';
  };
};
1
  • 1
    Make you logger a service (like UserService), register it as provider, and then add it as Constructor argument to your service classes. Angular will pass the logger to your service when it creates an instance. Commented Dec 23, 2015 at 14:22

8 Answers 8

19

You could write this as a service and then use dependency injection to make the class available to your components.

import {Injectable, provide} from 'angular2/core';

// do whatever you want for logging here, add methods for log levels etc.
@Injectable()
export class MyLogger {

  public log(logMsg:string) {
    console.log(logMsg); 
  }
}

export var LOGGING_PROVIDERS:Provider[] = [
      provide(MyLogger, {useClass: MyLogger}),
    ];

You'll want to place this in the top level injector of your application by adding it to the providers array of bootstrap.

import {LOGGING_PROVIDERS} from './mylogger';

bootstrap(App, [LOGGING_PROVIDERS])
  .catch(err => console.error(err));

A super simple example here: http://plnkr.co/edit/7qnBU2HFAGgGxkULuZCz?p=preview

Sign up to request clarification or add additional context in comments.

5 Comments

What is the advantage/reason for defining LOGGING_PROVIDERS rather than just bootstrapping with MyLogger: boostrap(App, [MyLogger])? simplified plunker
You're right you could just bootstrap the class directly. Using the providers array would allow you provide more than one class or to specify a 'multi provider'. In this case that's probably overkill though
@MarkRajcok if we provide that service via bootstrap file then can we use this service anywhere without importing ?
No you still need to import it's just that you're providing the class to the top level dependency injector ensuring the same instance will be available to all your components
The only problem I see with this answer is that in the console, you will see the logged message came from MyLogger as opposed to coming from the class that you logged from. See my answer to see how to solve this.
11

The example given by the accepted answer will print logs from the logger class, MyLogger, instead of from the class that is actually logging.

I have modified the provided example to get logs to be printed from the exact line that calls MyLogger.log(), for example:

get debug() {
    return console.debug.bind(console);
}
get log() {
    return console.log.bind(console);
}

I found how to do it here: https://github.com/angular/angular/issues/5458

Plunker: http://plnkr.co/edit/0ldN08?p=preview

As per the docs in developers.mozilla,

The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of 
arguments preceding any provided when the new function is called.

More information about bind here:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

3 Comments

Sorry but i dont get it: How can you but a string into log() without any arguments?
I updated my answer to provide a bit of information about how bind works and a link with more details.
This is awesome answer, better than the accepted one. Huge thanks!
3

If you want to use 'console.log' function just in your component you can do this:

import { Component, OnInit } from '@angular/core';

var output = console.log;

@Component({
  selector: 'app-component',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor() { }

  ngOnInit() { }

  printFunction(term: string): void {
    output('foo');
  }
}

Comments

1

How about using console on your main service, So we can customize and apply console.log conditionally:

myComponent.ts

export class myComponent implements OnInit {
 constructor(
        private config: GlobalService
    ) {}

    ngOnInit() {
       this.config.log('func name',{a:'aval'},'three');
    }
}

global.service.ts

@Injectable()
export class GlobalService {

   constructor() { }
   this.prod = true;
   public log(one: any, two?: any, three?: any, four?: any) {
        if (!this.prod) {
             console.log('%c'+one, 'background:red;color:#fff', two, three, four);
        }
    }
}

(Note: first parameter should be string in this example);

Comments

1

For toggling console.log ON\OFF:

logger.service.ts:

import { Injectable } from '@angular/core';

@Injectable()
export class LoggerService {

private oldConsoleLog = null;

enableLogger(){

    if (this.oldConsoleLog == null) { return; }

    window['console']['log'] = this.oldConsoleLog;
}

disableLogger() {
    this.oldConsoleLog = console.log;
    window['console']['log'] = function () { };
};

}

app.component.ts:

@Component({
  selector: 'my-app',
  template: `your templ;ate`
})

export class AppComponent {

constructor(private loggerService: LoggerService) {
    var IS_PRODUCTION = true;

    if ( IS_PRODUCTION ) {
        console.log("LOGGER IS DISABBLED!!!");
        loggerService.disableLogger();
    }

}
}

Comments

1

I created a logger based on the provided information here

Its very basic (hacky :-) ) at the moment, but it keeps the line number

@Injectable()
export class LoggerProvider {

constructor() {
  //inject what ever you want here
}

public getLogger(name: string) {
  return {

    get log() {
      //Transform the arguments 
      //Color output as an example
      let msg = '%c[' + name + ']';
      for (let i = 0; i < arguments.length; i++) {
        msg += arguments[i]
      }

      return console.log.bind(console, msg, 'color:blue');
    }

  }
 }
}

Hope this helps

Comments

1

type safer(ish) version with angular 4, typescript 2.3

logger.service.ts

import { InjectionToken } from '@angular/core';

export type LoggerService = Pick<typeof console,
                                 'debug' | 'error' | 'info' | 'log' | 'trace' | 'warn'>;
export const LOGGER_SERVICE = new InjectionToken('LOGGER_SERVICE');
export const ConsoleLoggerServiceProvider = { provide: LOGGER_SERVICE, useValue: console };

my.module.ts

// ...
@NgModule({
  providers: [
    ConsoleLoggerServiceProvider,
    //...
  ],
// ...

my.service.ts

// ...
@Injectable()
export class MyService {

  constructor(@Inject(LOGGER_SERVICE) log: LoggerService) {
//...

2 Comments

What does this do, exactly, and how do I use it to overwrite the default behavior of console.log()?
export const OtherLoggerServiceProvider = { provide: LOGGER_SERVICE, useValue: otherLogger /* should be of type LoggerService /}; / ... / providers: [OtherLoggerServiceProvider , / ... */ ] – (We're using Angular's DI so other places in the code do not change when you change the implementation.)
0

There is now an angular2 logger component on NPM which supports log levels. https://www.npmjs.com/package/angular2-logger

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.