Path: blob/trunk/third_party/closure/goog/debug/formatter.js
4087 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Definition of various formatters for logging. Please minimize8* dependencies this file has on other closure classes as any dependency it9* takes won't be able to use the logging infrastructure.10*/1112goog.provide('goog.debug.Formatter');13goog.provide('goog.debug.HtmlFormatter');14goog.provide('goog.debug.TextFormatter');15goog.provide('goog.debug.formatter');1617goog.require('goog.debug');18goog.require('goog.debug.RelativeTimeProvider');19goog.require('goog.html.SafeHtml');20goog.require('goog.html.SafeUrl');21goog.require('goog.html.uncheckedconversions');22goog.require('goog.log');23goog.require('goog.string.Const');24goog.requireType('goog.log.LogRecord');252627/**28* Base class for Formatters. A Formatter is used to format a LogRecord into29* something that can be displayed to the user.30*31* @param {string=} opt_prefix The prefix to place before text records.32* @constructor33*/34goog.debug.formatter.Formatter = function(opt_prefix) {35'use strict';36this.prefix_ = opt_prefix || '';3738/**39* A provider that returns the relative start time.40* @type {goog.debug.RelativeTimeProvider}41* @private42*/43this.startTimeProvider_ =44goog.debug.RelativeTimeProvider.getDefaultInstance();45};464748/**49* Whether to append newlines to the end of formatted log records.50* @type {boolean}51*/52goog.debug.formatter.Formatter.prototype.appendNewline = true;535455/**56* Whether to show absolute time in the DebugWindow.57* @type {boolean}58*/59goog.debug.formatter.Formatter.prototype.showAbsoluteTime = true;606162/**63* Whether to show relative time in the DebugWindow.64* @type {boolean}65*/66goog.debug.formatter.Formatter.prototype.showRelativeTime = true;676869/**70* Whether to show the logger name in the DebugWindow.71* @type {boolean}72*/73goog.debug.formatter.Formatter.prototype.showLoggerName = true;747576/**77* Whether to show the logger exception text.78* @type {boolean}79*/80goog.debug.formatter.Formatter.prototype.showExceptionText = false;818283/**84* Whether to show the severity level.85* @type {boolean}86*/87goog.debug.formatter.Formatter.prototype.showSeverityLevel = false;888990/**91* Formats a record.92* @param {?goog.log.LogRecord} logRecord the logRecord to format.93* @return {string} The formatted string.94*/95goog.debug.formatter.Formatter.prototype.formatRecord = goog.abstractMethod;969798/**99* Formats a record as SafeHtml.100* @param {?goog.log.LogRecord} logRecord the logRecord to format.101* @return {!goog.html.SafeHtml} The formatted string as SafeHtml.102*/103goog.debug.formatter.Formatter.prototype.formatRecordAsHtml =104goog.abstractMethod;105106107/**108* Sets the start time provider. By default, this is the default instance109* but can be changed.110* @param {goog.debug.RelativeTimeProvider} provider The provider to use.111*/112goog.debug.formatter.Formatter.prototype.setStartTimeProvider = function(113provider) {114'use strict';115this.startTimeProvider_ = provider;116};117118119/**120* Returns the start time provider. By default, this is the default instance121* but can be changed.122* @return {goog.debug.RelativeTimeProvider} The start time provider.123*/124goog.debug.formatter.Formatter.prototype.getStartTimeProvider = function() {125'use strict';126return this.startTimeProvider_;127};128129130/**131* Resets the start relative time.132*/133goog.debug.formatter.Formatter.prototype.resetRelativeTimeStart = function() {134'use strict';135this.startTimeProvider_.reset();136};137138139/**140* Returns a string for the time/date of the LogRecord.141* @param {?goog.log.LogRecord} logRecord The record to get a time stamp for.142* @return {string} A string representation of the time/date of the LogRecord.143* @private144*/145goog.debug.formatter.Formatter.getDateTimeStamp_ = function(logRecord) {146'use strict';147var time = new Date(logRecord.getMillis());148return goog.debug.formatter.Formatter.getTwoDigitString_(149(time.getFullYear() - 2000)) +150goog.debug.formatter.Formatter.getTwoDigitString_((time.getMonth() + 1)) +151goog.debug.formatter.Formatter.getTwoDigitString_(time.getDate()) + ' ' +152goog.debug.formatter.Formatter.getTwoDigitString_(time.getHours()) + ':' +153goog.debug.formatter.Formatter.getTwoDigitString_(time.getMinutes()) +154':' +155goog.debug.formatter.Formatter.getTwoDigitString_(time.getSeconds()) +156'.' +157goog.debug.formatter.Formatter.getTwoDigitString_(158Math.floor(time.getMilliseconds() / 10));159};160161162/**163* Returns the number as a two-digit string, meaning it prepends a 0 if the164* number if less than 10.165* @param {number} n The number to format.166* @return {string} A two-digit string representation of `n`.167* @private168*/169goog.debug.formatter.Formatter.getTwoDigitString_ = function(n) {170'use strict';171if (n < 10) {172return '0' + n;173}174return String(n);175};176177178/**179* Returns a string for the number of seconds relative to the start time.180* Prepads with spaces so that anything less than 1000 seconds takes up the181* same number of characters for better formatting.182* @param {?goog.log.LogRecord} logRecord The log to compare time to.183* @param {number} relativeTimeStart The start time to compare to.184* @return {string} The number of seconds of the LogRecord relative to the185* start time.186* @private187*/188goog.debug.formatter.Formatter.getRelativeTime_ = function(189logRecord, relativeTimeStart) {190'use strict';191var ms = logRecord.getMillis() - relativeTimeStart;192var sec = ms / 1000;193var str = sec.toFixed(3);194195var spacesToPrepend = 0;196if (sec < 1) {197spacesToPrepend = 2;198} else {199while (sec < 100) {200spacesToPrepend++;201sec *= 10;202}203}204while (spacesToPrepend-- > 0) {205str = ' ' + str;206}207return str;208};209210211212/**213* Formatter that returns formatted html. See formatRecord for the classes214* it uses for various types of formatted output.215*216* @param {string=} opt_prefix The prefix to place before text records.217* @constructor218* @extends {goog.debug.formatter.Formatter}219*/220goog.debug.formatter.HtmlFormatter = function(opt_prefix) {221'use strict';222goog.debug.formatter.Formatter.call(this, opt_prefix);223};224goog.inherits(225goog.debug.formatter.HtmlFormatter, goog.debug.formatter.Formatter);226227228/**229* Exposes an exception that has been caught by a try...catch and outputs the230* error as HTML with a stack trace.231*232* @param {*} err Error object or string.233* @param {?Function=} fn If provided, when collecting the stack trace all234* frames above the topmost call to this function, including that call,235* will be left out of the stack trace.236* @return {string} Details of exception, as HTML.237*/238goog.debug.formatter.HtmlFormatter.exposeException = function(err, fn) {239'use strict';240var html = goog.debug.formatter.HtmlFormatter.exposeExceptionAsHtml(err, fn);241return goog.html.SafeHtml.unwrap(html);242};243244245/**246* Exposes an exception that has been caught by a try...catch and outputs the247* error with a stack trace.248*249* @param {*} err Error object or string.250* @param {?Function=} fn If provided, when collecting the stack trace all251* frames above the topmost call to this function, including that call,252* will be left out of the stack trace.253* @return {!goog.html.SafeHtml} Details of exception.254*/255goog.debug.formatter.HtmlFormatter.exposeExceptionAsHtml = function(err, fn) {256'use strict';257try {258var e = goog.debug.normalizeErrorObject(err);259// Create the error message260var viewSourceUrl =261goog.debug.formatter.HtmlFormatter.createViewSourceUrl_(e.fileName);262var error = goog.html.SafeHtml.concat(263goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(264'Message: ' + e.message + '\nUrl: '),265goog.html.SafeHtml.create(266'a', {href: viewSourceUrl, target: '_new'}, e.fileName),267goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(268'\nLine: ' + e.lineNumber + '\n\nBrowser stack:\n' + e.stack +269'-> ' +270'[end]\n\nJS stack traversal:\n' + goog.debug.getStacktrace(fn) +271'-> '));272return error;273} catch (e2) {274return goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(275'Exception trying to expose exception! You win, we lose. ' + e2);276}277};278279280/**281* @param {?string=} fileName282* @return {!goog.html.SafeUrl} SafeUrl with view-source scheme, pointing at283* fileName.284* @private285*/286goog.debug.formatter.HtmlFormatter.createViewSourceUrl_ = function(fileName) {287'use strict';288if (fileName == null) {289fileName = '';290}291if (!/^https?:\/\//i.test(fileName)) {292return goog.html.SafeUrl.fromConstant(293goog.string.Const.from('sanitizedviewsrc'));294}295var sanitizedFileName = goog.html.SafeUrl.sanitize(fileName);296return goog.html.uncheckedconversions297.safeUrlFromStringKnownToSatisfyTypeContract(298goog.string.Const.from('view-source scheme plus HTTP/HTTPS URL'),299'view-source:' + goog.html.SafeUrl.unwrap(sanitizedFileName));300};301302303304/**305* Whether to show the logger exception text306* @type {boolean}307* @override308*/309goog.debug.formatter.HtmlFormatter.prototype.showExceptionText = true;310311312/**313* Formats a record314* @param {?goog.log.LogRecord} logRecord the logRecord to format.315* @return {string} The formatted string as html.316* @override317*/318goog.debug.formatter.HtmlFormatter.prototype.formatRecord = function(319logRecord) {320'use strict';321if (!logRecord) {322return '';323}324// OK not to use goog.html.SafeHtml.unwrap() here.325return this.formatRecordAsHtml(logRecord).getTypedStringValue();326};327328329/**330* Formats a record.331* @param {?goog.log.LogRecord} logRecord the logRecord to format.332* @return {!goog.html.SafeHtml} The formatted string as SafeHtml.333* @override334*/335goog.debug.formatter.HtmlFormatter.prototype.formatRecordAsHtml = function(336logRecord) {337'use strict';338if (!logRecord) {339return goog.html.SafeHtml.EMPTY;340}341342var className;343switch (logRecord.getLevel().value) {344case goog.log.Level.SHOUT.value:345className = 'dbg-sh';346break;347case goog.log.Level.SEVERE.value:348className = 'dbg-sev';349break;350case goog.log.Level.WARNING.value:351className = 'dbg-w';352break;353case goog.log.Level.INFO.value:354className = 'dbg-i';355break;356case goog.log.Level.FINE.value:357default:358className = 'dbg-f';359break;360}361362// HTML for user defined prefix, time, logger name, and severity.363var sb = [];364sb.push(this.prefix_, ' ');365if (this.showAbsoluteTime) {366sb.push(367'[', goog.debug.formatter.Formatter.getDateTimeStamp_(logRecord), '] ');368}369if (this.showRelativeTime) {370sb.push(371'[',372goog.debug.formatter.Formatter.getRelativeTime_(373logRecord, this.startTimeProvider_.get()),374's] ');375}376if (this.showLoggerName) {377sb.push('[', logRecord.getLoggerName(), '] ');378}379if (this.showSeverityLevel) {380sb.push('[', logRecord.getLevel().name, '] ');381}382var fullPrefixHtml =383goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(sb.join(''));384385// HTML for exception text and log record.386var exceptionHtml = goog.html.SafeHtml.EMPTY;387if (this.showExceptionText && logRecord.getException()) {388exceptionHtml = goog.html.SafeHtml.concat(389goog.html.SafeHtml.BR,390goog.debug.formatter.HtmlFormatter.exposeExceptionAsHtml(391logRecord.getException()));392}393var logRecordHtml = goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(394logRecord.getMessage());395var recordAndExceptionHtml = goog.html.SafeHtml.create(396'span', {'class': className},397goog.html.SafeHtml.concat(logRecordHtml, exceptionHtml));398399400// Combine both pieces of HTML and, if needed, append a final newline.401var html;402if (this.appendNewline) {403html = goog.html.SafeHtml.concat(404fullPrefixHtml, recordAndExceptionHtml, goog.html.SafeHtml.BR);405} else {406html = goog.html.SafeHtml.concat(fullPrefixHtml, recordAndExceptionHtml);407}408return html;409};410411412413/**414* Formatter that returns formatted plain text415*416* @param {string=} opt_prefix The prefix to place before text records.417* @constructor418* @extends {goog.debug.formatter.Formatter}419* @final420*/421goog.debug.formatter.TextFormatter = function(opt_prefix) {422'use strict';423goog.debug.formatter.Formatter.call(this, opt_prefix);424};425goog.inherits(426goog.debug.formatter.TextFormatter, goog.debug.formatter.Formatter);427428429/**430* Formats a record as text431* @param {?goog.log.LogRecord} logRecord the logRecord to format.432* @return {string} The formatted string.433* @override434*/435goog.debug.formatter.TextFormatter.prototype.formatRecord = function(436logRecord) {437'use strict';438var sb = [];439sb.push(this.prefix_, ' ');440if (this.showAbsoluteTime) {441sb.push(442'[', goog.debug.formatter.Formatter.getDateTimeStamp_(logRecord), '] ');443}444if (this.showRelativeTime) {445sb.push(446'[',447goog.debug.formatter.Formatter.getRelativeTime_(448logRecord, this.startTimeProvider_.get()),449's] ');450}451452if (this.showLoggerName) {453sb.push('[', logRecord.getLoggerName(), '] ');454}455if (this.showSeverityLevel) {456sb.push('[', logRecord.getLevel().name, '] ');457}458sb.push(logRecord.getMessage());459if (this.showExceptionText) {460var exception = logRecord.getException();461if (exception !== undefined) {462var exceptionText =463exception instanceof Error ? exception.message : String(exception);464sb.push('\n', exceptionText);465}466}467if (this.appendNewline) {468sb.push('\n');469}470return sb.join('');471};472473474/**475* Formats a record as text476* @param {?goog.log.LogRecord} logRecord the logRecord to format.477* @return {!goog.html.SafeHtml} The formatted string as SafeHtml. This is478* just an HTML-escaped version of the text obtained from formatRecord().479* @override480*/481goog.debug.formatter.TextFormatter.prototype.formatRecordAsHtml = function(482logRecord) {483'use strict';484return goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(485goog.debug.formatter.TextFormatter.prototype.formatRecord(logRecord));486};487488// Aliases for the above formatters.489// TODO(user): Delete these aliases when there are no more usages.490491/**492* @constructor493*/494goog.debug.Formatter = goog.debug.formatter.Formatter;495496/**497* @constructor498*/499goog.debug.TextFormatter = goog.debug.formatter.TextFormatter;500501/**502* @constructor503*/504goog.debug.HtmlFormatter = goog.debug.formatter.HtmlFormatter;505506507