Question

I'm making a logger service, to extend angular's $log service, by saving the errors (or debug if enabled) into an indexedDB database. Here's the code:

angular.module('appLogger', ['appDatabase'])

.service('LogServices', function($log, Database) {

    // ...

    this.log = function(type, message, details) {
        var log = {};
        log.type = type
        log.context = this.context;
        log.message = message;
        log.dateTime = moment().format('YYYY-MM-DD HH:mm:ss');
        log.details = details || '';
        $log[type.toLowerCase()](log);

        if (type === 'ERROR' || this.logDebug) {
            Database.logSave(log);
        }
    };

    // ...
})

This is working in my services as intended. Now the problem is I can't use my logger inside the Database service, because it throws a circular dependency error. I understand the problem but I have no clue how should I resolve it... How should I get around this ?

Thanks for helping :-)

Was it helpful?

Solution

The reason Angular is complaining about a circular dependency is that...well there is one.
It is a very dangerous path to go down, but if you know what you are doing (famous last words) then there is a solution to circumvent that:

.service('LogServices', function($log, $injector) {

    // ...

    var Database;   // Will initialize it later

    this.log = function(type, message, details) {
        /* Before using Database for the first time
         * we need to inject it */
        if (!Database) { Database = $injector.get('Database'); }

        var log = {};
        log.type = type
        log.context = this.context;
        log.message = message;
        log.dateTime = moment().format('YYYY-MM-DD HH:mm:ss');
        log.details = details || '';
        $log[type.toLowerCase()](log);

        if (type === 'ERROR' || this.logDebug) {
            Database.logSave(log);
        }
    };

    // ...
})

See, also, this short demo.

OTHER TIPS

See this answer and in particular Misko's blog and in particular Peter's question in the comments, in which almost the exact same problem is discussed.

Misko's answer is (using Java code)

class Database() implements DB;
class Logger(Database db);
class LoggingDatabase(Logger log, Database db) implements DB;

So in your application, you have

.service('Database', ...) // DO NOT inject the $log or LogServices here

.service('LogServices', function($log) {...}) // DO NOT inject Database here

.service('LoggingDB', function(Database, LogServices) {...}) 

Use LoggingDB for every part of your app where you want a database that logs. (Or is it a logger that uses the database!)

Another thought

How is it that you have a Database on the browser? Is Database a wrapper for $http or $resource or something? If that's the case, I agree with ExpertSystem in his 2) option: don't use $http for logging errors because what if the error kills $http? Use XMLHTTPRequest instead. See here for some discussion.

I faced this issue when trying to override exceptionHandler

here is the code where the issue appeared

angular
    .factory('$exceptionHandler', ExceptionHandler);

function exceptionHandler($log, sweetAlert) {// here is the problem with injecting sweetAlert
    return function myExceptionHandler(exception, cause) {
        //logErrorsToBackend(exception, cause);
        sweetAlert.swal('Opps...', 'An error has occurred');
        $log.warn(exception, cause);
    };
}

and to fix it I used injector

angular
    .factory('$exceptionHandler', ExceptionHandler);

ExceptionHandler.$inject = ['$injector']; //better for minification

function ExceptionHandler($injector) {
    var $log, sweetAlert, $translate;// use variables for caching

    return function exceptionHandler(exception, cause) {
        // Add DI here to prevent circular dependency
        $log = $log || $injector.get('$log');
        sweetAlert = sweetAlert || $injector.get('sweetAlert');
        $translate = $translate || $injector.get('$translate');
        //............
    }

here is a link for more info Injecting $http results in a Circular dependency

hope this helps you

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top