如何调试 Google Apps 脚本(又名 Logger.log 记录到哪里?)

Posted

技术标签:

【中文标题】如何调试 Google Apps 脚本(又名 Logger.log 记录到哪里?)【英文标题】:How to debug Google Apps Script (aka where does Logger.log log to?) 【发布时间】:2012-07-17 08:56:44 【问题描述】:

在 Google 表格中,您可以添加一些脚本功能。我正在为onEdit 事件添加一些内容,但我不知道它是否有效。据我所知,您无法从 Google 表格调试实时事件,因此您必须从调试器中进行,这是没有意义的,因为如果我运行,传递给我的 onEdit() 函数的事件参数将始终未定义它来自Script Editor

所以,每当调用onEdit 函数时,我都尝试使用Logger.log 方法记录一些数据,但这似乎也只有在从Script Editor 运行时才有效。当我从Script Editor 运行它时,我可以通过转到View->Logs... 来查看日志

我希望能够查看事件实际执行时的日志,但我无法弄清楚。

如何调试这些东西?

【问题讨论】:

这里有同样的问题 - 接受的答案没有回答它,但给出了很多明显的错误信息。 他们现在似乎修复了这个问题。只要您从电子表格中打开脚本编辑器,当您在工作表中运行这些内容时,请保持该选项卡处于打开状态。然后返回脚本选项卡,其中将包含日志信息。 tldr;复制、粘贴和运行Logger.log('firstLog');MailApp.sendEmail(to:'yourEmailAddressHere@someone.com',subject: "subject here ^_^",body: Logger.getLog()); 您应该更改已接受的答案或添加注释,说明 Stackdriver Logging 可用。 我在script.google.com/home/executions看到我的日志 【参考方案1】:

更新:

如this 回答中所写,

Stackdriver Logging 是现在首选的日志记录方法。

使用console.log() 登录到 Stackdriver。


Logger.log 将向您发送一封电子邮件(最终),说明您的脚本中发生的错误,或者,如果您正在从 Script Editor 运行东西,您可以通过转到查看上次运行函数的日志View->Logs(仍在脚本编辑器中)。同样,这只会向您显示从您运行的最后一个函数Script Editor内部记录的任何内容。

我尝试使用的脚本与电子表格有关 - 我制作了一个电子表格待办事项清单类型的东西,它按优先级等对项目进行排序。

我为该脚本安装的唯一触发器是 onOpen 和 onEdit 触发器。调试 onEdit 触发器是最难弄清楚的,因为我一直在想,如果我在我的 onEdit 函数中设置一个断点,打开电子表格,编辑一个单元格,我的断点就会被触发。事实并非如此。

为了模拟编辑过一个单元格,我确实最终不得不在实际的电子表格中做一些事情。我所做的只是确保选择了我希望它视为“已编辑”的单元格,然后在Script Editor 中,我将转到Run->onEdit。然后我的断点就会被命中。

但是,我确实不得不停止使用传递给 onEdit 函数的事件参数 - 您无法通过执行 Run->onEdit 来模拟它。我需要从电子表格中获取的任何信息,例如选择了哪个单元格等,我必须手动找出。

无论如何,答案很长,但我最终想通了。


编辑

如果你想看我做的待办事项清单,你可以check it out here

(是的,我知道任何人都可以编辑它——这就是分享它的目的!)

我希望它也能让您看到脚本。既然你在那里看不到它,那就是:

function onOpen() 
  setCheckboxes();
;

function setCheckboxes() 
  var checklist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("checklist");
  var checklist_data_range = checklist.getDataRange();
  var checklist_num_rows = checklist_data_range.getNumRows();
  Logger.log("checklist num rows: " + checklist_num_rows);

  var coredata = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("core_data");
  var coredata_data_range = coredata.getDataRange();

  for(var i = 0 ; i < checklist_num_rows-1; i++) 
    var split = checklist_data_range.getCell(i+2, 3).getValue().split(" || ");
    var item_id = split[split.length - 1];
    if(item_id != "") 
      item_id = parseInt(item_id);
      Logger.log("setting value at ("+(i+2)+",2) to " + coredata_data_range.getCell(item_id+1, 3).getValue());
      checklist_data_range.getCell(i+2,2).setValue(coredata_data_range.getCell(item_id+1, 3).getValue());
    
  


function onEdit() 
  Logger.log("TESTING TESTING ON EDIT");
  var active_sheet = SpreadsheetApp.getActiveSheet();
  if(active_sheet.getName() == "checklist") 
    var active_range = SpreadsheetApp.getActiveSheet().getActiveRange();
    Logger.log("active_range: " + active_range);
    Logger.log("active range col: " + active_range.getColumn() + "active range row: " + active_range.getRow());
    Logger.log("active_range.value: " + active_range.getCell(1, 1).getValue());
    Logger.log("active_range. colidx: " + active_range.getColumnIndex());
    if(active_range.getCell(1,1).getValue() == "?" || active_range.getCell(1,1).getValue() == "?") 
      Logger.log("made it!");
      var next_cell = active_sheet.getRange(active_range.getRow(), active_range.getColumn()+1, 1, 1).getCell(1,1);
      var val = next_cell.getValue();
      Logger.log("val: " + val);
      var splits = val.split(" || ");
      var item_id = splits[splits.length-1];
      Logger.log("item_id: " + item_id);

      var core_data = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("core_data");
      var sheet_data_range = core_data.getDataRange();
      var num_rows = sheet_data_range.getNumRows();
      var sheet_values = sheet_data_range.getValues();
      Logger.log("num_rows: " + num_rows);

      for(var i = 0; i < num_rows; i++) 
        Logger.log("sheet_values[" + (i) + "][" + (8) + "] = " + sheet_values[i][8]);
        if(sheet_values[i][8] == item_id) 
          Logger.log("found it! tyring to set it...");
          sheet_data_range.getCell(i+1, 2+1).setValue(active_range.getCell(1,1).getValue());
        
      

    
  

  setCheckboxes();
;

【讨论】:

噗,日志只会在邮件中可见?这有点让所有这些调试和日志记录功能毫无用处,因为实际上我们无法使用电子表格中的真实数据。 @rainabba 是的,脚本编辑器中提供了日志记录。但是,当脚本依赖于 event 参数并且 event 参数在脚本编辑器中不可用时,这实际上意味着这些类型的脚本的开发人员无法实时访问日志。 我之前无法发表评论,所以我在下面提供了答案,但是:如果您打开了脚本编辑器并在打开的电子表格中触发了一个事件,您可以返回到脚本编辑器浏览器实例并查看日志中的信息。只要您不必在无法打开脚本或无法登录的用户下测试某些内容,它就可以很好地工作。 过时的答案。请包括console.log() 此答案已过时,不应被视为已接受的答案。 Stackdriver Logging 是可用的,并且工作起来就像一个魅力。看看随机部分的答案!【参考方案2】:

据我所知,您无法从 google 文档中调试实时事件,因此您必须从调试器中进行,这是没有意义的,因为传递给我的 onEdit() 函数的事件参数将始终未定义如果我从脚本编辑器运行它。

是的 - 所以自己定义事件参数以进行调试。见How can I test a trigger function in GAS?

每当调用 onEdit 函数时,我都尝试使用 Logger.log 方法记录一些数据,但这似乎也仅在从脚本编辑器运行时才有效。当我从脚本编辑器运行它时,我可以通过转到 View->Logs...

来查看日志

再次正确,但有帮助。 Peter Hermann 的BetterLog library 会将所有日志重定向到电子表格,甚至可以从未附加到编辑器/调试器实例的代码中进行日志记录。

例如,如果您在包含电子表格的脚本中进行编码,则可以仅将这一行添加到脚本文件的顶部,所有日志都将转到电子表格中的“日志”表。无需其他代码,只需像往常一样使用Logger.log()

Logger = BetterLog.useSpreadsheet();

【讨论】:

Outdated.console.log() 现在应该是最好的答案了【参考方案3】:

2017 年更新: Stackdriver Logging 现在可用于 Google Apps 脚本。从脚本编辑器的菜单栏中,转到: View &gt; Stackdriver Logging 查看或流式传输日志。

console.log() 会写DEBUG 级别的消息

示例onEdit() 日志记录:

function onEdit (e) 
  var debug_e = 
    authMode:  e.authMode,  
    range:  e.range.getA1Notation(),    
    source:  e.source.getId(),
    user:  e.user,   
    value:  e.value,
    oldValue: e. oldValue
  

  console.log(message: 'onEdit() Event Object', eventObject: debug_e);

然后检查Stackdriver UI 中标记为onEdit() Event Object 的日志以查看输出

【讨论】:

原始问题专门询问Logger.log。这与您使用的console.log 有何不同?我对这些工具很陌生,所以只是想弄清楚一切是什么。【参考方案4】:

我浏览了这些帖子,不知何故最终找到了一个简单的答案,我将其发布在这里,以供那些想要简短而甜蜜的解决方案的人使用:

    在您的脚本中使用console.log("Hello World")。 转到https://script.google.com/home/my 并选择您的插件。 单击“项目详细信息”上的省略号菜单,选择“执行”。

    点击最新执行的头部,阅读日志。

【讨论】:

这是 2019 年 4 月之后创建的 Google Apps 脚本的基本“Stackdriver 日志记录”(此时访问 Google Cloud 项目以获取 Apps 脚本背后的“自动”项目变得不可能)。如果您更改 Apps 脚本项目的 GCP,则适用常规 Stackdriver 日志记录答案。 我在这里只看到直接执行(即我在脚本编辑器中单击“运行”的那些),但我看不到由工作表中的数据更改引起的执行。我该如何调试这些? 对不起,我没试过。我想如果工作表中的更改触发了一个函数并且该函数有一个日志,那么该日志会一起显示。更改总是由用户引起的,对吧? 是的,我也能想到。不幸的是,这不是发生的事情:-(对数据的更改确实会触发我的功能,但 Stackdriver 日志中未显示 console.log() 消息。我尝试添加一个 on-change 激活器,但这会在没有参数的情况下调用我的功能: -(【参考方案5】:

有点 hacky,但我创建了一个名为“console”的数组,每当我想输出到控制台时,我都会推送到数组。然后,每当我想查看实际输出时,我只返回console,而不是之前返回的任何内容。

    //return 'console' //uncomment to output console
    return "actual output";

【讨论】:

在 js console.log('smth') 中完美运行,但是在 GAS 中呢? console.log 无法正常工作,因为 GAS 不是与您的电子表格在同一网页中运行的脚本,它们是由 google 的应用引擎处理的脚本,因此您必须遵循他们笨拙的 Logger.log 调试器或使用像我这样的黑客 您的阵列“控制台”怎么样? I just returned console的时候怎么输出? 对不起,我解释得不是很好,但基本上电子表格函数会将值返回到单元格,所以如果您返回“控制台”,您将看到您在电子表格单元格中记录的所有内容【参考方案6】:

如果您打开了脚本编辑器,您将在 View->Logs 下看到日志。如果您的脚本具有 onedit 触发器,请更改电子表格,该电子表格应在第二个选项卡中打开脚本编辑器时触发该功能。然后转到脚本编辑器选项卡并打开日志。您将看到您的函数传递给记录器的任何内容。

基本上只要脚本编辑器打开,事件就会写入日志并显示给你。它不会显示其他人是否在其他地方的文件中。

【讨论】:

【参考方案7】:

我遇到了同样的问题,我在网上某处找到了以下内容......

Docs 中的事件处理程序有点棘手。因为文档可以处理多个用户同时进行的多个编辑,所以事件处理程序是在服务器端处理的。这种结构的主要问题是,当事件触发脚本失败时,它会在服务器上失败。如果您想查看调试信息,您需要在触发器菜单下设置一个显式触发器,当事件失败时通过电子邮件向您发送调试信息,否则它将静默失败。

【讨论】:

嗯,是的,我确实遇到了这个问题 - 第二天早上它通过电子邮件向我发送了一大堆来自我的脚本的错误。不过我确实最终弄清楚了(现在发布我自己的答案)【参考方案8】:

它远非优雅,但在调试时,我经常登录到 Logger,然后使用getLog() 获取其内容。然后,我要么:

将结果保存到一个变量中(可以在 Google Scripts 调试器中检查 - 这适用于我无法在某些代码中设置断点但我可以在代码中设置断点的情况稍后执行) 将其写入某个临时 DOM 元素 在alert 中显示它

基本上,它只是变成了javascript output 问题。

它严重缺乏现代 console.log() 实现的功能,但 Logger 仍然有助于调试 Google 脚本。

【讨论】:

【参考方案9】:

就像一个通知。我为我的电子表格制作了一个测试功能。我在 onEdit(e) 函数中使用变量 google throws(我称之为 e)。然后我做了一个这样的测试函数:

function test()
var testRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(GetItemInfoSheetName).getRange(2,7)
var testObject = 
    range:testRange,
    value:"someValue"

onEdit(testObject)
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(GetItemInfoSheetName).getRange(2,6).setValue(Logger.getLog())

调用此测试函数会使所有代码像电子表格中的事件一样运行。我刚刚输入了我编辑的单元格的位置,whitch 给了我一个意想不到的结果,将 value 设置为我放入单元格的值。 OBS!有关谷歌为函数提供的更多变量,请转到此处:https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events

【讨论】:

【参考方案10】:

目前,您仅限于在文档中使用脚本的容器绑定性质。如果您在文档之外创建新脚本,那么您将能够将信息导出到谷歌电子表格并像日志工具一样使用它。

例如在你的第一个代码块中

function setCheckboxes() 

    // Add your spreadsheet data
    var errorSheet = SpreadsheetApp.openById('EnterSpreadSheetIDHere').getSheetByName('EnterSheetNameHere');
    var cell = errorSheet.getRange('A1').offset(errorSheet.getLastRow(),0);

    // existing code
    var checklist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("checklist");
    var checklist_data_range = checklist.getDataRange();
    var checklist_num_rows = checklist_data_range.getNumRows();

    // existing logger
    Logger.log("checklist num rows: " + checklist_num_rows);

   //We can pass the information to the sheet using cell.setValue()
    cell.setValue(new Date() + "Checklist num rows: " + checklist_num_rows);

当我使用 GAS 时,我有两个监视器(您可以使用两个窗口),其中一个包含 GAS 环境,另一个包含 SS,因此我可以写入信息并记录。

【讨论】:

【参考方案11】:

开发控制台将记录应用脚本引发的错误,因此您只需引发错误即可将其记录为普通的 console.log。它将停止执行,但对于逐步调试可能仍然有用。

throw Error('hello world!');

将显示在控制台中,类似于console.log('hello world')

【讨论】:

【参考方案12】:

只需像这样调试您的电子表格代码:

...
throw whatAmI;
...

显示如下:

【讨论】:

我认为您应该提到该图像显示了自定义函数如何显示错误,但 OP 提到他正在使用简单的触发器 (onEdit)

以上是关于如何调试 Google Apps 脚本(又名 Logger.log 记录到哪里?)的主要内容,如果未能解决你的问题,请参考以下文章

google Apps 脚本中的 createFile() 无法正常运行

如何通过 Apps 脚本在 Google 表格中“清除格式”

Google 电子表格脚本:调试图标已禁用

Google Apps 脚本:Google 表格分组

如何使用 Google Apps 脚本执行 API + Java 创建 Google 表单?

如何使用 Google Apps 脚本将公式添加到 Google 表格?