{"id":2709,"date":"2017-01-26T10:13:20","date_gmt":"2017-01-25T23:13:20","guid":{"rendered":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/?p=2709"},"modified":"2019-12-13T17:26:55","modified_gmt":"2019-12-13T06:26:55","slug":"context-is-for-cancelation","status":"publish","type":"post","link":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/2017\/01\/26\/context-is-for-cancelation","title":{"rendered":"Context is for cancelation"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In my <a href=\"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/2017\/01\/23\/the-package-level-logger-anti-pattern\">previous post<\/a> I suggested that the best way to break the compile time coupling between the&nbsp;logger and the loggee was passing in a logger interface when constructing each major type in your program.&nbsp;The suggestion has been floated several times that logging is context specific, so maybe a logger can be passed around via a <code>context.Context<\/code>.&nbsp;I think this suggestion is flawed (as are most uses of <code>context.Value<\/code>, but that&#8217;s another story).&nbsp;This post explains why.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">context.Value() is goroutine thread local storage<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Using <code>context.Context<\/code> to pass a logger into a function is a poor design pattern.&nbsp;In effect <code>context.Context<\/code> is being used as a conduit to arbitrarily extend the API of any method that takes a <code>context.Context<\/code> value.&nbsp;It&#8217;s like Python&#8217;s&nbsp;<code>**kwargs<\/code>, or whatever the name is for that&nbsp;Ruby pattern of always passing a hash.&nbsp;Using <code>context.Context<\/code> in this way avoids an API break by smuggling data in the unstructured bag of values attached to the context. It&#8217;s thread local storage in a cheap suit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s not just that values are boxed into&nbsp;an <code>interface{}<\/code> inside <code>context.WithValue<\/code> that I object to.&nbsp;The far more serious concern is there is no schema to this data, so there is no way for a method that takes a context to ensure that it contains the specific key required to complete the operation.&nbsp;<code>context.Value<\/code> returns <code>nil<\/code> if the key is not found, which means any code doing the na\u00efve<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">log := ctx.Value(\"logger\").(log.Logger)\nlog.Warn(\"something you'll ignore later\")<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">will blow up if the <code>\"logger\"<\/code> key is not present.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sure, you can check that the assertion succeeded, but I feel pretty confident that if this pattern were to become popular then people would eschew the two arg form of type assertion and just expect that the key always returned a valid logger. This would be especially true as logging in error paths is rarely tested, so you&#8217;ll hit this when you need it the most.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In my opinion passing loggers inside <code>context.Context<\/code> would be the worst solution to the problem of decoupling loggers from implementations. We&#8217;d have gone from an explicit compile time dependency to an implicit run time dependency, one that could not&nbsp;be enforced by the compiler.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To quote <a href=\"https:\/\/proxy.goincop1.workers.dev:443\/https\/groups.google.com\/d\/msg\/golang-dev\/F3l9Iz1JX4g\/2TpGYsTkAQAJ\">@freeformz<\/a><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Loggers should be injected into dependencies. Full stop.<\/p><\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s verbose, but it&#8217;s the only way to achieve decoupled design.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my previous post I suggested that the best way to break the compile time coupling between the&nbsp;logger and the loggee was passing in a logger interface when constructing each major type in your program.&nbsp;The suggestion has been floated several times that logging is context specific, so maybe a logger can be passed around via [&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-2709","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\/2709","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=2709"}],"version-history":[{"count":9,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/posts\/2709\/revisions"}],"predecessor-version":[{"id":3924,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/posts\/2709\/revisions\/3924"}],"wp:attachment":[{"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/media?parent=2709"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/categories?post=2709"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/dave.cheney.net\/wp-json\/wp\/v2\/tags?post=2709"}],"curies":[{"name":"wp","href":"https:\/\/proxy.goincop1.workers.dev:443\/https\/api.w.org\/{rel}","templated":true}]}}