获取Angular 2和TypeScript中当前正在执行的函数的名称

时间:2017-08-01 13:45:53

标签: angular typescript

这个问题适用于使用TypeScript的Angular 2应用程序。

我需要记录自定义消息,类或组件的名称,以及我记录的函数的名称。

现在,我正在对这些值进行硬编码(同样这是TypeScript):

class MyService {
  getUsers() {
    console.log('MyService.getUsers()', 'I am here');
  }

但是,我不想硬编码类和函数的名称。

我设法获得了类或组件的名称:

console.log(MyService.name);

但是如何获取当前正在执行的函数的名称?

1 个答案:

答案 0 :(得分:1)

这个答案要求Lodash提供一些基本功能。您应该能够使用JavaScript对应代码替换该代码。

LogService.ts

import {FactoryProvider, Injectable, InjectionToken, Optional} from '@angular/core';
import * as _ from 'lodash';

const PREFIX_SEPARATOR = ':';

@Injectable()
export class LogService {
    /**
     * Global logger
     */
    public static log: LogService = new LogService();

    /**
     * Creates an injectable logger that can be used by a module.
     */
    public static provide(token: InjectionToken<LogService>): FactoryProvider {
        return {
            provide: token,
            useFactory: (log: LogService) => {
                let prefix = token.toString().replace(/^InjectionToken\s/, '').replace(/Logger$/, '').toUpperCase();
                return log.prefix(prefix);
            },
            deps: [LogService]
        };
    }

    /**
     * Gets the stack trace when browser adds stack property.
     */
    private static getStackTrace(): string[] | undefined {
        try {
            let ex = new Error() as Object;
            if (ex.hasOwnProperty('stack') && _.isString(ex['stack'])) {
                return ex['stack'].split('\n');
            }
        } catch (err) {
            // do nothing
        }
        return void 0;
    }

    /**
     * Gets the constructor name from the call stack, but only on Chrome browsers.
     */
    private static getConstructorName(depth: number): string | undefined {
        if (_.isUndefined(window['chrome'])) {
            return void 0;
        }
        let trace = LogService.getStackTrace();
        if (!trace || trace.length <= depth) {
            return void 0;
        }
        let str = trace[depth];
        if (!/\s+at\snew\s/.test(str)) {
            return void 0;
        }
        let match = /new ([$A-Z_][0-9A-Z_$]*)/i.exec(str);
        if (!match || match.length < 2) {
            return void 0;
        }
        return match[1].replace(/(Component|Directive|Service|Factory|Pipe|Resource|Module|Resolver|Provider)$/, '');
    }

    /**
     * Output prefix
     */
    private prefixName: string;

    /**
     * Flag if debugging is enabled.
     */
    private _debug: boolean = process.env.ENV && process.env.ENV === 'development';

    /**
     * Allows for optional prefix.
     */
    public constructor(@Optional() prefix?: string) {
        this.prefixName = prefix;
    }

    /**
     * Creates a logger with an automatic prefix.
     */
    public withPrefix(): LogService {
        if (!this._debug) {
            return this;
        }
        return this.prefix(LogService.getConstructorName(4));
    }

    /**
     * Creates a new logger with the given prefix in all output messages.
     */
    public prefix(prefix?: string): LogService {
        return prefix
            ? new LogService((this.prefixName || '') + prefix + PREFIX_SEPARATOR)
            : this;
    }

    public get info(): Function {
        if (!console || !console.info) {
            return _.noop;
        }
        return this.prefixName
            ? console.info.bind(console, this.prefixName)
            : console.info.bind(console);
    }

    public get debug(): Function {
        if (!this._debug || !console || !console.log) {
            return _.noop;
        }
        return this.prefixName
            ? console.log.bind(console, this.prefixName)
            : console.log.bind(console);
    }

    public get warn(): Function {
        if (!console || !console.warn) {
            return _.noop;
        }
        return this.prefixName
            ? console.warn.bind(console, this.prefixName)
            : console.warn.bind(console);
    }

    public get error(): Function {
        if (!console || !console.error) {
            return _.noop;
        }
        return this.prefixName
            ? console.error.bind(console, this.prefixName)
            : console.error.bind(console);
    }
}

LogService.module.ts

您必须在ngModule中将此服务导出为单身,然后在主模块中调用forRoot()。在您的其他模块中,您可以正常导入此模块。

@NgModule({
})
export class LogModule {
    public static forRoot() {
        return {
            ngModule: LogModule,
            providers: [
                LogService
            ]
        };
    }
}

为每个模块创建记录器

您可以通过为该模块创建可注入令牌来创建为每个模块名称添加前缀的记录器。

此示例适用于UI模块。

export const UI_LOGGER = new InjectionToken<LogService>('UILogger');

@NgModule({
    providers: [
        LogService.provide(UI_LOGGER)
    ]
})
export class UIModule {
}

您无法将此新记录器注入UI组件。

@Component({})
export class MyComponent {
    private log: LogService; // logger with constructor name

    public constructor(@Inject(UI_LOGGER) log: LogService) {
        this.log = log.withPrefix();
    }
}

调用log.withPrefix()创建一个带有构造函数名称的新记录器,并且还具有模块名称。