mirror of
				https://github.com/elyby/accounts-frontend.git
				synced 2025-05-31 14:11:58 +05:30 
			
		
		
		
	#315: improve logger errors serialization
This commit is contained in:
		
							
								
								
									
										154
									
								
								src/services/logger/abbreviate.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/services/logger/abbreviate.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
			
		||||
const STRING_MAX_LENGTH = 128*1024;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a copy of any object without non-serializable elements to make result safe for JSON.stringify().
 | 
			
		||||
 * Guaranteed to never throw.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {any}    obj     Any data structure
 | 
			
		||||
 * @param {object} options
 | 
			
		||||
 * @param {function} options.filter - callback that is called on every object's key with (key,value) and should return
 | 
			
		||||
 *        value to use (may return undefined to remove unwanted keys). See nodeFilter and browserFilter.
 | 
			
		||||
 * @param {number} options.depth - maximum recursion depth. Elements deeper than that are stringified with util.inspect()
 | 
			
		||||
 * @param {number} options.maxSize - roughly maximum allowed size of data after JSON serialisation (but it's not guaranteed
 | 
			
		||||
 *        that it won't exceed the limit)
 | 
			
		||||
 *
 | 
			
		||||
 * @see https://github.com/ftlabs/js-abbreviate
 | 
			
		||||
 */
 | 
			
		||||
function abbreviate(obj, options) {
 | 
			
		||||
    if (!options) options = {};
 | 
			
		||||
 | 
			
		||||
    var filter = options.filter || function(k,v){return v;};
 | 
			
		||||
    var maxDepth = options.depth || 10;
 | 
			
		||||
    var maxSize = options.maxSize || 1*1024*1024;
 | 
			
		||||
 | 
			
		||||
    return abbreviateRecursive(undefined, obj, filter, {sizeLeft: maxSize}, maxDepth);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function limitStringLength(str) {
 | 
			
		||||
    if (str.length > STRING_MAX_LENGTH) {
 | 
			
		||||
        return str.substring(0, STRING_MAX_LENGTH/2) + ' … ' + str.substring(str.length - STRING_MAX_LENGTH/2);
 | 
			
		||||
    }
 | 
			
		||||
    return str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function abbreviateRecursive(key, obj, filter, state, maxDepth) {
 | 
			
		||||
    if (state.sizeLeft < 0) {
 | 
			
		||||
        return '**skipped**';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.sizeLeft -= 5; // rough approximation of JSON overhead
 | 
			
		||||
 | 
			
		||||
    obj = filter(key, obj);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        switch(typeof obj) {
 | 
			
		||||
            case 'object':
 | 
			
		||||
                if (null === obj) {
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (maxDepth < 0) {
 | 
			
		||||
                    break; // fall back to stringification
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var newobj = Array.isArray(obj) ? [] : {};
 | 
			
		||||
                for(var i in obj) {
 | 
			
		||||
                    newobj[i] = abbreviateRecursive(i, obj[i], filter, state, maxDepth-1);
 | 
			
		||||
                    if (state.sizeLeft < 0) break;
 | 
			
		||||
                }
 | 
			
		||||
                return newobj;
 | 
			
		||||
 | 
			
		||||
            case 'string':
 | 
			
		||||
                obj = limitStringLength(obj);
 | 
			
		||||
                state.sizeLeft -= obj.length;
 | 
			
		||||
                return obj;
 | 
			
		||||
 | 
			
		||||
            case 'number':
 | 
			
		||||
            case 'boolean':
 | 
			
		||||
            case 'undefined':
 | 
			
		||||
                return obj;
 | 
			
		||||
        }
 | 
			
		||||
    } catch(e) {/* fall back to inspect*/}
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        obj = limitStringLength('' + obj);
 | 
			
		||||
        state.sizeLeft -= obj.length;
 | 
			
		||||
        return obj;
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        return "**non-serializable**";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function commonFilter(key, val) {
 | 
			
		||||
    if ('function' === typeof val) {
 | 
			
		||||
        return undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (val instanceof Date) {
 | 
			
		||||
        return "**Date** " + val;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (val instanceof Error) {
 | 
			
		||||
        var err = {
 | 
			
		||||
            // These properties are implemented as magical getters and don't show up in for in
 | 
			
		||||
            stack: val.stack,
 | 
			
		||||
            message: val.message,
 | 
			
		||||
            name: val.name,
 | 
			
		||||
        };
 | 
			
		||||
        for(var i in val) {
 | 
			
		||||
            err[i] = val[i];
 | 
			
		||||
        }
 | 
			
		||||
        return err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function nodeFilter(key, val) {
 | 
			
		||||
 | 
			
		||||
    // domain objects are huge and have circular references
 | 
			
		||||
    if (key === 'domain' && 'object' === typeof val && val._events) {
 | 
			
		||||
        return "**domain ignored**";
 | 
			
		||||
    }
 | 
			
		||||
    if (key === 'domainEmitter') {
 | 
			
		||||
        return "**domainEmitter ignored**";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (val === global) {
 | 
			
		||||
        return "**global**";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return commonFilter(key, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function browserFilter(key, val) {
 | 
			
		||||
    if (val === window) {
 | 
			
		||||
        return "**window**";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (val === document) {
 | 
			
		||||
        return "**document**";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (val instanceof HTMLElement) {
 | 
			
		||||
        var outerHTML = val.outerHTML;
 | 
			
		||||
        if ('undefined' != typeof outerHTML) {
 | 
			
		||||
            return "**HTMLElement** " + outerHTML;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return commonFilter(key, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
    abbreviate,
 | 
			
		||||
    nodeFilter,
 | 
			
		||||
    browserFilter
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function(obj) {
 | 
			
		||||
    return abbreviate(obj, {
 | 
			
		||||
        filter: browserFilter
 | 
			
		||||
    })
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										3
									
								
								src/services/logger/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/services/logger/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
import logger from './logger';
 | 
			
		||||
 | 
			
		||||
export default logger;
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
import Raven from 'raven-js';
 | 
			
		||||
 | 
			
		||||
import abbreviate from './abbreviate';
 | 
			
		||||
 | 
			
		||||
const isTest = process.env.__TEST__; // eslint-disable-line
 | 
			
		||||
const isProduction = process.env.__PROD__; // eslint-disable-line
 | 
			
		||||
 | 
			
		||||
@@ -72,6 +74,8 @@ const logger = {
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        context = abbreviate(context); // prepare data for JSON.stringify
 | 
			
		||||
 | 
			
		||||
        console[method](message, context); // eslint-disable-line
 | 
			
		||||
 | 
			
		||||
        Raven.captureException(message, {
 | 
			
		||||
		Reference in New Issue
	
	Block a user