Skip to content

Latest commit

 

History

History
149 lines (108 loc) · 8.7 KB

12.md

File metadata and controls

149 lines (108 loc) · 8.7 KB

十二、同构 Go 的调试

调试同构 Go web 应用包括以下项目:

  • 识别编译器/传输程序错误
  • 检查紧急堆栈跟踪
  • 跟踪代码以查明问题的根源

识别编译器/传输程序错误

我们可以把编程看作是你(程序员)和机器(编译器/transpiler)之间的对话。因为 Go 是一种类型化语言,我们可以在编译/传输时发现许多错误。这是编写普通 JavaScript 的一个明显优势,在这种情况下,问题(由于缺乏类型检查而导致)可能会隐藏起来,并在最不合适的时候暴露出来。编译器错误是机器向我们传达程序存在根本性错误的手段,无论是语法问题还是类型使用不当。

kick对于显示编译器错误非常方便,因为它将显示来自 Go 编译器和 GopherJS transpiler 的错误。当您引入一个错误(编译器/transpiler 可以识别)并保存源文件时,您将在运行kick的终端窗口中看到该错误。

例如,让我们打开client/client.go源文件。在run函数中,让我们注释掉将ts变量设置为TemplateSet对象的行,我们通过templateSetChannel接收该对象:

//ts := <-templateSetChannel

我们知道ts变量稍后将用于填充env对象的TemplateSet字段。让我们通过引入以下代码将ts变量设置为false的布尔值:

ts := false

当我们保存client.go源文件时,kick会给我们一脚(有意双关语),关于我们刚才引入的错误,如图 A1所示:

图 A1:kick 命令在保存 Go 源文件时立即向我们显示 transpiler 错误

收到的编译器错误向我们显示了问题发生的确切位置,我们可以从中诊断和纠正问题。从这个例子中可以学到的教训是,在开发同构的 Go web 应用时,在后台运行kick终端窗口非常方便。通过这样做,您将能够在出现编译器/transpiler 错误时立即看到它们。

检查紧急堆栈跟踪

对于传输程序在传输时间无法发现的运行时错误,通常会有一个有用的紧急堆栈跟踪,显示在 web 浏览器控制台中,为我们诊断问题提供有价值的信息。GopherJS 生成的 JavaScript 源映射文件有助于 web 浏览器将 JavaScript 指令映射到 Go 源文件中各自的行。

让我们引入一个运行时错误,根据该错误,客户端程序在语法上是正确的(它将通过 transpiler 检查);但是,代码在运行时会出现问题。

回到client/client.go源文件中的run函数,注意我们对ts变量所做的以下代码更改:

func run() {
  println("IGWEB Client Application")

  // Fetch the template set
  templateSetChannel := make(chan *isokit.TemplateSet)
  funcMap := template.FuncMap{"rubyformat": templatefuncs.RubyDate, "unixformat": templatefuncs.UnixTime, "productionmode": templatefuncs.IsProduction}
  go isokit.FetchTemplateBundleWithSuppliedFunctionMap(templateSetChannel, funcMap)
  // ts := <-templateSetChannel

  env := common.Env{}
 // env.TemplateSet = ts
 env.TemplateSet = nil
  env.Window = dom.GetWindow()
  env.Document = dom.GetWindow().Document()
  env.PrimaryContent = env.Document.GetElementByID("primaryContent")
  env.Location = env.Window.Location()

  registerRoutes(&env)
  initializePage(&env)
}

我们已经注释掉了ts变量的声明和初始化,也注释掉了ts变量对env对象的TemplateSet字段的赋值。我们引入了一行代码,将nil值分配给env对象的TemplateSet字段。通过执行此操作,我们基本上禁用了客户端模板集,这将阻止我们在客户端渲染任何模板。这还可以防止渲染任何 cog,因为 cog 依赖于模板集以正常工作。

加载 IGWEB 主页后,将生成一个紧急堆栈跟踪,并在 web 浏览器控制台中可见,如图 A2 所示:

图 A2:web 浏览器控制台中显示的紧急堆栈跟踪

在前端调试过程中,您经常会遇到以下错误消息:

client.js:1412 Uncaught Error: runtime error: invalid memory address or nil pointer dereference

runtime error: invalid memory address or nil pointer dereference通常意味着我们试图对一个等于 JavaScriptnull值的值执行操作(例如访问或修改属性)

检查生成的紧急堆栈跟踪有助于我们关注该问题:

Uncaught Error: runtime error: invalid memory address or nil pointer dereference
 at $callDeferred (client.js:1412)
 at $panic (client.js:1451)
 at throw$1 (runtime.go:219)
 at Object.$throwNilPointerError (client.js:29)
 at Object.$packages.github.com/isomorphicgo/isokit.TemplateSet.ptr.Members (templateset.go:37)
 at Object.$packages.github.com/isomorphicgo/isokit.TemplateSet.ptr.Render (templateset.go:115)
 at Object.$packages.github.com/uxtoolkit/cog.UXCog.ptr.RenderCogTemplate (uxcog.go:143)
 at Object.$packages.github.com/uxtoolkit/cog.UXCog.ptr.Render (uxcog.go:179)
 at Object.$packages.github.com/EngineerKamesh/igb/igweb/shared/cogs/carousel.Carousel.ptr.Start (carousel.go:47)
 at Object.InitializeIndexPage (index.go:31)
 at initializePage (client.go:45)
 at run (client.go:100)
 at main (client.go:112)
 at $init (client.js:127543)
 at $goroutine (client.js:1471)
 at $runScheduled (client.js:1511)
 at $schedule (client.js:1527)
 at $go (client.js:1503)
 at client.js:127554
 at client.js:127557

紧急堆栈跟踪中的相关区域以粗体显示。从紧急堆栈跟踪中,我们可以确定旋转木马 cog 未能渲染,因为似乎TemplateSet有问题。通过进一步检查紧急堆栈跟踪,我们可以确定调用是在client.go源文件的第 112 行对run函数进行的。run函数是我们通过将env对象的TemplateSet字段设置为nil引入错误的地方。从这个调试练习中,我们可以看到,在这种情况下,紧急堆栈跟踪没有揭示问题的确切路线,但它为我们提供了足够的线索来纠正问题。

在客户端开发时要遵循的一个良好实践是始终打开 web 浏览器的控制台,以便在出现问题时能够看到问题。

跟踪代码以查明问题的根源

另一个很好的客户端调试实践是跟踪,即打印出程序流程中的关键步骤的实践。在调试场景中,这将包括围绕可疑的问题代码区域战略性地调用println(或fmt.Println函数。您可以使用 web 浏览器的控制台验证是否达到这些打印语句,这将使您更好地了解客户端程序在运行时的工作方式。

例如,在调试上一节介绍的问题时,我们可以在 run 函数中放置以下println调用:

func run() {
  //println("IGWEB Client Application")
  println("Reached the run function")
  // Fetch the template set
  templateSetChannel := make(chan *isokit.TemplateSet)
  funcMap := template.FuncMap{"rubyformat": templatefuncs.RubyDate, "unixformat": templatefuncs.UnixTime, "productionmode": templatefuncs.IsProduction}
  go isokit.FetchTemplateBundleWithSuppliedFunctionMap(templateSetChannel, funcMap)
  // ts := <-templateSetChannel
  println("Value of template set received over templateSetChannel: ", <-templateSetChannel)
  env := common.Env{}
  // env.TemplateSet = ts
  env.TemplateSet = nil
  env.Window = dom.GetWindow()
  env.Document = dom.GetWindow().Document()
  env.PrimaryContent = env.Document.GetElementByID("primaryContent")
  env.Location = env.Window.Location()
  println("Value of template set: ", env.TemplateSet)
  registerRoutes(&env)
  initializePage(&env)
}

我们通过打印程序流程中的关键步骤,通过进行策略性的println函数调用来执行跟踪。第一个println调用用于验证我们是否达到run函数。第二个println调用用于检查从模板集通道返回给我们的模板集的运行状况。第三个,也是最后一个println调用,用于在我们通过填充env对象的字段完成对其的预处理后,检查模板集的运行状况。

图 A3显示了 web 控制台,其中显示了打印语句,以及client.go源文件中进行println调用的相应行号:

图 A3:web 控制台中显示的打印语句

通过跟踪练习,我们可以首先验证我们已经成功地达到了run功能。其次,我们可以通过注意对象的属性出现(例如membersFuncsbundle来验证通过templateSetChannel接收到的TemplateSet对象的健康状况。第三条,也是最后一条 print 语句,在env对象已准备好之后,还将验证TemplateSet对象的运行状况。这个 print 语句通过向我们显示[T8]对象尚未初始化来揭示问题的根源,因为我们在 print 语句中没有看到对象的任何属性。