<p>Hey ! Let me first say that I've searched for an answer to my question over this topic, but didn'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'm in the process of creating some libraries that will help me code other applications in Go. And while doing that, I'm facing a problem: I'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't want to
impose on the user of the library (let'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've done now is creating a small library (Let'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'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 & 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'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'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 "the wrong choice" for 80%+ of your users.</p>
<p>Part of the problem is that the Go library didn't declare a standard, and a part of the problem is that right now it'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's an increasing trend towards structured logging, as logrus demonstrates, but it's far from a sure thing. And you simply <em>can't</em> square the circle between those two things, because a struct is not a string, so there'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'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've just got nothing good. I mean, I could write things that could deal with it, but they wouldn't be pretty, involving type switches at a minimum and possibly requiring full-on reflection.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传