Is there a good pattern for error handling and logging?

blov · · 562 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Say I have a simple service/store object, User. (Modeled as an interface to allow injecting the db for testing, right?)</p> <p>User shouldn&#39;t handle errors directly, only report them to its consumer, correct? But these errors may be more or less expected, and have different types, which then need to be parsed by the caller, which now needs to import the same packages as User.</p> <p>Specifically, I may not want to log every failed login attempt, but I would want to know if the db&#39;s blown up. That requires parsing for <code>sql.ErrNoRows</code>, which means the consumer must now import <code>database/sql</code> for no other reason than this (when I have otherwise isolated db logic).</p> <p>What do you think? Have you found a good pattern for handling this and similar cases?</p> <p>This is a specific question, but I think there may be a more general pattern worth knowing lurking within.</p> <p>Separately, it also feels strange using a pq driver-specific type (NullTime) as a field on User. Though I guess I could abstract that away, too.</p> <hr/>**评论:**<br/><br/>gentleman_tech: <pre><p>I&#39;m of the opinion (and it&#39;s an opinion) that errors should be handled where they can be dealt with.</p> <p>So if the user fat-fingers the password, nothing in the system can deal with that, so it needs to be passed back to the user to resolve.</p> <p>If the database blows up, the user cannot deal with that. Only the system can determine if there&#39;s a reason it blew up, if there&#39;s anything that can be done about that reason. If it can&#39;t resolve it, then the error needs to be passed to the sysadmin and the user needs to be told the system is broken (different from passing the user the error).</p> <p>In your situation, if the User service handles its database storage then it needs to handle database errors, because handing those errors back to its consumer is pointless: the consumer can&#39;t do anything about them.</p> <p>It&#39;s about separation of responsibilities: the User service is responsible for storing the data. If it can&#39;t do that it needs to say it can&#39;t do it, but only as a last resort. It should deal with the entire process of storing the data, including handling problems.</p></pre>phoger: <pre><p>Thanks. If User lives within the same app/api as its consumer, does that change anything about this approach?</p> <p>I was calling it &#34;service&#34; as distinguished from its store, which is separate as described above to facilitate testing (the DI pattern discussed in this group and elsewhere).</p> <p>In this case there&#39;s another entity &#34;Auth&#34; representing the endpoint, but in most cases the object itself (for example &#34;Project&#34; or &#34;Product&#34;) will have its own endpoints and be more self-contained. (And in fact User itself will, but that&#39;s more for viewing one&#39;s profile, versus the authentication scenario, which has no &#34;Auth&#34; data counterpart.)</p> <p>So in this case user isn&#39;t really a &#34;service&#34; in the generic sense, separately deployed and monitored, but merely represents the api endpoint and any HTTP processing required before handing off to the store responsible for db interaction.</p> <p>Does that make sense? I&#39;m wondering if that changes anything. Probably not a big deal, but it&#39;s always nice to nail down an approach to these things.</p></pre>acln0: <pre><p>Define errors you care about in the scope of your application code. The application should not know anything about <code>sql.ErrNoRows</code>, but it should define <code>ErrNotFound</code> or something, which it does care about. The implementation of <code>User</code> shall translate <code>sql.ErrNoRows</code> to <code>ErrNotFound</code>.</p> <p>The same applies for <code>NullTime</code>. Encode the notion of a time which can be null / nil in your application. Have the database layer translate that to whatever the database needs.</p></pre>gobwas: <pre><p>I think the library that implements a client for some service/database/... should care on errors with that service and translate them into more abstract view for the caller. Also, it is a good practice to provide a way to log detailed errors: for example some Logger interface as a field of library &#39;s client struct.</p></pre>phoger: <pre><p>Thanks! I think you&#39;re basically saying to typify the errors, is that right? So instead of returning errors in their &#34;raw&#34; form, User returns from among a set of known errors within its package, that a consumer could identify by reading through its code/tests/documentation.</p> <p>In this case both the &#34;client&#34; and User are more familiar, since they live within the same app/package (a simple web app or api). So it&#39;s not a separate library per se, but I can understand if the suggestion is to write it that way (while thinking of the additional code I have to write to make that happen ;).</p></pre>gobwas: <pre><p>Yes, same app is not the reason to couple packages when it is not required. You could return some predefined errors, not necessarily different type – like ErrTimeout or ErrBadRequest or something other. Maybe not all of possible errors, but those which caller could react on.</p></pre>ardanstudios: <pre><p>You might find this helpful. </p> <p><a href="https://www.goinggo.net/2017/05/design-philosophy-on-logging.html" rel="nofollow">https://www.goinggo.net/2017/05/design-philosophy-on-logging.html</a></p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

562 次点击  
加入收藏 微博
0 回复
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传