node.jsangularionic-frameworkionic2logentries

Logging in Typescript using Bunyan and Logentries


I want to setup remote Logging using logentries.com in my ionic app.

This an extract from my package.json:

  "dependencies": {

    "bunyan": "^1.8.5",
    "bunyan-logentries": "^1.2.0",

  },
  "devDependencies": {
    "@types/bunyan": "0.0.35",
    "@types/bunyan-logentries": "0.1.30",
    "typescript": "2.0.9"
  },

Code

import {createStream} from "bunyan-logentries";
import * as Logger from "bunyan";

// ...
constructor() {
    let token = 'xxxxxxxxxxxxxxxxx';
    let log = Logger.createLogger({
      name: '',
      streams: [{
        level: 'info',
        stream: createStream({token: token}),
        type: 'raw'
       }]
    });
    log.info("Created log");
 }

The problem

My IDE does not warn me about any errors. But as soon as I run the app I get the following error:

exists is not a function. (In 'exists(pkgPath)', 'exists' is undefined)

findPackage@http://localhost:8101/build/main.js:131257:18
register@http://localhost:8101/build/main.js:131332:31
http://localhost:8101/build/main.js:112585:50
http://localhost:8101/build/main.js:113391:34
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:129423:37
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:29972:95
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:80592:90
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:59390:96
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:59495:94
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:128048:94
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:116775:92
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:149625:89
__webpack_require__@http://localhost:8101/build/main.js:20:34
http://localhost:8101/build/main.js:66:37
global code@http://localhost:8101/build/main.js:67:12 

I think the core of the problem is that the @types don't match the actual node modules, but it is unclear how this is supposed to be addressed.


Solution

  • I have faced the same issue. The bunyan-logentries module depends on le_node: the logentries module for node.

    le_node uses the net and tls modules that are not available from the browser.

    To move on, I have implemented a custom bunyan stream that sends the logs to logentries via their REST API. It is straightforward to code and it works.

    Here is a sample code to illustrate this solution.

    The LoggerService that creates Bunyan Instances:

    @Injectable()
    export class LoggerService {
        constructor(private http: Http) {
        }
    
        public create(name: string): Logger{
            return Bunyan.createLogger({
                name: name,
                streams: [{
                    stream: new LogentriesBunyanStream(AppConfig.LogEntries.token, this.http),
                    type: 'raw'
                }]
            });
        }
    }
    

    The bunyan custom stream that sends logs to logentries:

    export class LogentriesBunyanStream extends Writable {
        private token: string;
        private http: Http;
        
        constructor(token: string, http: Http) {
            super({objectMode: true});
            this.http = http;
            this.token = token;
        }
            
        public _write(rec: any, encoding?: string, cb?: Function) {
            // Replace the level code with a friendly string 
            rec.level = LogentriesBunyanStream.resolveLevel(rec.level);
            
            // Send the post request to logentries
            // https://docs.logentries.com/docs/http-post
            const LOGENTRIES_URL = "https://webhook.logentries.com/noformat/logs/";
            let headers = new Headers();
            headers.append("Content-Type", 'application/json');
            let requestoptions = new RequestOptions({
                method: RequestMethod.Post,
                headers: headers,
                body: JSON.stringify(rec)
            });
            this.http.request(LOGENTRIES_URL + this.token, requestoptions).toPromise().then((response) => {
                cb();
            }).catch((error) =>{
                console.log("faield to send log to the logentries server");
            });
        };
    
        private static resolveLevel(bunyanLevel) {
            let levelToName = {
                10: 'DEBUG',
                20: 'DEBUG',
                30: 'INFO',
                40: 'WARN',
                50: 'ERROR',
                60: 'CRIT'
            };
            return levelToName[bunyanLevel] || 'INFO';
        }
    };
    

    Maybe it helps,

    Regards.