<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Horst Gutmann</title>
    <description>The latest articles on DEV Community by Horst Gutmann (@zerok).</description>
    <link>https://dev.to/zerok</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F113466%2F4b88d328-181e-4c69-8c01-b9710dd1b2fa.jpeg</url>
      <title>DEV Community: Horst Gutmann</title>
      <link>https://dev.to/zerok</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zerok"/>
    <language>en</language>
    <item>
      <title>Timezone data in Go</title>
      <dc:creator>Horst Gutmann</dc:creator>
      <pubDate>Thu, 05 Dec 2019 20:01:50 +0000</pubDate>
      <link>https://dev.to/zerok/timezone-data-in-go-2ej3</link>
      <guid>https://dev.to/zerok/timezone-data-in-go-2ej3</guid>
      <description>&lt;p&gt;After reading Jon Skeet's excellent &lt;a href="https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a-silver-bullet/"&gt;blog post about issues with storing datetimes in UTC&lt;/a&gt; inside applications I wondered, how Go was dealing with updates to timezones. In Python this is done using the &lt;a href="https://pypi.org/project/pytz/#history"&gt;pytz package&lt;/a&gt; which is updated frequently. Go goes a slightly different way:&lt;/p&gt;

&lt;p&gt;On Unix systems it tries to load information about the current timezone from one of the following places (&lt;a href="https://github.com/golang/go/blob/50535e6b422ac6b0195f9d3a83607326401cee0b/src/time/zoneinfo_unix.go#L21"&gt;src/time/zoneinfo_unix.go:21&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var zoneSources = []string{
    "/usr/share/zoneinfo/",
    "/usr/share/lib/zoneinfo/",
    "/usr/lib/locale/TZ/",
    runtime.GOROOT() + "/lib/time/zoneinfo.zip",
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/usr/share/zoneinfo&lt;/code&gt; is a folder that is usually provided by the tzdata package of whatever Linux distribution you're using. This in turn is built based on data provided by the tz database project which is maintained by the IANA. You can get the "original" data and project code on &lt;a href="https://www.iana.org/time-zones"&gt;https://www.iana.org/time-zones&lt;/a&gt;. If you want to get notified when something changes there, there is even an &lt;a href="https://mm.icann.org/mailman/listinfo/tz-announce"&gt;annoucement mailing list&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;If you want to stay up-to-date here (and you definitely want!) then it's probably easiest to just stick with the package provided by your operating system. Looking at, for instance, Debian's &lt;a href="https://packages.debian.org/jessie/tzdata"&gt;tzdata package for Jessie&lt;/a&gt;, it still gets updated and is currently at 2019c which is the latest release of the tz database at the time of writing this.&lt;/p&gt;

&lt;p&gt;At the other end of the spectrum you can also force Go to explicitly use a path of your choosing by setting the &lt;a href="https://github.com/golang/go/blob/50535e6b422ac6b0195f9d3a83607326401cee0b/src/time/zoneinfo.go#L294"&gt;&lt;code&gt;ZONEINFO&lt;/code&gt; environment variable&lt;/a&gt;. You could even roll your own &lt;code&gt;zoneinfo.zip&lt;/code&gt; file: The lib/time folder inside Go's source tree contains an &lt;code&gt;update.bash&lt;/code&gt; file which should come in handy there.&lt;/p&gt;

&lt;p&gt;To summarize: If you want to stay up-to-date with your timezone information in Go applications, Go makes that pretty simple by sticking close to what the operating system provides. For edge cases you are able to roll your own version of the tz database, though.&lt;/p&gt;

</description>
      <category>go</category>
      <category>timezones</category>
    </item>
    <item>
      <title>Custom repositories in MacPorts</title>
      <dc:creator>Horst Gutmann</dc:creator>
      <pubDate>Sat, 04 May 2019 08:42:30 +0000</pubDate>
      <link>https://dev.to/zerok/custom-repositories-in-macports-5183</link>
      <guid>https://dev.to/zerok/custom-repositories-in-macports-5183</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://zerokspot.com/weblog/2019/05/04/custom-repositories-in-macports/"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In order to distribute custom tools among friends but also to some degree at work I've created a &lt;a href="https://docs.brew.sh/Taps"&gt;custom tap&lt;/a&gt; in &lt;a href="https://brew.sh"&gt;Homebrew&lt;/a&gt;. This is pretty straight forward as it only requires a Git repository where the audience can access it. Does &lt;a href="https://www.macports.org"&gt;MacPorts&lt;/a&gt; have something similar?&lt;/p&gt;

&lt;p&gt;While MacPorts focuses heavily on the main port tree you can specify multiple sources for that tree. You do that by editing &lt;code&gt;/opt/local/etc/macports/sources.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rsync://rsync.macports.org/macports/release/tarballs/ports.tar [default]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Any custom source should be added before the "default" entry in there as MacPorts will go through each source in the order they are specified here. In order to demonstrate this feature, I've created a custom Portfile for &lt;a href="https://the.exa.website/"&gt;exa&lt;/a&gt; which we are now going to distribute through a custom source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signed archives
&lt;/h2&gt;

&lt;p&gt;While MacPorts doesn't strictly require the ports tree to be distributed as archive, this is the only way where it also enforces the use of signatures. Since that road is only a bit longer, let's take that extra step here.&lt;/p&gt;

&lt;p&gt;In order to sign anything, we first need a key-pair (consisting of a private and public key).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Generate the private key
$ openssl genrsa -des3 -out privkey.pem 2048

# Derive the public key
$ openssl rsa -in privkey.pem -pubout -out pubkey.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I've then placed the public key file (&lt;code&gt;pubkey.pem&lt;/code&gt;) inside &lt;code&gt;/Users/zerok/src/github.com/zerok/macports-ports/&lt;/code&gt;. Next, MacPorts has to know that the public key can be used to verify artefacts. This is done by adding it to the &lt;code&gt;/opt/local/etc/macports/pubkeys.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# MacPorts system-wide public key configuration file.

/opt/local/share/macports/macports-pubkey.pem
/Users/zerok/src/github.com/zerok/macports-ports/pubkey.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  A new source
&lt;/h2&gt;

&lt;p&gt;Now that MacPorts knows how it can verify our new source, we need to create an archive to distribute. For my exa Portfile I've created the following repository: &lt;a href="https://github.com/zerok/macports-ports"&gt;https://github.com/zerok/macports-ports&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This contains the following files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ exa -l --tree
drwxr-xr-x    - zerok  4 May 10:05 .
.rw-r--r--  562 zerok  4 May  9:59 ├── Makefile
.rw-r--r-- 1.8k zerok  4 May  9:32 ├── privkey.pem
.rw-r--r--  451 zerok  4 May  9:32 ├── pubkey.pem
.rw-r--r--  566 zerok  4 May 10:01 ├── pubkey.pem.sig
.rw-r--r--  316 zerok  4 May 10:01 ├── README.md
drwxr-xr-x    - zerok  4 May  9:26 └── sysutils
drwxr-xr-x    - zerok  4 May  9:26    └── exa
.rw-r--r-- 1.2k zerok  4 May  9:26       └── Portfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The Makefile is mostly there to automate the creation of the ports-tree archive and its signing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;all: dist/ports.tar.bz2 dist/ports.tar.bz2.rmd160

dist:
    mkdir -p dist

dist/ports.tar.bz2: dist
    mkdir dist/ports &amp;amp;&amp;amp; \
    cp -R sysutils dist/ports/ &amp;amp;&amp;amp; \
    cd dist &amp;amp;&amp;amp; tar -cjvf ports.tar.bz2 ports &amp;amp;&amp;amp; \
    rm -rf ports

dist/ports.tar.bz2.rmd160:
    openssl dgst -ripemd160 -sign privkey.pem -out dist/ports.tar.bz2.rmd160 dist/ports.tar.bz2.rmd160

clean:
    rm -rf dist

release:
    rsync -avz dist/* zerokspot.com:/srv/www/h10n.me/www/htdocs/macports-ports/ &amp;amp;&amp;amp; \
    rsync -avz pubkey.* zerokspot.com:/srv/www/h10n.me/www/htdocs/macports-ports/

.PHONY: all clean release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;make clean all release&lt;/code&gt; now generates an archive, signs it, and uploads it including the public key (and its signature) to &lt;code&gt;https://h10n.me/macports-ports&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hurray, we now have a ports archive that we can finally use. Finally, update the sources.conf inside &lt;code&gt;/opt/local/etc/macports/&lt;/code&gt; to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://h10n.me/macports-ports/ports.tar.bz2
rsync://rsync.macports.org/macports/release/tarballs/ports.tar [default]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This tells MacPorts that it should look first inside the h10n.me source for a port before falling back to the default one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Download the sources:
$ sudo port sync

# Show the details of the new exa port:
$ port info exa
exa @0.8.0 (sysutils)

Description:          exa is a replacement for ls with additional features like color-coding for file types, Git integration, and extended attributes.
Homepage:             https://github.com/ogham/exa

Extract Dependencies: unzip
Platforms:            darwin
License:              MIT
Maintainers:          Email: zerok@macports.org
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  The good and the not so good
&lt;/h2&gt;

&lt;p&gt;While MacPorts approach for offering multiple sources of packages is a bit more complicated to set up than Homebrew's, I really like that archives &lt;em&gt;have to be signed&lt;/em&gt; (unless you just expose a folder somewhere). Personally, I would have preferred GnuPG signatures over openssl/rmd160 simply because they are a bit more modern &lt;em&gt;and&lt;/em&gt; include more meta-data about the owner of the signature. At least for the distribution of the source's public key I'd still opt to sign that with GnuPG which is why there is also a &lt;code&gt;pubkey.pem.sig&lt;/code&gt; in my example repository.&lt;/p&gt;

&lt;p&gt;I'm relatively new to MacPorts so I don't know if what I've described here even remotely follows best practices. You've been warned 😅&lt;/p&gt;

</description>
      <category>packagemanagers</category>
      <category>macos</category>
    </item>
    <item>
      <title>Getting started with OpenCensus</title>
      <dc:creator>Horst Gutmann</dc:creator>
      <pubDate>Tue, 30 Apr 2019 05:09:56 +0000</pubDate>
      <link>https://dev.to/zerok/getting-started-with-opencensus-3b59</link>
      <guid>https://dev.to/zerok/getting-started-with-opencensus-3b59</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="https://zerokspot.com/weblog/2019/04/27/opencensus/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you want to want to create a service that supports introspection&lt;br&gt;
using traces and also expose metrics you usually have to manually&lt;br&gt;
integrate APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://opentracing.io" rel="noopener noreferrer"&gt;OpenTracing&lt;/a&gt; for Jaeger&lt;/li&gt;
&lt;li&gt;A Prometheus client library to expose metrics to &lt;a href="https://prometheus.io" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are just two examples but as they reflect my current preferred&lt;br&gt;
tooling around traces and metrics I will list them here. If you want&lt;br&gt;
to switch from one vendor to another you usually end up having to&lt;br&gt;
replace big parts of these integrations.&lt;/p&gt;

&lt;p&gt;And this is precisely what makes &lt;a href="https://opencensus.io" rel="noopener noreferrer"&gt;OpenCensus&lt;/a&gt;&lt;br&gt;
by Google so interesting: It acts as an abstraction layer where you&lt;br&gt;
define your metrics and traces and then plug in the actual export&lt;br&gt;
format somewhere else. If you, for instance, then want to switch from&lt;br&gt;
Prometheus + Jaeger to Stackdriver, you only have to replace the&lt;br&gt;
exporter implementation and can leave everything else just as it has&lt;br&gt;
been done before.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tracking metrics
&lt;/h2&gt;

&lt;p&gt;So, how do you integrate metrics and tracing with OpenCensus? Let's&lt;br&gt;
say you want to track the number of failed logins on your page. You&lt;br&gt;
first have to define that metric somewhere (I tend to add them to a&lt;br&gt;
&lt;code&gt;metrics.go&lt;/code&gt; file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"go.opencensus.io/stats"&lt;/span&gt;
    &lt;span class="s"&gt;"go.opencensus.io/stats/view"&lt;/span&gt;
    &lt;span class="s"&gt;"go.opencensus.io/tag"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;loginFailedTotal&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login_failed_total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Total number of failed logins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;loginFailedTotalView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;View&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="s"&gt;"login_failed_total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Measure&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;loginFailedTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Aggregation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;TagKeys&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loginFailedTotalView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a login then fails, you record a new value using the &lt;code&gt;stats.Record&lt;/code&gt;&lt;br&gt;
function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"go.opencensus.io/stats"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;loginHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;

    &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;loginFailedTotal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This all looks quite similar to what you'd have to do in&lt;br&gt;
Prometheus. What's left is the code that exposes the metric to the&lt;br&gt;
outside world.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "contrib.go.opencensus.io/exporter/prometheus"
    "go.opencensus.io/stats/view"
)

func main() {
    // ...

    promex, _ := prometheus.NewExporter(prometheus.Options{Namespace: "myapp"})
    view.RegisterExporter(promex)

    mux.Handle("/metrics", promex)

    // ...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The metrics that are then exposed through &lt;code&gt;/metrics&lt;/code&gt; also contain a&lt;br&gt;
&lt;code&gt;myapp_login_failed_total&lt;/code&gt; entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# HELP myapp_login_failed_total Total number of failed logins
# TYPE myapp_login_failed_total counter
myapp_login_failed_total 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd now have to support another metrics collector, you'd just&lt;br&gt;
have to register another exporter with the &lt;code&gt;view&lt;/code&gt; package and&lt;br&gt;
(depending on the exporter) start it. The rest of the integration (the&lt;br&gt;
metrics, the views, the Record-calls) can stay the same.&lt;/p&gt;
&lt;h2&gt;
  
  
  Traces
&lt;/h2&gt;

&lt;p&gt;For traces, the story looks quite similar. You have a place where you&lt;br&gt;
do the actual tracing and configure an exporter that is responsible&lt;br&gt;
for submitting your traces to whatever backend you want to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;

    &lt;span class="s"&gt;"contrib.go.opencensus.io/exporter/jaeger"&lt;/span&gt;
    &lt;span class="s"&gt;"go.opencensus.io/trace"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;

    &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DefaultSampler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlwaysSample&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;agentEndpointURI&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"localhost:6831"&lt;/span&gt;
    &lt;span class="n"&gt;collectorEndpointURI&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:14268/api/traces"&lt;/span&gt;
    &lt;span class="n"&gt;jex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jaeger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jaeger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AgentEndpoint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;agentEndpointURI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CollectorEndpoint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;collectorEndpointURI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ServiceName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="s"&gt;"myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;loginHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"failed-login"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"some-user"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in the example above, when you start a new "span", you&lt;br&gt;
receive a context back which you can then pass on to "sub-spans". If&lt;br&gt;
we do that with another span for the operation "helper", you get a&lt;br&gt;
rendering like this inside Jaeger:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjqg06q7odymabhkkn3y7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjqg06q7odymabhkkn3y7.png" alt="Nested spans in Jaeger"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In OpenTracing/Jaeger you'd do pretty much the same using the&lt;br&gt;
&lt;code&gt;opentracing.StartSpanFromContext&lt;/code&gt; call, which is far more verbose&lt;br&gt;
than necessary. In general, except for the distinction between metrics&lt;br&gt;
and views, the API feels more concise and more optimized for&lt;br&gt;
best-practices compared to other APIs I've worked within the past.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;The way exporters can be plugged into a project also makes the core of&lt;br&gt;
OpenCensus well-suited for integration into project&lt;br&gt;
templates. Coincidentally, that's pretty much what I'm doing right now&lt;br&gt;
;-) Additionally, there is currently work going on to &lt;a href="https://medium.com/opentracing/a-roadmap-to-convergence-b074e5815289" rel="noopener noreferrer"&gt;merge&lt;br&gt;
OpenCensus and&lt;br&gt;
OpenTracing&lt;/a&gt;&lt;br&gt;
so the whole space will look even more interesting in a couple of&lt;br&gt;
months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete example
&lt;/h2&gt;

&lt;p&gt;You can find a complete example which I've used as basis for this post&lt;br&gt;
on &lt;a href="https://github.com/zerok/opencensus-demo" rel="noopener noreferrer"&gt;https://github.com/zerok/opencensus-demo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opencensus</category>
      <category>operations</category>
      <category>monitoring</category>
      <category>go</category>
    </item>
  </channel>
</rss>
