Question: abstracting loggers & other frameworks using interfaces

agolangf · · 457 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hey ! Let me first say that I&#39;ve searched for an answer to my question over this topic, but didn&#39;t find anything that answer my questions (If I missed an article or something else that relates to this topic, please send it to me ! :D).</p> <p>First, a bit of context. I&#39;m in the process of creating some libraries that will help me code other applications in Go. And while doing that, I&#39;m facing a problem: I&#39;d like to use loggers in these libraries, but it is optional. If a user does want log messages, it only has to call a <em>obj.SetLogger(logger pkg.StructuredLoggerInterface)</em>. The problem is in the StructuredLoggerInterface interface. I want it to be an interface for Logrus, apex/log and other structured loggers, as I don&#39;t want to impose on the user of the library (let&#39;s name it <strong>A</strong>) which logger he should use, as long as it follow a basic contract for a structured logger. The interface would somewhat look like this:</p> <pre><code>type StructuredLoggerInterface interface{ Print(...interface{}) WithField(key string, obj interface{}) StructuredLoggerInterface } </code></pre> <p>Problem: It requires a custom implementation for every logger that needs to be used as loggers, as Logrus (or others) actually return internal custom structures (*<em>logrus.Entry</em> for example). I need an custom adapter for every logging library. </p> <p>What I&#39;ve done now is creating a small library (Let&#39;s name it <strong>logpkg</strong>), containing the StructuredLoggerInterface and some implementation for different type of Loggers. This library <strong>logpkg</strong> is then used as dependency by my other projects needing loggers.</p> <p>Meaning that an external user would need to wrap his logger in one of my custom structures before passing it to my library A, something like:</p> <pre><code>obj.SetLogger(logpkg.NewLogrusWrapper(myLogrusWrapper)) </code></pre> <p>So, Here is my questions:</p> <ol> <li><p>As a user of library <strong>A</strong>, what would be your thoughts on that type of structure ? Would it bother you ? Can I improve this system to make it easier to use ?</p></li> <li><p>Am I going too far ? Is this kind of absraction not needed ? Should I just require Logrus directly ?</p></li> <li><p>In the same situation, what would you do ? </p></li> </ol> <p>PS: I know that I could just require a simple Logger with </p> <pre><code>type SimpleLogger interface { Print(...interface{}) </code></pre> <p>}</p> <p>But that doesn&#39;t fix the overall issue. There was a point where I asbtracted mgo to be able to provide a custom test implementation, and I still had to provide an adapter for the real mgo. Every time I use mgo, I will have to recreate it, or extract the mgo abstraction &amp; adapter in an external library. Even if I simplify the Logger interfaces, I will have the problem with other packages.</p> <hr/>**评论:**<br/><br/>tylermumford: <pre><p>I was going to suggest that you simply make a new interface and then add a couple methods to common loggers so they could implement it. I thought method definitions worked the same way as extensions in Swift, allowing you to add methods to types you didn&#39;t define.</p> <p>But alas, I read the Go spec and that is not permitted; methods have to be defined in the same package as their receiver types. Good luck finding another solution. :)</p></pre>phuber: <pre><p>As long as you provide an interface that others can implement, provide a default implementation and allow the user to specify their own implementation via dependency injection, I see no issues with your approach.</p> <p>I believe this is the <a href="https://martinfowler.com/eaaCatalog/layerSupertype.html" rel="nofollow">layer supertype</a> pattern.</p> <p>Not sure if you can get past the adapter problem, it&#39;s required to implement this pattern.</p></pre>jerf: <pre><p>I definitely recommend a logging abstraction and not hard-wiring one, because you really limit your appeal if you hardcode what will be &#34;the wrong choice&#34; for 80%+ of your users.</p> <p>Part of the problem is that the Go library didn&#39;t declare a standard, and a part of the problem is that right now it&#39;s not even clear what that standard would or should be. <code>(string, ...interface{})</code> is popular, but it hard codes some expensive operations in to the interface (somehow converting interface{} into strings). And there&#39;s an increasing trend towards structured logging, as logrus demonstrates, but it&#39;s far from a sure thing. And you simply <em>can&#39;t</em> square the circle between those two things, because a struct is not a string, so there&#39;s absolutely no way to match it.</p> <p>One of my libraries provides the ability to pass in a logging function for each of the limited sets of things that the library can log, each of which are passed all the information about the logged incident as a function. A default implementation which uses the built-in logger is provided. This is maximally flexible, but only works because I have two core things that can be logged; obviously this fails for a package with dozens of possible messages. So beyond that, the only thing I&#39;ve got is, provide yourself an interface that you like and tell people they have to wrap.</p> <p>And that still leaves you with the decision as to whether you treat log events as objects or strings, for which I&#39;ve just got nothing good. I mean, I could write things that could deal with it, but they wouldn&#39;t be pretty, involving type switches at a minimum and possibly requiring full-on reflection.</p></pre>

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

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