Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion modules/@angular/core/src/application_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

import {AnimationQueue} from './animation/animation_queue';
import {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
import {ApplicationRef, ApplicationRef_} from './application_ref';
import {ApplicationRef, ApplicationRef_, isDevMode} from './application_ref';
import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection';
import {Inject, Optional, SkipSelf} from './di/metadata';
import {LOCALE_ID} from './i18n/tokens';
import {Compiler} from './linker/compiler';
import {ViewUtils} from './linker/view_utils';
import {ConsoleLogger, LOGGER_OPTIONS, Logger, LoggerOptions, NoOpLogger} from './logger';
import {NgModule} from './metadata';
import {initServicesIfNeeded} from './view/index';

Expand All @@ -34,6 +35,17 @@ export function _initViewEngine() {
initServicesIfNeeded();
}

export function _loggerFactory(options?: LoggerOptions): Logger {
options = options || {};
const enabled = options.enabled != null ? options.enabled : isDevMode();
if (enabled) {
const _console: Console = typeof console === 'object' ? console : <any>{};
const debug = options.debug != null ? options.debug : true;
return new ConsoleLogger(_console, debug);
}
return new NoOpLogger();
}

/**
* This module includes the providers of @angular/core that are needed
* to bootstrap components via `ApplicationRef`.
Expand All @@ -51,6 +63,11 @@ export function _initViewEngine() {
AnimationQueue,
{provide: IterableDiffers, useFactory: _iterableDiffersFactory},
{provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory},
{
provide: Logger,
useFactory: _loggerFactory,
deps: [[new Inject(LOGGER_OPTIONS), new Optional()]]
},
{
provide: LOCALE_ID,
useFactory: _localeFactory,
Expand Down
1 change: 1 addition & 0 deletions modules/@angular/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ export {AnimationStyles} from './animation/animation_styles';
export {AnimationKeyframe} from './animation/animation_keyframe';
export {Sanitizer, SecurityContext} from './security';
export {TransitionFactory, TransitionInstruction, Trigger} from './triggers';
export {Logger, LOGGER_OPTIONS, LoggerOptions} from './logger';
export * from './codegen_private_exports';
111 changes: 111 additions & 0 deletions modules/@angular/core/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {InjectionToken} from './di/injection_token';

/** @experimental */
export interface LoggerOptions {
enabled?: boolean;
debug?: boolean;
}

/**
* @whatItDoes Is used in DI to configure the {@link Logger}.
* @experimental
*/
export const LOGGER_OPTIONS = new InjectionToken<LoggerOptions>('Logger Options');

/**
* Simple service for logging.
*
* @experimental
*/
export abstract class Logger {
/** Write a log message. */
abstract log(...args: any[]): void;

/** Write an information message. */
abstract info(...args: any[]): void;

/** Write a warning message. */
abstract warn(...args: any[]): void;

/** Write an error message. */
abstract error(...args: any[]): void;

/** Write a debug message. */
abstract debug(...args: any[]): void;

/** Create a new inline group. */
abstract group(groupTitle?: string): void;

/** Exit the current inline group. */
abstract groupEnd(): void;
}

const noop = (): any => undefined;

/**
* Default implementation of {@link Logger} that safely writes the message into the console.
*
* @experimental
*/
export class ConsoleLogger implements Logger {
constructor(private _console: Console, private _debugEnabled: boolean = true) {}

log(...args: any[]): void { this._invokeConsoleMethod('log', args); }

info(...args: any[]): void { this._invokeConsoleMethod('info', args); }

warn(...args: any[]): void { this._invokeConsoleMethod('warn', args); }

error(...args: any[]): void { this._invokeConsoleMethod('error', args); }

debug(...args: any[]): void {
if (this._debugEnabled) this._invokeConsoleMethod('debug', args);
}

group(groupTitle?: string): void {
const args = groupTitle != null ? [groupTitle] : [];
this._invokeConsoleMethod('group', args);
}

groupEnd(): void { this._invokeConsoleMethod('groupEnd'); }

private _invokeConsoleMethod(type: string, args?: any[]): void {
let logFn: Function = (<any>this._console)[type] || this._console.log || noop;

// console methods in IE9 don't have 'apply' method, polyfill it
if (!logFn.apply) {
logFn = Function.prototype.bind.call(logFn, this._console);
}

logFn.apply(this._console, args);
}
}

/**
* No op implementation of {@link Logger}.
*
* @experimental
*/
export class NoOpLogger implements Logger {
log(): void {}

info(): void {}

warn(): void {}

error(): void {}

debug(): void {}

group(): void {}

groupEnd(): void {}
}
37 changes: 36 additions & 1 deletion modules/@angular/core/test/application_module_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,47 @@
* found in the LICENSE file at https://angular.io/license
*/

import {LOCALE_ID} from '@angular/core';
import {Injectable, LOCALE_ID} from '@angular/core';
import {TestBed} from '@angular/core/testing';

import {_loggerFactory} from '../src/application_module';
import {ConsoleLogger, Logger, NoOpLogger} from '../src/logger';
import {describe, expect, inject, it} from '../testing/testing_internal';

export function main() {
describe('Application module', () => {
it('should set the default locale to "en-US"',
inject([LOCALE_ID], (defaultLocale: string) => { expect(defaultLocale).toEqual('en-US'); }));

describe('Logger', () => {
describe('factory', () => {
it('should return NoOpLogger when disabled', () => {
const logger = _loggerFactory({enabled: false});
expect(logger instanceof NoOpLogger).toBe(true);
});

it('should return ConsoleLogger when enabled', () => {
const logger = _loggerFactory({enabled: true});
expect(logger instanceof ConsoleLogger).toBe(true);
});

it('should use isDevMode without options', () => {
const logger = _loggerFactory();
expect(logger instanceof ConsoleLogger).toBe(true);
});
});

describe('should inject Logger service', () => {
@Injectable()
class DependsOnLogger {
constructor(public logger: Logger) {}
}

beforeEach(() => TestBed.configureTestingModule({providers: [DependsOnLogger]}));

it('works',
() => expect(TestBed.get(DependsOnLogger).logger instanceof ConsoleLogger).toBe(true));
});
});
});
}
131 changes: 131 additions & 0 deletions modules/@angular/core/test/logger_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {ConsoleLogger, Logger, NoOpLogger} from '../src/logger';

interface TestConsole {
log?: (arg?: any) => string;
warn?: (arg?: any) => string;
info?: (arg?: any) => string;
error?: (arg?: any) => string;
debug?: (arg?: any) => string;
group?: (arg?: any) => string;
groupEnd?: (arg?: any) => string;
}

export function main() {
describe('Logger', () => {
let logBuffer: string;
let _console: TestConsole|any;

beforeEach(() => {
logBuffer = '';
_console = {
log: (arg: any = '') => logBuffer += `log${arg};`,
warn: (arg: any = '') => logBuffer += `warn${arg};`,
info: (arg: any = '') => logBuffer += `info${arg};`,
error: (arg: any = '') => logBuffer += `error${arg};`,
debug: (arg: any = '') => logBuffer += `debug${arg};`,
group: (arg: any = '') => logBuffer += `group${arg};`,
groupEnd: () => logBuffer += 'groupEnd;',
};
});

describe('No op', () => {
it('should not log anything', () => {
const logger: Logger = new NoOpLogger();
logger.info();
logger.log();
logger.debug();
logger.error();
logger.warn();
logger.group();
logger.groupEnd();
expect(logBuffer).toEqual('');
});
});

describe('Console', () => {
it('should use console if present', () => {
const logger: Logger = new ConsoleLogger(_console);
logger.info('$');
logger.log('$');
logger.debug('$');
logger.error('$');
logger.warn('$');
logger.group('$');
logger.groupEnd();
expect(logBuffer).toEqual('info$;log$;debug$;error$;warn$;group$;groupEnd;');
});

it('should use console.log() if other not present', () => {
const _console: any = {log: () => logBuffer += 'log;'};
const logger: Logger = new ConsoleLogger(_console);
logger.info();
logger.log();
logger.debug();
logger.error();
logger.warn();
logger.group();
logger.groupEnd();
expect(logBuffer).toEqual('log;log;log;log;log;log;log;');
});

it('should use noop if no console', () => {
const logger: Logger = new ConsoleLogger(<any>{});
logger.info();
logger.log();
logger.debug();
logger.error();
logger.warn();
logger.group();
logger.groupEnd();
expect(logBuffer).toEqual('');
});

describe('debug', () => {
it('should skip debugging output if disabled', () => {
const logger: Logger = new ConsoleLogger(_console, false);
logger.info();
logger.log();
logger.debug();
logger.error();
logger.warn();
logger.group();
logger.groupEnd();
expect(logBuffer).toEqual('info;log;error;warn;group;groupEnd;');
});
});

describe('IE logging', () => {
it(`should work in IE where console methods don't have 'apply' method`, () => {
removeApplyFunctionForIE(_console);
const logger: Logger = new ConsoleLogger(_console);
logger.info('$');
logger.log('$');
logger.debug('$');
logger.error('$');
logger.warn('$');
logger.group('$');
logger.groupEnd();
expect(logBuffer).toEqual('info$;log$;debug$;error$;warn$;group$;groupEnd;');
});

function removeApplyFunctionForIE(console: TestConsole): void {
console.log.apply = null;
console.warn.apply = null;
console.info.apply = null;
console.error.apply = null;
console.debug.apply = null;
console.group.apply = null;
console.groupEnd.apply = null;
}
});
});
});
}
1 change: 1 addition & 0 deletions modules/@angular/core/testing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export * from './test_bed';
export * from './testing';
export * from './metadata_override';
export * from './private_export_testing';
export * from './mock_logger';
Loading