<p>I made a small program that loops and does something, writes the result to a file (overwriting the previous file), and then sleeps 60 seconds and starts over again.</p>
<pre><code>file, err := os.Create(info)
if err != nil {
fmt.Print("Error writing info")
os.Exit(1)
}
defer file.Close()
fmt.Fprintf(file, "%v", output)
</code></pre>
<p>That's the code at the end which outputs the results to the file. The program has been working as intended, however at some point it failed (and printed Error Writing info)</p>
<p>I can't think of a reason why it would fail (and fail randomly a single time). Is there any way to get a detailed reason for that kind of failure?</p>
<hr/>**评论:**<br/><br/>shovelpost: <pre><p>I can recommend you this pattern:</p>
<pre><code>func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v", err) // or log.Fatal
os.Exit(1)
}
}
// like main but returns error
func run() error {
file, err := os.Create(info)
if err != nil {
return fmt.Errorf("creating file for info: %v", err)
}
defer file.Close()
// more work
}
</code></pre>
<p>There are two things to pay attention to on the code above.</p>
<ol>
<li>os.Exit and log.Fatal <a href="https://golang.org/pkg/os/#Exit" rel="nofollow">do not let deferred functions run</a></li>
<li>putting context to your errors with fmt.Errorf (or errors.New)</li>
</ol>
<p>By using this pattern you deal with 1 because your <code>run</code> function will simply return an error (and normally call the deferred functions) ensuring the file will close. This pattern avoids the common pitfall of <code>os.Exit</code> and makes your main code work like the rest of your code (returning errors).</p>
<p>It is also a good practice to <a href="https://blog.golang.org/error-handling-and-go" rel="nofollow">"decorate" the errors you return with extra information</a> so that you know why and where the error happened by reading one line. In the program above, if <code>os.Create</code> fails, the program will exit and will print "Error: creating file for info: <the real reason>". (Notice how when we decorate the error we do not use capital letters or periods.) This is a lightweight approach to error handling compared to other languages (no stack traces). The nice thing about this way is that because <a href="https://blog.golang.org/errors-are-values" rel="nofollow">errors are just values</a>, you can make your error handling <a href="https://commandcenter.blogspot.gr/2017/12/error-handling-in-upspin.html" rel="nofollow">as elaborate as your project requires</a>.</p></pre>Redundancy_: <pre><p>errors.Wrap might be even nicer as things grow (instead of fmt.Errorf) as it will take a stack trace and keep the original error available as a errors.Cause(err)</p></pre>shovelpost: <pre><p><code>errors.Wrap</code> was created for large and complex projects like Juju. Unless your project has similar scale, relying on stacktraces is an excuse for not doing proper error handling. Just my opinion.</p></pre>Redundancy_: <pre><p>Everything in your example would be there with using errors.Wrap, including an extra string for context. I don't think that I'm suggesting not doing anything there that you would consider "proper error handling", and I've found that I occasionally want access to the cause while still supplementing the errors even on much smaller projects. There seems to be very little downside to Wrap/Wrapf compared to Errorf, imo.</p></pre>IAmCodeMachine: <pre><p>Your error handling should be:</p>
<pre><code>if err != nil {
log.Fatal(err)
}
</code></pre>
<p>Which will print out more details of the error for you to debug and also automatically call os.Exit(1) for you.</p></pre>shovelpost: <pre><blockquote>
<p><code>log.Fatal(err)</code></p>
</blockquote>
<p><del>Fatal is not appropriate here because if it's called, the defer statement won't run.</del></p>
<p><code>Fatal</code> is indeed appropriate here but in general be careful with it because it calls <code>os.Exit</code> which terminates the program immediately and deferred functions are not run.</p></pre>davidgsb: <pre><p>Would not that be the point ? There is probably no purpose to running the defer when the resource creating call has failed.</p></pre>shovelpost: <pre><p>Hmm yeah you are right. I guess I am too used to avoid Fatal when there's any kind of defer statement.</p></pre>saturn_vk: <pre><p>There's nothing to defer that that point, so it's valid.</p></pre>SeerUD: <pre><p>Or if you don't want the output to be like a log line, you can still use the <code>fmt</code> package, but use :</p>
<pre><code>if err != nil {
fmt.Printf("Error writing info: %v\n", err)
os.Exit(1)
}
</code></pre></pre>qu33ksilver: <pre><p>You need to print the <code>err</code> variable. That contains the details about the failure.</p></pre>barti59: <pre><p>So I assume if I print err it would have the same effect as log.Fatal(err) minus the program stopping? Because I don't need it to crash if it fails at that point</p></pre>qu33ksilver: <pre><p>right. You can look at the documentation for further info - <a href="https://golang.org/pkg/log/#Fatal" rel="nofollow">https://golang.org/pkg/log/#Fatal</a></p></pre>metamatic: <pre><p>You could also <code>log.Print(err)</code>, or <code>log.Printf("failed to create file: %v", err)</code> to add some info about what you were trying to do when you got the error.</p>
<p>If you were returning the error to the calling code, you'd want to do something like <code>return log.Errorf("MyMethod failed to create file: %v", err)</code> to wrap the error so the caller can print it and get information about where it occured.</p></pre>alecthomas: <pre><p>Is this code inside a function or is it the body of the loop you mention?</p>
<p>If the latter, note that defer will only execute when the enclosing function exits, not at the end of each iteration. This means that Close() will not be called and you are leaking file descriptors, which I suspect is the error you're encountering.</p></pre>barti59: <pre><p>All of it is at the very end of the 'main' loop (minus the sleep statement which is after)</p>
<p>I haven't had any more errors and it's been running all night, so it's only happened once.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
0 回复
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传