{"id":2690,"date":"2017-01-23T11:06:34","date_gmt":"2017-01-23T00:06:34","guid":{"rendered":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/?p=2690"},"modified":"2019-12-13T17:27:45","modified_gmt":"2019-12-13T06:27:45","slug":"the-package-level-logger-anti-pattern","status":"publish","type":"post","link":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/2017\/01\/23\/the-package-level-logger-anti-pattern","title":{"rendered":"The package level logger anti pattern"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This post is a spin-off from&nbsp;<a href=\"https:\/\/proxy.goincop1.workers.dev:443\/https\/twitter.com\/bketelsen\/status\/820768241849077760\">various<\/a> <a href=\"https:\/\/proxy.goincop1.workers.dev:443\/https\/groups.google.com\/forum\/#!topic\/golang-dev\/F3l9Iz1JX4g\">conversations<\/a> around improving (I&#8217;m trying not to say standardising, otherwise I&#8217;ll have to link to XKCD) the way logging is performed in Go projects.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider this familiar pattern for establishing a package level&nbsp;<code>log<\/code>&nbsp;variable.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">package foo\n\nimport \u201cmylogger\u201d\n\nvar log = mylogger.GetLogger(\u201cgithub.com\/project\/foo\u201d)<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">What&#8217;s wrong with this pattern?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The first problem with declaring a package level <code>log<\/code> variable is the tight&nbsp;coupling between package <code>foo<\/code> and package <code>mylogger<\/code>. Package <code>foo<\/code> now depends directly on package <code>mylogger<\/code> at&nbsp;<em>compile time.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The second problem is the\u00a0tight coupling between package <code>foo<\/code> and package <code>mylogger<\/code> is transitive. Any package that consumes package <code>foo<\/code> is itself dependant on <code>mylogger<\/code> at compile time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This leads to a third problem, Go projects composed of packages using multiple logging libraries, <em>or<\/em> fiefdoms of projects who can only consume packages that use their particular logging library.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Avoid source level coupling<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The solution to this anti pattern is to delay the binding between the type that does the logging, and the type that needs to log, until it is needed. That is, until the variable is declared.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">package foo\n\nimport \"github.com\/pkg\/log\"\n\ntype T struct {\n        logger log.Logger\n        \/\/ other fields\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, the consumer of &nbsp;type <code>T<\/code>&nbsp;supplies a value of type <code>log.Logger<\/code>&nbsp;when constructing new <code>T<\/code>&#8216;s, and the methods on <code>T<\/code>&nbsp;use the <code>logger<\/code>&nbsp;they were provided when they want to log.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Interfaces to the rescue<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The eagle eyed reader will note that the previous selection removed the package level <code>log<\/code>&nbsp;variable, but the coupling between package <code>foo<\/code>&nbsp;and package <code>log<\/code>&nbsp;remains.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, this can be remedied by the&nbsp;<em>consumer<\/em> of the <code>logger<\/code>&nbsp;type declaring its own interface for the behaviour it expects.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">package foo\n\ntype logger interface {\n        Printf(string, ...interface{})\n}\n\ntype T struct {\n        logger\n        \/\/ other fields\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As long as the type assigned to <code>foo.T.logger<\/code>&nbsp;implements <code>foo.logger&nbsp;<\/code>the decision for which specific type to use can be deferred until run time in the same way that&nbsp;<code>io.Copy<\/code>&nbsp;escapes any knowledge of the <code>io.Reader<\/code> and <code>io.Writer<\/code> implementations in use until it is invoked.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">It&#8217;s not just logging<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Logging is a cross cutting concern, but the anti patterns associated with it also apply to other common areas like metrics, telemetry, and auditing.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Get involved<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The Go 1.9 development window is opening next month. If this topic is important to you, <a href=\"https:\/\/proxy.goincop1.workers.dev:443\/https\/groups.google.com\/forum\/#!topic\/golang-dev\/F3l9Iz1JX4g\">get involved<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is a spin-off from&nbsp;various conversations around improving (I&#8217;m trying not to say standardising, otherwise I&#8217;ll have to link to XKCD) the way logging is performed in Go projects. Consider this familiar pattern for establishing a package level&nbsp;log&nbsp;variable. package foo import \u201cmylogger\u201d var log = mylogger.GetLogger(\u201cgithub.com\/project\/foo\u201d) What&#8217;s wrong with this pattern? The first problem [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":""},"categories":[29,36],"tags":[202,192],"class_list":["post-2690","post","type-post","status-publish","format-standard","hentry","category-golang","category-programming-2","tag-design","tag-logging"],"_links":{"self":[{"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/posts\/2690","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/comments?post=2690"}],"version-history":[{"count":11,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/posts\/2690\/revisions"}],"predecessor-version":[{"id":3925,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/posts\/2690\/revisions\/3925"}],"wp:attachment":[{"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/media?parent=2690"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/categories?post=2690"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/tags?post=2690"}],"curies":[{"name":"wp","href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/api.w.org\/{rel}","templated":true}]}}