<?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>Forem: Aditi Ahuja</title>
    <description>The latest articles on Forem by Aditi Ahuja (@metonymicsmokey).</description>
    <link>https://forem.com/metonymicsmokey</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%2F710930%2F7f54aaf5-99e2-4c65-94f5-bda609f95fd7.jpeg</url>
      <title>Forem: Aditi Ahuja</title>
      <link>https://forem.com/metonymicsmokey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/metonymicsmokey"/>
    <language>en</language>
    <item>
      <title>Thanos: Musings of a Mentee</title>
      <dc:creator>Aditi Ahuja</dc:creator>
      <pubDate>Sun, 19 Dec 2021 06:01:10 +0000</pubDate>
      <link>https://forem.com/metonymicsmokey/thanos-musings-of-a-mentee-350m</link>
      <guid>https://forem.com/metonymicsmokey/thanos-musings-of-a-mentee-350m</guid>
      <description>&lt;p&gt;This is my account of my experience as a Fall ‘21 LFX mentee working with the Thanos project. The mentorship, as an experience, deserved an exhaustive post in its own right and this is my attempt at penning down the events this fall, in as much detail as I could. &lt;/p&gt;

&lt;p&gt;This post is divided into 4 sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Project&lt;/li&gt;
&lt;li&gt;The Thanos Process&lt;/li&gt;
&lt;li&gt;Getting started as a contributor&lt;/li&gt;
&lt;li&gt;Learnings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Project&lt;/strong&gt;&lt;br&gt;
My project involved adding a feature flag to enable Prometheus metrics which tracked the progress of compaction, downsampling and retention processes. These metrics are useful when performing a time-consuming compaction on large blocks of TSDB data. Prior to the project, compaction progress would have to be manually monitored using the bucket UI.The added metrics export data that can be visualised graphically and would give an estimate about some progress parameters related to these processes.I also added unit tests and the required documentation for the functions added as part of the project. &lt;/p&gt;

&lt;p&gt;Some other additions I worked on include adding tracing support for compaction and adding the enable-feature flag for negative offsets and @ modifiers in Thanos compact.&lt;/p&gt;

&lt;p&gt;For understanding my work better, these are some links:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://github.com/thanos-io/thanos/pull/4801"&gt;PR&lt;/a&gt; for compaction and downsampling progress metrics&lt;br&gt;
a. &lt;a href="https://drive.google.com/file/d/1t6JQkNdzF-DiSUV1fLfrGAtlS7gK4j4t/view?usp=sharing"&gt;Diagram&lt;/a&gt; to understand compaction metrics.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://drive.google.com/file/d/1t6JQkNdzF-DiSUV1fLfrGAtlS7gK4j4t/view?usp=sharing"&gt;PR&lt;/a&gt; for retention metrics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://github.com/thanos-io/thanos/pull/4903"&gt;PR&lt;/a&gt; for compaction tracing support&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Thanos Process&lt;/strong&gt;&lt;br&gt;
Since my mentor and I were in timezones 11 and a half hours apart, our weekly sync up was on Sunday morning(for me)/Saturday night(for him). Time zones notwithstanding, we frequently communicated synchronously.&lt;/p&gt;

&lt;p&gt;The first few meetings involved clarifying the project goals and understanding the Thanos compactor. Each meeting had a defined agenda and actionables for the next week.&lt;/p&gt;

&lt;p&gt;Most meetings had me relentlessly asking my mentor all kinds of queries (Some in hindsight, very trivial). Each meeting would end with my mentor detailing the week’s actionables to me and each week would be peppered with queries on the nuts and bolts of the implementation. When I started on my first major PR(which was ultimately not merged), I was completely off the mark when coding my first draft. My mentor had to then break down the implementation into steps to get me onto the right track.&lt;/p&gt;

&lt;p&gt;My project PRs were frequently reviewed by my mentor and I tried to resolve the comments as fast as possible. This led to PRs with a short review-response cycle with some intense discussions and a very steep learning curve from the reviews. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting started as a contributor&lt;/strong&gt;&lt;br&gt;
The first step to familiarising myself was setting up Thanos locally. In the process, I discovered that an interactive example lacked instructions and opened a PR for the same. This was my first PR and undoubtedly an easy one, which boosted my confidence a teeny bit. &lt;/p&gt;

&lt;p&gt;The mentorship was the first time I was contributing to Thanos. To get acquainted with the code, my mentor suggested I take on some good first issues. My first such issue involved refactoring a file and had earlier PRs refactoring other files similarly, for reference. Even though this PR was relatively straightforward in terms of coding(since I wasn’t completely new to Go), it taught me about the Thanos PR process, which, incidentally, is very thorough. The reviewers found nits as small as an extra pair of parentheses in my commits!&lt;/p&gt;

&lt;p&gt;Taking the initiative to work on small issues definitely paved the way for a smoother mentorship when I was working on my project. It helped me in many ways, the most obvious one being getting familiarised with navigating the codebase and the review process. In hindsight, I realise that one of the other ways it benefited me was the confidence I developed when I took on an issue, however trivial, discussed the approach with a maintainer and then iterated based on the reviews. The supportive community definitely merits a mention here and had it not been for the encouragement extended to the mentees, this would have been an intimidating task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learnings&lt;/strong&gt;&lt;br&gt;
Prior to the mentorship, I thought I was experienced enough in Go, having coded in it for roughly a year. What did I know, that myth would be soon busted! One of the things that caught my eye as a new contributor was the style guide. Opinionated and quite exhaustive, the style guide has some really interesting Go conventions. The few that struck with me are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pre-allocating slices and maps if we are aware of their size&lt;/li&gt;
&lt;li&gt;Using values of type struct{} with maps since they are allocated with 0 bytes&lt;/li&gt;
&lt;li&gt;Handling errors from deferred functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since my project involved adding metrics to a Prometheus exporter, I learnt about the types of metrics and developed a small side project to export some data. This was interesting and came in useful when I had to add metrics related to compaction, downsampling and retention to the upstream codebase. &lt;/p&gt;

&lt;p&gt;Initially, I struggled to understand how I would pare down the compaction process to its essentials to simulate it and predict the blocks and compaction runs. My mentor then had to explain it to me using skeletal pseudo code, which cleared the fog a little but the implementations of some concepts like filter and syncer were still hazy. I understood those better in the last phase when after asking my nth trivial doubt, my mentor explained a crucial fact about syncer’s working which, in hindsight, explained the implementation details of code I had already worked on for a prior feature. The next phase, working on downsampling simulation, saw me a tiny bit more independent since the codebase is relatively more straightforward compared to the compaction codebase. I still considered adding some non-essentials to the process, which my mentor had to tell me were unnecessary.  The last leg of the project involved working on a stretch goal, adding retention metrics. This was fairly straightforward since the retention codebase seemed more intuitive and it was the third time I was adding metrics for a process. Third time’s the charm :) &lt;/p&gt;

&lt;p&gt;The exhaustive review process(easily a 100+ review comments, led by my indefatigable mentor) taught me a lot about considering some of the finer aspects of a feature, usually related to Prometheus and the Go SDK. These ranged from always specifying a registry when creating a new metric with promauto to avoid registering with the default global registry, using Reset() to clear earlier exported values and the basics of promtestutil for related testing.  &lt;/p&gt;

&lt;p&gt;Another area I learnt a lot about was structuring my code including,surprisingly, naming functions/variables and writing comments! &lt;br&gt;
The first draft of my PR lacked the ‘code consistency’ of the final PR in terms of proper use of interfaces, consistent function signatures and abstraction of related data structures. Aside: the Thanos codebase is a pretty good example of a well structured Go codebase. Definitely worth your while exploring it!&lt;br&gt;
I could never get variable/struct/function names right and fixing them seemed like a never-ending process during the reviews. Turns out, I either did not comment enough or when I did, my comments were too detailed and not abstract enough. Always remember: All comments at Thanos end with a period. Period. &lt;/p&gt;

&lt;p&gt;The first major PR, which I worked hard on for a couple of weeks,was made redundant. It not being merged did dampen my morale for a tiny bit. However, apart from the technical learnings about the internals of compactor, the overarching lesson was not letting my enthusiasm dampen when faced with such a situation and not considering the time spent as a sunk cost.  This, I would say, was one of the most important lessons from the mentorship. &lt;/p&gt;

&lt;p&gt;To sum it up, the mentorship gave my progression as a developer a fillip in terms of not just technical and non-technical knowledge, but fostered a connection with an inspiring, supportive community and made me aware of interesting opportunities and perspectives which I wouldn’t have been if not for the mentorship. Truly thankful to everyone who helped me in this journey, my mentor, my co-mentees, the Thanos maintainers and the wider Thanos community!&lt;/p&gt;

</description>
      <category>mentorship</category>
      <category>thanos</category>
      <category>opensource</category>
      <category>observability</category>
    </item>
    <item>
      <title>Custom Prometheus Metrics with Go</title>
      <dc:creator>Aditi Ahuja</dc:creator>
      <pubDate>Sun, 26 Sep 2021 12:58:06 +0000</pubDate>
      <link>https://forem.com/metonymicsmokey/custom-prometheus-metrics-with-go-520n</link>
      <guid>https://forem.com/metonymicsmokey/custom-prometheus-metrics-with-go-520n</guid>
      <description>&lt;p&gt;This post is intended to be a guide for understanding and working on Prometheus instrumentation using the go client. I personally did not feel any existing content on this was comprehensive enough and hence, decided to pen down my lessons from recent trial-and-error development.&lt;/p&gt;

&lt;h2&gt;
  
  
  End Goal
&lt;/h2&gt;

&lt;p&gt;Developing an exporter for weather data fetched using the API.&lt;br&gt;&lt;br&gt;
All of the code referred to in the post is from &lt;a href="https://github.com/metonymic-smokey/prom-instrumentation"&gt;this repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A working installation of Prometheus running on port 9090 &lt;/li&gt;
&lt;li&gt;Go 1.15+&lt;/li&gt;
&lt;li&gt;An API key from &lt;a href="https://app.tomorrow.io/home"&gt;tomorrow.io&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;A basic understanding of metrics and exporters - &lt;a href="https://blog.pvincent.io/2017/12/prometheus-blog-series-part-1-metrics-and-labels/"&gt;this&lt;/a&gt; is an article I recommend. I will be covering the practical aspects related to developing and using them. &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Querying the weather API
&lt;/h2&gt;

&lt;p&gt;Our GET request will query the API to gather data about the temperature in metric units at specific coordinates, with a 1 hour interval between data points.&lt;/p&gt;

&lt;p&gt;The GET request can be made using &lt;code&gt;NewRequest&lt;/code&gt; from &lt;code&gt;net/http&lt;/code&gt;. Replace the "APIKEY" string with your API key.&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="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.tomorrow.io/v4/timelines?location=%f,%f&amp;amp;fields=temperature&amp;amp;timesteps=%s&amp;amp;units=%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;73.98529171943665&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;40.75872069597532&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"metric"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;NewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error in GET 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;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"apikey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"APIKEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;DefaultClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;err&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;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the response data is structured JSON, we create structs with specific fields to unmarshal the response. This is done using the &lt;code&gt;encoding/json&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;For the struct fields, look at this part of the repo. In the interests of brevity, I will not be adding those snippets here.&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="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error reading response"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dat&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;dat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error unmarshalling JSON"&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;h2&gt;
  
  
  Developing a custom exporter
&lt;/h2&gt;

&lt;p&gt;I am going to implement an exporter two ways, asynchronous and synchronous.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jobs and Registries
&lt;/h3&gt;

&lt;p&gt;Many target endpoints with the same purpose combine to form a job. &lt;/p&gt;

&lt;h3&gt;
  
  
  Serving Metrics
&lt;/h3&gt;

&lt;p&gt;Metrics can be 'served' i.e. exposed at a port of your choice. Here, I have chosen port 2112. I have chosen '/metrics' as my endpoint.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asynchronous&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this type of exporter, metrics are collected asynchronously. The function to collect the metrics is run asynchronously.&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;go&lt;/span&gt; &lt;span class="n"&gt;recordMetrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two metrics we will implement are gauges, &lt;code&gt;opsProcessed&lt;/code&gt; and &lt;code&gt;tempCelsius&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the initialisation for &lt;code&gt;tempCelsius&lt;/code&gt;:&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;var&lt;/span&gt; &lt;span class="n"&gt;tempCelsius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;promauto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewGauge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;prometheus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GaugeOpts&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;"current_temperature_api_celsius"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Help&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Current temperature"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Name&lt;/code&gt; should be a fully qualified Prometheus metric name and &lt;code&gt;Help&lt;/code&gt; is the description that appears on hovering over the metric in the UI.&lt;/p&gt;

&lt;p&gt;Since we are using &lt;code&gt;promauto&lt;/code&gt; to initialise them, we do not need to explicitly add these to the registry.&lt;/p&gt;

&lt;p&gt;Each call to the API returns multiple temperatures, each of which will be treated as a separate data point. To add these to the gauge, we will use &lt;code&gt;gauge.Set(dataPoint)&lt;/code&gt;  as shown:&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;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;dat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timestep&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TempVal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;tempCelsius&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Temp&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;The above code snippet is an example of 'direct instrumentation' where the same metrics are updated on each scrape. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Synchronous.
We first create a registry and register the standard Process and Go metrics.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;reg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;prometheus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;reg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MustRegister&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewProcessCollector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessCollectorOpts&lt;/span&gt;&lt;span class="p"&gt;{}),&lt;/span&gt;
        &lt;span class="n"&gt;collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewGoCollector&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;The metrics we will be monitoring are temperature fetched using the weather API, the time taken for those requests and the humidity percentage.&lt;/p&gt;

&lt;p&gt;This manner of metric exporting involves 'collectors'. A collector simply represents a set of metrics. In contrast to 'direct instrumentation', this involves creating new metrics each time with &lt;code&gt;MustNewConstMetric&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's first describe the temperature and humidity metrics. This is the description for temperature:&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="n"&gt;tempDesc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prometheus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"temperature_city_fahrenheit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"temperature of a city in fahrenheit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&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;The first parameter is the metric name, this is the name that will be used when querying the metric from the UI.&lt;/p&gt;

&lt;p&gt;The second is the description/help string. It gives a brief description of the metric.&lt;/p&gt;

&lt;p&gt;A struct &lt;code&gt;CityStats&lt;/code&gt; is defined. Based on this struct, the &lt;code&gt;Collect&lt;/code&gt; and &lt;code&gt;NewCityStats&lt;/code&gt; functions, and the &lt;code&gt;CityStatsCollector&lt;/code&gt; interface are defined.&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;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt; &lt;span class="n"&gt;CityStatsCollector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;prometheus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metric&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;tempByCity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;humidityByCity&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CityStats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TemperatureAndHumidity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;tempByCity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;prometheus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MustNewConstMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;tempDesc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;prometheus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CounterValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;city&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the &lt;code&gt;TemperatureAndHumidity&lt;/code&gt; function basically returns the results of the API call. Each of those are added to the metric channel &lt;code&gt;ch&lt;/code&gt; using &lt;code&gt;MustNewConstMetric&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;Let's understand the structure of the Prometheus config file used with such exporters.&lt;/p&gt;

&lt;p&gt;We will be working with a very basic version of the file. &lt;/p&gt;

&lt;p&gt;First, we have the &lt;code&gt;global&lt;/code&gt; rules. Here, we will focus on 'scrape_interval'. &lt;/p&gt;

&lt;p&gt;Scrape interval refers to the time period between two consecutive scraping runs.  Scraping is the process of gathering data for all the metrics from specific endpoints or 'targets'.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next up, we have the scrape configs. Each job here should be assigned a unique 'job_name'. &lt;/p&gt;

&lt;p&gt;Job configs can be specified in two ways, either statically or dynamically using the available service discovery mechanism. &lt;/p&gt;

&lt;p&gt;For now, we will be focusing on the static configs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
    &lt;span class="s"&gt;static_configs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;localhost:2112&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoints we mentioned above, 'targets' are assigned to a job and are one or more monitored URL endpoints that Prometheus will scrape metrics from.&lt;/p&gt;

&lt;p&gt;Run Prometheus as usual with the config file specs discussed above.&lt;br&gt;
To run either the synchronous or asynchronous exporters, just run them using &lt;code&gt;go run sync.go temp.go&lt;/code&gt; or &lt;code&gt;go run async.go temp.go&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Hopefully this was a good primer on getting started with exporting custom metrics. Would love to discuss and hear feedback in the comments. &lt;/p&gt;

</description>
      <category>go</category>
      <category>prometheus</category>
      <category>observability</category>
    </item>
  </channel>
</rss>
