我想扩展'console.log'函数以向其输出添加其他信息 - 但我不想影响浏览器在控制台窗口中生成的脚本名称/行号信息。看看如果我创建自己的实现,我得到无用的跟踪信息,我是否应该找到代码区域...(它们都链接到日志实现,而不是导致日志消息的实际脚本)
基本上,我的应用程序是一个非常可插拔的基础架构,任何日志输出都可能出现在任意数量的帧中。 因此,我希望每条日志消息都在日志消息的开头包含一个特殊的唯一标识符。
我尝试用自己的方法替换console.log方法,但chrome抱怨
Uncaught TypeError: Illegal invocation
这是我覆盖它的方式
var orig = console.log;
console.log = function( message )
{
orig( (window == top ? '[root]' : '[' + window.name + ']') + ': ' + message );
}
有什么想法吗?
[编辑] 注意:修复“非法调用”问题后,覆盖率似乎仍然“污染”文件名/编号...
[编辑] 看起来一般的答案是 - 不 - 尽管有一些令人困惑的鹅追逐,但是在当前版本的浏览器中无法实现所需的功能。
答案 0 :(得分:20)
是的,可以添加信息而不会弄乱日志调用的原始行号。这里的一些其他答案很接近,但诀窍是让您的自定义日志记录方法返回修改后的记录器。下面是一个简单的示例,仅使用上下文变体进行了适度测试。
log = function() {
var context = "My Descriptive Logger Prefix:";
return Function.prototype.bind.call(console.log, console, context);
}();
这可用于:
log("A log message...");
这是一个jsfiddle:http://jsfiddle.net/qprro98v/
可以轻松获得创意并传递上下文变量,并从函数定义中删除自动执行的parens。即log(“DEBUG:”)(“调试消息”),log(“INFO:”)(“这是一些信息”)等。
关于函数的唯一真正导入部分(关于行号)是它返回记录器。
答案 1 :(得分:10)
如果您的用例可以处理一些限制, 可以使其成为可行的方式。限制是:
必须在绑定时计算额外的日志内容;它不能是时间敏感的,也不能以任何方式依赖传入的日志消息。
额外的日志内容只能放在日志消息的开头。
有了这些限制,以下内容可能对您有用:
var context = "ALIASED LOG:"
var logalias;
if (console.log.bind === 'undefined') { // IE < 10
logalias = Function.prototype.bind.call(console.log, console, context);
}
else {
logalias = console.log.bind(console, context);
}
logalias('Hello, world!');
答案 2 :(得分:8)
实际上至少可以使用chrome。这是最相关的。这可能会因设置而异,我获得分组的方法只是记录整个堆栈,并找到我需要的信息。
var stack = new Error().stack;
var file = stack.split("\n")[2].split("/")[4].split("?")[0]
var line = stack.split("\n")[2].split(":")[5];
这就是整个事情,保留了本机对象的记录。
var orig = console.log
console.log = function(input) {
var isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
if(isChrome){
var stack = new Error().stack;
var file = stack.split("\n")[2].split("/")[4].split("?")[0]
var line = stack.split("\n")[2].split(":")[5];
var append = file + ":" + line;
}
orig.apply(console, [input, append])
}
答案 3 :(得分:4)
可接受的解决方案是创建自己的日志函数,该函数返回与日志参数绑定的console.log
函数。
log = function() {
// Put your extension code here
var args = Array.prototype.slice.call(arguments);
args.unshift(console);
return Function.prototype.bind.apply(console.log, args);
}
// Note the extra () to call the original console.log
log("Foo", {bar: 1})();
&#13;
这样console.log
调用将从正确的行开始,并将在控制台中很好地显示,允许您点击它和所有内容。
答案 4 :(得分:3)
您需要使用正确的上下文(console.log
)调用console
:
orig.call(console, message);
完成允许多个参数的函数:
var orig = console.log;
console.log = function() {
var msgs = [],
prefix = (window== top ? '[root]' : '[' + window.name + ']');
while(arguments.length) {
msgs.push(prefix + ': ' + [].shift.call(arguments));
}
orig.apply(console, msgs);
};
请记住,当使用 + 符号将对象与字符串组合时,您会在控制台中丢失内置对象/数组浏览器。
答案 5 :(得分:2)
我刚刚在一篇帖子中回答了这个问题,该帖子帮我回答了原来的“别名”问题:
(http://stackoverflow.com/a/12942764/401735)
my_log_alias = console.log.bind(console)
显然,执行此操作的能力已经过设计。经过测试。的工作原理。
此后my_log_alias与console.log相同,可以以相同的方式调用;从函数内部调用此函数将报告该函数调用的行号,包括别名或建议函数内的行(如果适用)。
具体来说,Chrome提供的行号会告诉您该行所在的文件,因此您所做的可能是不必要的;考虑将此报告为chrome中的错误/功能请求,它在console.log中提供此信息。
答案 6 :(得分:1)
Christopher Currie提供了一个出色的解决方案。为了我的需要,我已经扩展了一点。这是AMD模块:
define([], function () {
var enableDebug = true;
var separator = ">";
function bind(f, thisArg, ctx) {
if (f.bind !== 'undefined') { // IE < 10
return Function.prototype.bind.call(f, thisArg, ctx);
}
else {
return f.bind(thisArg, ctx);
}
}
function newConsole(context, parentConsole) {
var log;
var debug;
var warn;
var error;
if (!parentConsole) {
parentConsole = console;
}
context = context + separator;
if (enableDebug) {
debug = bind(console.log, console, context + "DEBUG" + separator);
} else {
debug = function () {
// suppress all debug messages
};
}
log = bind(console.log, console, context);
warn = bind(console.warn, console, context);
error = bind(console.error, console, context);
return {
debug: debug,
info: log,
log: log,
warn: warn,
error: error,
/* access console context information */
context: context,
/* create a new console with nested context */
nest: function (subContext) {
return newConsole(context + subContext, this);
},
parent: parentConsole
};
}
return newConsole("");
});
默认情况下,这将输出> {message}
。您还可以将嵌套上下文添加到日志记录中,例如console.nest("my").log("test")
将输出>my> test
。
我还添加了一个debug
函数,该函数将使用>DEBUG>
希望有人会发现它很有用。
答案 7 :(得分:1)
我已经多次调查过,并且总是发现这是不可能的。
如果您感兴趣,我的解决方法是将控制台分配给另一个变量,然后将所有日志消息包装在一个函数中,该函数允许我修改/样式/消息上的任何内容。
使用CoffeeScript看起来很不错,不确定它与普通JS的实用性。
我养成了用x
为前缀添加前缀的习惯。
logger.debug x 'Foo'
log x 'Bar'
log x('FooBar %o'), obj
答案 8 :(得分:1)
毫无疑问,这是不可能的,将来我们可以使用ECMAScript 6中的Proxy对象来完成。
我的用例是使用有用的信息(如传递的参数和执行方法)自动为控制台消息添加前缀。目前我最接近的是使用Function.prototype.apply
。
一个简单的方法就是编写调试语句:
console.info('=== LazyLoad.css(', arguments, '): css files are skipped, gives us a clean slate to style within theme\'s CSS.');
一个复杂的方法是使用如下的帮助函数,我个人现在更喜欢简单的方法。
/* Debug prefixing function
* ===========================
*
* A helper used to provide useful prefixing information
* when calling `console.log`, `console.debug`, `console.error`.
* But the catch is that to utilize one must leverage the
* `.apply` function as shown in the below examples.
*
* ```
* console.debug.apply(console, _fDebugPrefix(arguments)
* .concat('your message'));
*
* // or if you need to pass non strings
* console.debug.apply(console, _fDebugPrefix(arguments)
* .concat('json response was:', oJson));
*
*
* // if you need to use strict mode ("use strict") one can't
* // extract the function name but following approach works very
* // well; updating the name is just a matter of search and replace
* var aDebugPrefix = ['fYourFunctionName('
* ,Array.prototype.slice.call(arguments, 0),
* ,')'];
* console.debug.apply(console,
* aDebugPrefix.concat(['json response was:', oJson]));
* ```
*/
function _fDebugPrefix(oArguments) {
try {
return [oArguments.callee.name + '('
,Array.prototype.slice.call(oArguments, 0)
, ')'];
}
catch(err) { // are we in "use strict" mode ?
return ['<callee.name unsupported in "use strict">('
,Array.prototype.slice.call(oArguments, 0)
, ')'];
}
}
答案 9 :(得分:1)
TS/JS 中的可重用类
// File: LogLevel.ts
enum LogLevel {
error = 0,
warn,
info,
debug,
verbose,
}
export default LogLevel;
// File: Logger.js
import LogLevel from "./LogLevel";
export default class Logger {
static id = "App";
static level = LogLevel.info;
constructor(id) {
this.id = id;
const commonPrefix = `[${Logger.id}/${this.id}]`;
const verboseContext = `[V]${commonPrefix}`;
if (console.log.bind === "undefined") {
// IE < 10
this.verbose = Function.prototype.bind.call(console.log, console, verboseContext);
} else {
this.verbose = console.log.bind(console, verboseContext);
}
if (LogLevel.verbose > Logger.level) {
this.verbose = function() {
return // Suppress
};
}
const debugContext = `[D]${commonPrefix}`;
if (console.debug.bind === "undefined") {
// IE < 10
this.debug = Function.prototype.bind.call(console.debug, console, debugContext);
} else {
this.debug = console.debug.bind(console, debugContext);
}
if (LogLevel.debug > Logger.level) {
this.debug = function() {
return // Suppress
};
}
const infoContext = `[I]${commonPrefix}`;
if (console.info.bind === "undefined") {
// IE < 10
this.info = Function.prototype.bind.call(console.info, console, infoContext);
} else {
this.info = console.info.bind(console, infoContext);
}
if (LogLevel.info > Logger.level) {
this.info = function() {
return // Suppress
};
}
const warnContext = `[W]${commonPrefix}`;
if (console.warn.bind === "undefined") {
// IE < 10
this.warn = Function.prototype.bind.call(console.warn, console, warnContext);
} else {
this.warn = console.warn.bind(console, warnContext);
}
if (LogLevel.warn > Logger.level) {
this.warn = function() {
return // Suppress
};
}
const errorContext = `[E]${commonPrefix}`;
if (console.error.bind === "undefined") {
// IE < 10
this.error = Function.prototype.bind.call(console.error, console, errorContext);
} else {
this.error = console.error.bind(console, errorContext);
}
if (LogLevel.error > Logger.level) {
this.error = function() {
return // Suppress
};
}
}
}
用法(反应):
// File: src/index.tsx
// ...
Logger.id = "MCA"
const env = new Env()
if (env.env == Environment.dev) {
Logger.level = LogLevel.verbose
const log = new Logger("Main")
log.info("Environment is 'Development'")
}
///...
// File: src/App/CookieConsent/index.tsx
import React, { useEffect } from "react";
import { useCookies } from "react-cookie";
import "./index.scss";
import Logger from "@lib/Logger" // @lib is just alias configured in webpack.
const cookieName = "mca-cookie-consent";
// const log = new Logger(CookieConsent.name) // IMPORTANT! Don't put log instance here. It is too early! Put inside function.
export default function CookieConsent(): JSX.Element {
const log = new Logger(CookieConsent.name) // IMPORTANT! Have to be inside function, not in global scope (after imports)
useEffect(() => {
log.verbose(`Consent is accepted: ${isAccepted()}`);
}, []);
const [cookie, setCookie] = useCookies([cookieName]);
function isAccepted(): boolean {
return cookie[cookieName] != undefined;
}
function containerStyle(): React.CSSProperties {
return isAccepted() ? { display: "none" } : {};
}
function handleClick() {
const expires = new Date();
expires.setFullYear(expires.getFullYear() + 1);
log.verbose(`Accepted cookie consent. Expiration: ${expires}`)
setCookie(cookieName, true, { path: "/", expires: expires, sameSite: "lax" });
}
return (
<div className="cookieContainer" style={containerStyle()}>
<div className="cookieContent">
<div>
<p className="cookieText">This website uses cookies to enhance the user experience.</p>
</div>
<div>
<button onClick={handleClick} className="cookieButton">
I understand
</button>
</div>
</div>
</div>
);
}
浏览器控制台输出:
20:47:48.190 [I][MCA/Main] Environment is 'Development' index.tsx:19
20:47:48.286 [V][MCA/CookieConsent] Consent is accepted: false index.tsx:13
20:47:52.250 [V][MCA/CookieConsent] Accepted cookie consent. Expiration: Sun Jan 30 2022 20:47:52 GMT+0100 (Central European Standard Time) index.tsx:29
答案 10 :(得分:0)
我也遇到了关于扩展console.log()的问题,以便除了将内容记录到控制台之外,应用程序还可以扩展,控制和使用它。然而,丢失行号信息无异于失败。在与问题搏斗之后,我提出了一个冗长的解决方法,但至少它仍然是一个&#34; 1-liner&#34;使用。
首先,定义一个要使用的全局类,或者将一些方法添加到主现有的&#34; app&#34;类:
/**
* Log message to our in-app and possibly on-screen console, return args.
* @param {!string} aMsgLevel - one of "log", "debug", "info", "warn", or "error"
* @param {any} aArgs - the arguments to log (not used directly, just documentation helper)
* @returns args so it can be nested within a console.log.apply(console,app.log()) statement.
*/
MyGlobalClassWithLogMethods.prototype.debugLog = function(aMsgLevel, aArgs) {
var s = '';
var args = [];
for (var i=1; i<arguments.length; i++) {
args.push(arguments[i]);
if (arguments[i])
s += arguments[i].toString()+' ';
}
if (typeof this.mLog === 'undefined')
this.mLog = [];
this.mLog.push({level: aMsgLevel, msg: s});
return args;
};
MyGlobalClassWithLogMethods.prototype.log = function() {
var args = ['log'].concat(Array.prototype.slice.call(arguments));
return this.debugLog.apply(this,args);
};
MyGlobalClassWithLogMethods.prototype.debug = function() {
var args = ['debug'].concat(Array.prototype.slice.call(arguments));
return this.debugLog.apply(this,args);
};
MyGlobalClassWithLogMethods.prototype.info = function() {
var args = ['info'].concat(Array.prototype.slice.call(arguments));
return this.debugLog.apply(this,args);
};
MyGlobalClassWithLogMethods.prototype.warn = function() {
var args = ['warn'].concat(Array.prototype.slice.call(arguments));
return this.debugLog.apply(this,args);
};
MyGlobalClassWithLogMethods.prototype.error = function() {
var args = ['error'].concat(Array.prototype.slice.call(arguments));
return this.debugLog.apply(this,args);
};
//not necessary, but it is used in my example code, so defining it
MyGlobalClassWithLogMethods.prototype.toString = function() {
return "app: " + JSON.stringify(this);
};
接下来,我们将这些方法用于:
//JS line done as early as possible so rest of app can use logging mechanism
window.app = new MyGlobalClassWithLogMethods();
//only way to get "line info" reliably as well as log the msg for actual page display;
// ugly, but works. Any number of params accepted, and any kind of var will get
// converted to str using .toString() method.
console.log.apply(console,app.log('the log msg'));
console.debug.apply(console,app.debug('the log msg','(debug)', app));
console.info.apply(console,app.info('the log msg','(info)'));
console.warn.apply(console,app.warn('the log msg','(warn)'));
console.error.apply(console,app.error('the log msg','(error)'));
现在,控制台会获取包含相应行信息的日志消息,同时我们的应用程序还包含一系列可以使用的日志消息。例如,要使用HTML,JQuery和一些CSS显示您的应用内日志,可以使用以下简单示例。
首先,HTML:
<div id="debug_area">
<h4 class="text-center">Debug Log</h4>
<ul id="log_list">
<!-- console log/debug/info/warn/error ('msg') lines will go here -->
</ul>
</div>
一些CSS:
.log_level_log {
color: black;
background-color: white;
font-size: x-small;
}
.log_level_debug {
color: #060;
background-color: #80FF80;
font-size: x-small;
}
.log_level_info {
color: #00F;
background-color: #BEF;
font-size: x-small;
}
.log_level_warn {
color: #E65C00;
background-color: #FB8;
font-size: x-small;
}
.log_level_error {
color: #F00;
background-color: #FBB;
font-size: x-small;
}
和一些JQuery:
var theLog = app.mLog || [];
if (theLog.length>0) {
var theLogList = $('#log_list');
theLogList.empty();
for (var i=0; i<theLog.length; i++) {
theLogList.prepend($('<li class="log_level_'+theLog[i].level+'"></li>').text(theLog[i].msg));
}
}
这是一个简单的用法,但是一旦你有了这个机制,你就可以做任何你想象的事情,包括将日志行留在代码中,但设置一个阈值,以便只有警告和错误通过。希望这有助于他人的项目。
答案 11 :(得分:0)
尝试setTimeout(console.log.bind(console,'foo'));
答案 12 :(得分:0)
不久前,Chrome推出了一项功能,可以在没有代码破解的情况下解决您的问题。它被称为&#34; blackbox&#34;这基本上允许你标记应该用他们的工具忽略的文件。
https://gist.github.com/paulirish/c307a5a585ddbcc17242
是的,此解决方案是特定于浏览器的,但如果您使用Chrome,则需要此解决方案。
针对每个日志抛出错误的大量破解的解决方案可以显示正确的行,但它不会是控制台中的可点击链接。
基于绑定/别名的解决方案仅允许您修改打印文本。您将无法将参数转发给第三个函数以进行进一步处理。
答案 13 :(得分:0)
今天,您必须将 args
与 rest operator
一起使用,因为正如 Mozilla 文档所说,Function.arguments
已被弃用并且无法在箭头函数中访问。所以简单地你可以像下面这样扩展它:
//#1
const myLog= (...args) =>
console.log.bind(console, ...args);
//myLog("this is my new log")();
//#2
const myNewLog= (...args) =>{
const prefix = "Prefixed: ";
return console.log.bind(console, ...[prefix,...args]);
}
//myNewLog("test")()
你可以像这样制作一个beautifulLog
:
//#3
const colorizedLog = (text, color= "#40a7e3", ...args) =>
console.log.bind(
console,
`%c ${text}`,
`font-weight:bold; color:${color}`,
...args
);
//colorizedLog("Title:", "#40a7e3", "This is a working example")();
答案 14 :(得分:0)
希望这对您的某些情况有所帮助...
new ListView(
scrollDirection: Axis.horizontal,
crossAxisSize: CrossAxisSize.min,
children: <Widget>[
Column(
children:[
new ListItem(),
new ListItem(),
],
)
],
);
作为中间件、文件顶部或函数的第一行运行。