/* * Copyright © 2012, 2013 Pedro Agullo Soliveres. * * This file is part of Log4js-ext. * * Log4js-ext is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License. * * Commercial use is permitted to the extent that the code/component(s) * do NOT become part of another Open Source or Commercially developed * licensed development library or toolkit without explicit permission. * * Log4js-ext is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Log4js-ext. If not, see <http://www.gnu.org/licenses/>. * * This software uses the ExtJs library (http://extjs.com), which is * distributed under the GPL v3 license (see http://extjs.com/license). */ /*jslint strict:false */ (function() { // "use strict"; //$NON-NLS-1$ /** * @abstract * * Base class for all layouts. * * A layout is in charge of formatting and augmenting the log data so that * it can be consumed by an appender. * * It knows about formatting the log time, obtaining an string that * represents all information in the log, adding special entries to a * logging event, nicely formatting a logged object, etc. * */ Ext.define('Sm.log.LayoutBase', { //$NON-NLS-1$ uses : ['Ext.Date', 'Ext.JSON', 'Sm.log.util.Debug', 'Sm.log.util.Assert'], config : { /** * @cfg * @accessor * * The format string used to format log time. * * See Ext.Date.format for information about this format string. */ timeFormat : "Y-m-d H:i:s.u", /** * @cfg * @accessor * * If set to true, an attempt will be made to highlight the logged * object JSON-formatted. * * It might be ignored if the JSON formatter does not support * highlighting. * */ highlightLoggedObject : true, /** * @cfg * @accessor * * If set to true, an attempt will be made to format the logged * object as a multiple line string. * * It might be ignored if the JSON formatter does not support * multiple line formatting. */ multilineLoggedObject : false, /** * @cfg * @accessor * * If set to true, the logged object will be output. */ exportFormattedLoggedObject : true, /** * @cfg * @accessor * * Desired indentation used to format the logged object. * * It might be ignored if the JSON formatter does not support * indenting. */ formatIndentLoggedObject : 3 }, /** * Appends extra formatted data to the original log event. * * By default, it provides formattedTime and formattedMessage * values, as well as a formattedLogObject if the appender is * configured to log the loggedObject too. * * Derived classes might want to add data for the output destination * to handle. For example, an output window might want to provide * a version of a formatted logged object that will be shown in a * single line, and another one formatted in multiple line, * to be shown in an expanded view. * * @protected * * @param {Sm.log.LoggingEvent} logEvent The original log data. * * @returns {void} */ appendFormattedData : function(logEvent) { var formattedLoggedObject; logEvent.formattedTime = this.formatTime(logEvent); logEvent.formattedMessage = this.formatMessage(logEvent); if( this.getExportFormattedLoggedObject() ) { if( logEvent.hasLoggedObject) { logEvent.formattedLoggedObject = this.formatLoggedObject(logEvent); } else { logEvent.formattedLoggedObject = ''; } } }, /** * Returns a text corresponding to the log. * * This is useful in scenarios in which the log is written in a * single line, such as the browser console. * * @protected * @abstract * @param {Sm.log.LoggingEvent} logEvent The log information. */ formatLogAsText : function(logEvent) { Sm.log.util.Debug.abort( "You must use an appender that knows " + "how to format a log event as a text" ); }, /** * Formats the log time. * * If there is no timeFormat, calls toString. * * @param {Sm.log.LoggingEvent} logEvent The log data. * * @protected * @returns */ formatTime : function(logEvent) { var time = logEvent.time; if( this.getTimeFormat() ) { return Ext.Date.format( time, this.getTimeFormat() ); } return time.toString(); }, /** * Formats the log message. * * This will use the extra formatting information provided in the log * method call to return a formatted message. Take a look at this [wiki * entry](http://code.google.com/p/log4js-ext/wiki/LoggingFormatting) * for examples on supported formatting. * * @param {Sm.log.LoggingEvent} logEvent The log data. * * @protected * @returns */ formatMessage : function(logEvent) { var message = logEvent.message, formatParams = logEvent.formatParams, useComplexLayout, args, template, i; if( formatParams ) { Sm.log.util.Assert.assert(formatParams.length > 0); useComplexLayout = Ext.isObject(formatParams[0]); if( !useComplexLayout ) { message = this.simpleFormat( message, formatParams ); } else { message = this.multiFormat( message, formatParams ); } } else { message = message.toString(); } return message; }, /** * Returns a formatted text corresponding to the logged object. * * @param {Sm.log.LoggingEvent} logEvent The log data. * @returns The formatted text corresponding to the logged object. * * @protected */ formatLoggedObject : function(logEvent) { if(logEvent.hasOwnProperty( "loggedObject" )) { return this.jsonLikeFormat(logEvent.loggedObject, this.getHighlightLoggedObject(), this.getMultilineLoggedObject() ); } return ''; }, /** * CSS styles used to highight logged object as JSON. * * @private */ jsonCls : { key : 'sm-log-json-key', string : 'sm-log-json-string', number : 'sm-log-json-number', bool : 'sm-log-json-boolean', nul : 'sm-log-json-null' }, /** * Highlights and makes more readable a JSON string corresponding * to a logged object. * * @param {String} json * The json string corresponding to the logged object. * @param jsonCls CSS classes used for highlighting. * * @returns {String} A string representing the highlighted logged object. * * @protected */ // Obtained from http://jsfiddle.net/KJQ9K/ : many thanks!! jsonSyntaxHighlight : function(json, jsonCls) { var regex; // Special case, we *want* undefined if( json === undefined ) { return '<span class="' + this.jsonCls.nul + '">undefined</span>'; } regex = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g; return json.replace(regex, function (match) { var cls = jsonCls.number, isKey = false, isString = false, result; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = jsonCls.key; isKey = true; } else { cls = jsonCls.string; isString = true; } } else if (/true|false/.test(match)) { cls = jsonCls.bool; } else if (/null/.test(match)) { cls = jsonCls.nul; } // ******************************************************* // Improve readability // Remove quotes from keys and add additional space if( isKey ) { // We *MUST* leave ':' here, because if it is out // of span, searches for something like 'key:' will not // find the key because there will be a 'hidden' span tag there. // And this kind of search shuld be rather common! match = match.slice(1, match.length - 2 ) + ": "; } // Replace quotes by single quotes from strings if( isString ) { match = "'" + match.slice(1, match.length -1 ) + "'"; } result = '<span class="' + cls + '">' + match + '</span>'; return result; }); }, /** * Returns a JSON string correponding to an object. * * @param obj The object to format. * @param {Boolean} highlight If true, attempts to provide * highlighting (html) * @param {Boolean} multiline Use multiple text lines? * @param {Number} indent Indentation level (spaces) * * @returns {String} A formatted JSON string representing an object. * * @protected */ jsonLikeFormat : function( obj, highlight, multiline, indent ) { var json; // Crazy as it might seem, just writing 'if(JSON)' generates // an error in IE 9. That's why we wrap call in try..catch, to // handle IE 9 failure :( // BTW: using if( window.JSON ) is *not* the solution, as that // does not pass all the tests :( try { // This one allows for better readability thanks to spacing json = JSON.stringify( obj, undefined, indent || this.getFormatIndentLoggedObject() ); } catch(ex) { json = Ext.JSON.encode(obj); } if( highlight ) { if( json !== undefined ) { // Replace spaces *before* adding html tags, // or the white space in the tags will get replaced too, // with bad effects json = json.replace(/&/g, '&').replace(/</g, '<'). replace(/>/g, '>'); if( multiline ) { json = json.replace( / /g, ' '); json = json.replace( /(\r\n|\n|\r)/gm, '<br>'); } } json = this.jsonSyntaxHighlight(json, this.jsonCls); } else { // Special case, we *want* undefined if( json === undefined) { return "undefined"; } } return json; }, /** * Formats a string depending on the format parameters a-la * Ext.Template. * * It is a poor man's version of Ext.Template.apply(..) that is not very * efficient *but* can cope with values not being present without * returning an empty string, unlike Ext.Template * * @protected * * @param {String} str The string to format. * @param {Array} formatParams Formatting parameters * @returns {String} The formatted string. */ multiFormat : function( str, formatParams ) { var i, result = str, formatKeyValue; // Inlined function to avoid warnings with my IDE: else, I would // have defined it inline in the 'each' iteration below formatKeyValue = function( key, value ) { result = result.replace( "{" + key + "}", value.toString() ); }; for( i = 0; i < formatParams.length; i = i + 1) { Ext.Object.each(formatParams[i], formatKeyValue ); } return result; }, /** * Format a string using the format parameters, a-la Ext.String.format. * * @protected * * @param {String} str The string to format. * @param {Array} formatParams Formatting parameters * * @returns {String} The formatted String */ simpleFormat : function( str, formatParams ) { var i, result = str, formatKeyValue; for( i = 0; i < formatParams.length; i = i + 1) { result = result.replace( "{" + i + "}", formatParams[i]); } return result; }, /** * Call this method from derived classes. * * @protected * * @param cfg */ constructor : function(cfg) { this.initConfig(cfg); } }); }());