【问题标题】:Should I be removing console.log from production code?我应该从生产代码中删除 console.log 吗?
【发布时间】:2011-12-21 13:46:39
【问题描述】:

我目前在我的代码中到处都有这个 JS 语句:

window.console && console.log("Foo");

我想知道这是否成本高昂,或者对生产有任何负面影响。

我可以离开客户端登录吗?还是应该离开?

编辑:最后,我想我(以及其他任何人?)可以提出的最佳论点是,通过留下日志消息,在服务器和客户端之间传输的额外数据量可能不可忽略in. 如果要完全优化生产代码,则必须删除日志记录以减少发送到客户端的 javascript 的大小。

【问题讨论】:

标签: javascript logging


【解决方案1】:

您应该不将开发工具添加到生产页面。

回答另一个问题:代码不能产生负面影响:

  • 如果未定义 console,window.console 将评估为 false
  • console.log("Foo") 将在定义时将消息打印到控制台(前提是页面不会被非函数覆盖 console.log)。

【讨论】:

  • 我原则上同意你的看法。但是,我想我们都同意,实现除调试模式外不会触发的日志记录对于高质量的服务器端代码是必要的。没有人会为生产版本检查并删除他们的日志记录——程序会确定需要什么级别的日志记录并做出相应的反应。我希望客户端编程有类似的东西......如果需要的话,即使只是设置一个“isDebug”变量。为什么我要让下一个开发人员在将来必须返回并重新添加有问题的区域的日志记录?
  • 破旧怎么办?我认为说开发人员控制台是生产网站的一部分就像说日志文件是应用程序的一部分。是的,它们都是由代码生成的,但应该有一定程度的理解,即日志必须留在某处。不过,日志中的语句是否对用户友好是另一回事。
  • 如何在最小化之前自动剥离调试代码?客户端日志消息可以采用多种形式。
  • 您可以标记每一段调试代码,例如通过注释为一行调试代码添加前缀/后缀。示例:/*DEBUG:start*/console.log("Foo");/*DEBUG:end*/。然后,使用 RegExp 删除所有出现的/*DENUG-start*/[\S\s]*?/*DEBUG-end*/。剩余的空白字符将被最小化器删除。
  • 除非这个答案说为什么你不应该这样做,否则它真的没用。我们只是应该在没有证据或理由的情况下盲目地接受这是不好的吗?
【解决方案2】:

是的。 console.log 会在不支持它的浏览器中抛出异常(找不到控制台对象)。

【讨论】:

  • 不用他的短路评估,只要定义了窗口
  • 虽然我在最初的回答中错过了它,但也应该检查console.log。
  • 我大多只是做 window.console 来防止 IE 的麻烦。我还没有遇到过日志被无意覆盖的情况,但我相信它可能会发生。
  • @MK_Dev 即,哪些浏览器?
  • 现在是 2019 年。我怀疑有没有不支持控制台的浏览器。
【解决方案3】:

解决此问题的另一种方法是在未定义控制台对象时将其“存根”,这样在没有控制台的上下文中不会引发错误,即

if (!window.console) {
  var noOp = function(){}; // no-op function
  console = {
    log: noOp,
    warn: noOp,
    error: noOp
  }
}

你明白了......在控制台的各种实现中定义了很多函数,所以你可以将它们全部或只是你使用的那些(例如,如果你只使用console.log并且从未使用过console.profile, console.time 等等...)

对我来说,这比在每次调用前添加条件或不使用条件更好。

另请参阅:Is it a bad idea to leave "console.log()" calls in your producton JavaScript code?

【讨论】:

  • 好主意。只是需要一些修复。 Visual Studio JS 调试器在第一个 console.log = noOp() 处抛出,因为控制台对象本身没有定义。我是这样做的:控制台 = { 日志:noOp,警告:noOp,错误:noOp }。另请注意,您不想将 () 放在 noOp 之后 - 您想分配函数本身而不是其返回值。在 Visual Studio JS 调试器和 IE9 上测试 - 现在可以正常工作了。
  • 仅供参考,如果您已经在使用 jQuery,它们提供了一个 noop 函数:$.noop。
【解决方案4】:

如果缩小是您的构建过程的一部分,您可以使用它来去除调试代码,如 Google 闭包编译器所述:Exclude debug JavaScript code during minification

if (DEBUG) {
  console.log("Won't be logged if compiled with --define='DEBUG=false'")
}

如果你用高级优化编译,这段代码甚至会被识别为死代码并被完全删除

【讨论】:

  • 谢谢!我在找这个。 :)
【解决方案5】:

是的,将console.log 用于 javascript 调试目的是一种很好的做法,但它需要从生产服务器中删除,或者如果需要可以添加到生产服务器上,需要考虑一些关键点:

**var isDebugEnabled="Get boolean value from Configuration file to check whether debug is enabled or not".**
if (window.console && isDebugEnabled) {
    console.log("Debug Message");
}

为了首先验证当前浏览器是否支持控制台以及是否启用调试,必须在任何地方使用以上代码块进行日志记录。

isDebugEnabled 必须根据我们的设置为真或假 环境。

【讨论】:

    【解决方案6】:

    通常是的,在生产代码中公开日志消息不是一个好主意。

    理想情况下,您应该在部署之前使用构建脚本删除此类日志消息;但是很多(大多数)人不使用构建过程(包括我)。

    这是我最近用来解决这个难题的一些代码的简短 sn-p。它修复了旧 IE 中未定义的console 导致的错误,以及在“development_mode”中禁用日志记录。

    // fn to add blank (noOp) function for all console methods
    var addConsoleNoOp =  function (window) {
        var names = ["log", "debug", "info", "warn", "error",
            "assert", "dir", "dirxml", "group", "groupEnd", "time",
            "timeEnd", "count", "trace", "profile", "profileEnd"],
            i, l = names.length,
            noOp = function () {};
        window.console = {};
        for (i = 0; i < l; i = i + 1) {
            window.console[names[i]] = noOp;
        }
    };
    
    // call addConsoleNoOp() if console is undefined or if in production
    if (!window.console || !window.development_mode) {
        this.addConsoleNoOp(window);
    }
    

    我很确定我从关于 SO 的另一个答案中获取了上述大部分addConsoleNoOp f'n,但现在找不到。如果我找到它,我会在稍后添加参考。

    编辑:不是我想的帖子,但这里有一个类似的方法:https://github.com/paulmillr/console-polyfill/blob/master/index.js

    【讨论】:

      【解决方案7】:

      我基本上用知道代码运行位置的函数覆盖了 console.log 函数。因此,我可以像往常一样继续使用 console.log。它会自动知道我处于 dev/qa 模式或生产中。还有一种方法可以强制它。 这是一个工作小提琴。 http://jsfiddle.net/bsurela/Zneek/

      这里是 sn-p,因为发布 jsfiddle 的人暗示堆栈溢出

        log:function(obj)
      {
          if(window.location.hostname === domainName)
          {
              if(window.myLogger.force === true)
              {
                  window.myLogger.original.apply(this,arguments);
              }
          }else {
              window.myLogger.original.apply(this,arguments);
          }
      },
      

      【讨论】:

        【解决方案8】:

        UglifyJS2

        如果你使用这个缩小器,你可以设置drop_console option:

        传递 true 以放弃对 console.* 函数的调用

        所以我建议保留console.log 调用,因为它们是代码库中最棘手的部分。

        【讨论】:

        • 如果您使用 grunt 和 uglify,同样的选项也可用(似乎 uglify 是基于 UglifyJS2):github.com/gruntjs/…
        • 问题是“我应该”,而不是“我该怎么做”。
        • 您应该将 console.errors 留在 .如果生产中的某个人有问题,那么您可以轻松诊断它。不要剥离 console.errors!
        • 如果需要它们,您可以将drop_console 设置为false,观察它们并再次隐藏它们。
        【解决方案9】:
        var AppLogger = (function () {
          var debug = false;
          var AppLogger = function (isDebug) {
            debug = isDebug;
          }
          AppLogger.conlog = function (data) {
            if (window.console && debug) {
                console.log(data);
            }
          }
          AppLogger.prototype = {
            conlog: function (data) {
                if (window.console && debug) {
                    console.log(data);
                }
            }
          };
        return AppLogger;
        })();
        

        用法:

        var debugMode=true;
        var appLogger = new AppLogger(debugMode);
        appLogger.conlog('test');
        

        【讨论】:

          【解决方案10】:

          TL;DR

          想法:记录对象可以防止它们被垃圾收集。

          详情

          1. 如果您将对象传递给console.log,则可以通过 DevTools 控制台的引用访问这些对象。您可以通过记录对象、对其进行变异并发现旧消息反映对象后来的更改来检查它。
          2. 如果日志太长,旧消息会在 Chrome 中删除。
          3. 如果日志很短,则不会删除旧消息,如果这些消息引用对象,则不会对这些对象进行垃圾收集。

          这只是一个想法:我检查了第 1 点和第 2 点,但没有检查第 3 点。

          解决方案

          如果您想保留日志以用于客户端故障排除或其他需求,那么:

          ['log', 'warn', 'error'].forEach( (meth) => {
            const _meth = window.console[meth].bind(console);
            window.console[meth] = function(...args) { _meth(...args.map((arg) => '' + arg)) }
          });
          

          【讨论】:

            【解决方案11】:

            我知道这是一个相当古老的问题,并且有一段时间没有太多活动了。我只是想添加我想出的解决方案,它似乎对我很有效。

                /**
                 * Logger For Console Logging 
                 */
                Global.loggingEnabled = true;
                Global.logMode = 'all';
                Global.log = (mode, string) => {    
                    if(Global.loggingEnabled){
                        switch(mode){
                          case 'debug':
                              if(Global.logMode == 'debug' || Global.logMode == 'all'){
                                console.log('Debug: '+JSON.stringify(string));
                              }
                              break;
                          case 'error':
                              if(Global.logMode == 'error' || Global.logMode == 'all'){
                                console.log('Error: '+JSON.stringify(string));
                              }       
                              break;
                          case 'info':
                              if(Global.logMode == 'info' || Global.logMode == 'all'){
                                console.log('Info: '+JSON.stringify(string));
                              }
                              break;
                        }
                    }
                }
            

            然后我通常会像这样在我的脚本中创建一个函数,或者您可以在全局脚本中使用它:

            Something.fail = (message_string, data, error_type, function_name, line_number) => {
                try{
            
                    if(error_type == undefined){
                        error_type = 'error';
                    }
            
                    Global.showErrorMessage(message_string, true);
                    Global.spinner(100, false);
            
                    Global.log(error_type, function_name);
                    Global.log(error_type, 'Line: '+line_number);
                    Global.log(error_type, 'Error: '+data);
            
                }catch(error){
                    if(is_global){
                        Global.spinner(100, false);
                        Global.log('error', 'Error: '+error);
                        Global.log('error', 'Undefined Error...');
                    }else{
                        console.log('Error:'+error);
                        console.log('Global Not Loaded!');
                    }           
                }   
            }
            

            然后我只是使用它而不是这样的 console.log:

            try{
             // To Do Somehting
             Something.fail('Debug Something', data, 'debug', 'myFunc()', new Error().lineNumber);
            }catch(error){
             Something.fail('Something Failed', error, 'error', 'myFunc()', new Error().lineNumber);
            }
            

            【讨论】:

              【解决方案12】:

              如果使用正确的工具(例如 parcel/webpack)完成工作流,那么它就不再令人头疼了,因为使用 production 构建 console.log 正在被删除。即使在几年前使用Gulp/Grunt,它也可以实现自动化。

              Angular、React、Svelte、Vue.js 等许多现代框架都带有开箱即用的设置。基本上,你不需要做任何事情,只要你部署了正确的构建,即production,而不是development,它仍然有console.log。

              【讨论】:

                【解决方案13】:

                不要把事情复杂化!我个人在开发过程中一直使用console.log,这样可以节省很多时间。对于生产,我只需添加一行代码(在我的情况下,在“生产配置文件”中)禁用所有日志:

                window.console.log = () => {};
                

                完成 ;) 此猴子修补 window.console 并将 log 函数替换为空函数,从而禁用输出。

                在大多数情况下,这对我来说已经足够了。如果您想“一路”并从代码中删除 console.logs 以减小包大小,则必须更改 js 的捆绑方式(例如,使用 minifier 或其他方式删除 console.logs)

                此外,我认为您实际上可以提出将它们留在其中的强项——即使在生产中也是如此。对于普通用户来说,它不会改变任何东西,但可以真正加快理解奇怪的“异国浏览器”问题。它不像后端日志可能包含关键信息。无论如何,这一切都在前端,不显示日志消息是因为您害怕透露用户不应该知道的东西,这实际上只是“默默无闻的安全性”,并且应该让您思考为什么这些信息甚至在一开始就可以在前端获得地方。只是我的看法。

                【讨论】:

                  猜你喜欢
                  • 2019-06-12
                  • 2020-10-14
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-09-13
                  • 1970-01-01
                  • 2023-03-27
                  • 1970-01-01
                  相关资源
                  最近更新 更多