Extending $log to log to database in AngularJS

$log is a very useful built-in service that is part of the AngularJS (angular 1) distribution. It is even more useful if you could write logs to a database table rather than just to console.

Here’s how you can do it:

Assumes you have a RESTful back-end that responds to the url in the log() function of LoggingService below,


var app = angular.module('app');

app.factory("LoggingService", function ($http) {
    var _isEnabled = true;

    var isEnabled = function () {
        return _isEnabled;
    }

    var enable = function (b) {
        _isEnabled = b;
    }

    var log = function () {
        var url = "/Api/Audit/Log";
        if (this.isEnabled()) {
            var type = "UNK"; // placeholder for when no type is passed
            args = [];
            if (typeof arguments === 'object') {
                type = arguments[0];
                for (var i = 1; i < arguments.length; i++) {
                    arg = arguments[i];
                    if (typeof (arg) != 'string') arg = JSON.stringify(arg);
                    args.push(arg);
                }
                return $http.post(url, { "ACTIVITY": type + ': ' + args.join('\n') });
            }
            else {
                return $http.post(url, { "ACTIVITY": type + ': ' + arguments[0] });
            }
        }
    };

    return {
        log: log,
        enable: enable,
        isEnabled: isEnabled
    };
})

.config(function ($routeProvider, $logProvider, $httpProvider, $provide) {

    $logProvider.debugEnabled(1);

    // -----------------------------------------------------------------------------------
    // logging algorithm suggested by:
    // http://stackoverflow.com/questions/32365811/decorate-angulars-log-to-use-a-service-that-uses-http
    // -----------------------------------------------------------------------------------

    $provide.decorator("$log", function ($delegate, $injector) {

        var logFn = $delegate.log;

        // N.B. Don't delegate $log.debug()...that is ALWAYS written just to the console!
        // $log.warn(), $log.error() and $log.info() are written to both console (assuming $logProvider.debugEnabled == 1) and database
        // $log.db() is written ONLY to database!!

        $delegate.warn = function (message) {
            var LoggingService = $injector.get('LoggingService');
            LoggingService.log('WARN', message);
            if ($logProvider.debugEnabled()) logFn.apply(null, arguments);
        };
        $delegate.error = function (message) {
            var LoggingService = $injector.get('LoggingService');
            LoggingService.log('ERROR', message);
            if ($logProvider.debugEnabled()) logFn.apply(null, arguments);
        };
        $delegate.info = function (message) {
            var LoggingService = $injector.get('LoggingService');
            LoggingService.log('INFO', message);
            if ($logProvider.debugEnabled()) logFn.apply(null, arguments);
        };
        $delegate.db = function (message) {
            var LoggingService = $injector.get('LoggingService');
            LoggingService.log('DB', message);
            // N.B. $log.db is not written to console ever!!
        };
        //Return the delegate
        return $delegate;
    });
//   :
//   :
// more config functionality here
//   :
//   :
}) // end of config function

.run();

RESTful Angular 2 services

Sample RESTful service for use with Angular 2:

 

import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';
import 'rxjs/add/operator/map'
import { Observable } from 'rxjs/Observable';

/*
database User table structure:
    ID INTEGER NOT NULL, AUTOINCREMENT
    NAME VARCHAR(50) NOT NULL
    PHONE VARCHAR(20) NOT NULL
*/

export class User {
    public id:number;
    public name:string;
    public phone:string;
}

@Injectable()
export class UserService {

    private actionUrl: string;
    private headers: Headers;

//
// sample url for a web service ==> "http://somesite.com/user/"
//

    constructor(private http: Http, url:string) {

        this.actionUrl = url;

        this.headers = new Headers();
        this.headers.append('Content-Type', 'application/json');
        this.headers.append('Accept', 'application/json');
    }

    public GetUsers = (): Observable<Response> => {
        return this.http.get(this.actionUrl).map(res => res.json());
    }

    public GetUser = (id: number): Observable<Response> => {
        return this.http.get(this.actionUrl + id).map(res => res.json());
    }

    public InsertUser = (user: User): Observable<Response> => {
        var toAdd = JSON.stringify({ User: user });

        return this.http.post(this.actionUrl, toAdd, 
                    { headers: this.headers }).map(res => res.json());
    }

    public UpdateUser = (id: number, itemToUpdate: User): Observable<Response> => {
        return this.http.put(this.actionUrl + id, JSON.stringify(itemToUpdate), 
                    { headers: this.headers }).map(res => res.json());
    }

    public DeleteUser = (id: number): Observable<Response> => {
        return this.http.delete(this.actionUrl + id);
    }
}