<?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: Rob Hoelz</title>
    <description>The latest articles on DEV Community by Rob Hoelz (@hoelzro).</description>
    <link>https://dev.to/hoelzro</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%2F15194%2Fa333215c-17f2-4a65-aa4d-2326ec5fe031.jpg</url>
      <title>DEV Community: Rob Hoelz</title>
      <link>https://dev.to/hoelzro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hoelzro"/>
    <language>en</language>
    <item>
      <title>Avoiding spurious wakeups with my ThinkPad X1 Yoga</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Sat, 01 Jan 2022 18:42:04 +0000</pubDate>
      <link>https://dev.to/hoelzro/avoiding-spurious-wakeups-with-my-thinkpad-x1-yoga-20co</link>
      <guid>https://dev.to/hoelzro/avoiding-spurious-wakeups-with-my-thinkpad-x1-yoga-20co</guid>
      <description>&lt;p&gt;Last year I bought myself a ThinkPad X1 Yoga to use as my personal laptop - I've been pretty happy with it so far, but there's one problem I've run into that I've noticed a lot more lately - the laptop will &lt;em&gt;sometimes&lt;/em&gt; wake up while the lid is closed, go back to sleep, and then repeat that whole process every few minutes. This can't be good for the hardware, so I dug into this issue and wanted to share my findings with anyone who's having the same problem running Linux on this laptop!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr - disable Thunderbolt wake events by running &lt;code&gt;echo RP13 &amp;gt; /proc/acpi/wakeup&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A bit of searching around the internet pointed me at &lt;code&gt;/proc/acpi/wakeup&lt;/code&gt;, which lists and controls various ACPI devices that can trigger a wake-up, which ACPI state they work with, and whether or not they're currently allowed to wake the device up. I took a quick look to see which devices were allowed to trigger a wake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ grep enabled /proc/acpi/wakeup
GLAN S4 *enabled pci:0000:00:1f.6
XHC S3 *enabled pci:0000:00:14.0
RP09 S4 *enabled pci:0000:00:1d.0
RP13 S4 *enabled pci:0000:00:1d.4
PXSX S4 *enabled pci:0000:05:00.0
SLPB S3 *enabled platform:PNP0C0E:00
LID S4 *enabled platform:PNP0C0D:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm &lt;em&gt;pretty&lt;/em&gt; sure I want &lt;code&gt;LID&lt;/code&gt; to bring my laptop out of sleep, and I don't know what &lt;code&gt;SLPB&lt;/code&gt; is. I suspected that the culprit causing the wake-ups was one of the various PCI devices here, so I disabled all of those by running &lt;code&gt;echo $DEVICE &amp;gt; /proc/acpi/wakeup&lt;/code&gt; as root for each of them (except for &lt;code&gt;PXSX&lt;/code&gt; - for some reason writing that to &lt;code&gt;/proc/acpi/wakeup&lt;/code&gt; didn't flip its state). I closed my laptop, and it didn't wake until I opened it back up again, huzzah!&lt;/p&gt;

&lt;p&gt;I wasn't 100% satisified with this, though - I wanted to know if this showed up again in the future, and I wanted to know &lt;em&gt;which&lt;/em&gt; device was causing the wake-ups. I figured that looking for WiFi connection events in my router's logs would serve as a pretty decent proxy for a wake-up event, so I set up &lt;a href="https://github.com/fstab/grok_exporter"&gt;grok exporter&lt;/a&gt; to look for those in my logs and export those as a counter named &lt;code&gt;pavo_wifi_connections_total&lt;/code&gt; ("pavo" being my laptop's hostname). Once I had that metric in Prometheus, I wrote a simple alert rule to ping me if the laptop connected more than six times in the last hour: &lt;code&gt;increase(pavo_wifi_connections_total[1h]) &amp;gt; 6&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At this point, I conducted a few experiments by disabling/reenabling devices selectively. After some trial and error, I discovered that I only need to disable the &lt;code&gt;RP13&lt;/code&gt; device - whatever that is - to prevent the false wake-ups! I was curious, so I combed through &lt;code&gt;lshw&lt;/code&gt;'s output and discovered that &lt;code&gt;RP13&lt;/code&gt; corresponds to the laptop's Thunderbolt bus. So then I just wrote a little systemd service to disable that device upon every boot and called it day!&lt;/p&gt;

&lt;p&gt;I'm not sure &lt;em&gt;why&lt;/em&gt; that was triggering the wake-ups - maybe there's some weird electrical interference going on, or maybe the touchscreen goes through that bus somehow and there was some gunk on the screen generating events or something. I'm not going to dig into that any more, but if you know the answer or have an idea I'd love to hear about it!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>prometheus</category>
    </item>
    <item>
      <title>Random zsh tips</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Sat, 24 Oct 2020 15:47:22 +0000</pubDate>
      <link>https://dev.to/hoelzro/random-zsh-tips-4k2d</link>
      <guid>https://dev.to/hoelzro/random-zsh-tips-4k2d</guid>
      <description>&lt;h1&gt;
  
  
  Random zsh tips
&lt;/h1&gt;

&lt;p&gt;My friend &lt;a href="https://blog.afoolishmanifesto.com/"&gt;fREW&lt;/a&gt; and I both use zsh, and we've picked up a number of tricks over the years. We decided to collaborate on a post sharing some assorted tips that can really improve the zsh experience!&lt;/p&gt;

&lt;h1&gt;
  
  
  Noclobber
&lt;/h1&gt;

&lt;p&gt;This is a small thing, but it's saved me from many a headache. I (Rob) will often be working on some data, and I'll squirrel away a long-running or expensive computation to a temporary file, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ long-running-or-expensive-command &amp;gt; /tmp/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All's well and good, until I search through history too far, or until I mistype a search that yields the above command line, and I overzealously hit enter - whoops, there goes that data! This is especially frustrating if the data you just clobbered changes over time, and there was a case in the old data that isn't present in the new! Or maybe you saved the data, got on a plane, and wanted to work with it.&lt;/p&gt;

&lt;p&gt;Well, if you &lt;code&gt;set noclobber&lt;/code&gt; in your zshrc, zsh will prevent you from making this mistake - it will refuse to run commands that would destroy a file via shell redirection (something like &lt;code&gt;curl -Lo my-data.txt http://example.com/data.txt&lt;/code&gt; will still clobber &lt;code&gt;my-data.txt&lt;/code&gt;). You can override this via the &lt;code&gt;&amp;gt;|&lt;/code&gt; operator - personally, I prefer to explicitly &lt;code&gt;rm&lt;/code&gt; the destination file and then re-run the command.&lt;/p&gt;

&lt;p&gt;On a side note - wouldn't it be cool if the shell were smart enough to only clobber the destination file in the case of success, or if the shell somehow knew that &lt;code&gt;long-running-or-expensive-command&lt;/code&gt; could result in different results? Or maybe it would be neat to have a facility to keep a history of temporary files as you work with them!&lt;/p&gt;

&lt;h1&gt;
  
  
  Vi Mode
&lt;/h1&gt;

&lt;p&gt;Like noclobber, another little tweak that changed how I use zsh: &lt;code&gt;bindkey -v&lt;/code&gt; to enable Vi mode! I was skeptical about this until I tried it, but if you're a Vim user, I recommend you at least try it out and see how you like it - I feel like it's made me so much more efficient at editing command lines over the years!&lt;/p&gt;

&lt;h1&gt;
  
  
  Widgets &amp;amp; Keybindings
&lt;/h1&gt;

&lt;p&gt;zsh provides a feature called "widgets", which are really just functions that have a special relationship with the line editor. They're used to implement all of the various keybindings - for example, the default keymap assigns (most) keys to the &lt;code&gt;self-insert&lt;/code&gt; widget, which inserts the character corresponding to the key you just typed into the line editor.&lt;/p&gt;

&lt;p&gt;You can view the current keybindings via &lt;code&gt;bindkey -L&lt;/code&gt; (if you're using vi mode, you might want to check out &lt;code&gt;bindkey -L -M vicmd&lt;/code&gt; as well to see what widgets are bound in command mode) - this can reveal some interesting key combinations you didn't know about before! There are plenty of widgets that aren't bound to anything by default - you can find a bunch of interesting widgets in &lt;code&gt;man zshzle&lt;/code&gt; and &lt;code&gt;man zshcontrib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's also pretty easy to write your own - for example, I will sometimes accidentally type &lt;code&gt;!4&lt;/code&gt; instead of &lt;code&gt;!$&lt;/code&gt;, which expands to the fourth entry in my history. This is &lt;em&gt;never&lt;/em&gt; what I want, so I wrote a little widget to detect this situation and fix it to expand &lt;code&gt;!$&lt;/code&gt; instead.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tips by fREW
&lt;/h1&gt;

&lt;p&gt;Here are some random, but cool, features that I (fREW) really enjoy:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;push-line&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I bound the &lt;code&gt;q&lt;/code&gt; key in the &lt;code&gt;vicmd&lt;/code&gt; mode to &lt;code&gt;push-line&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bindkey -M vicmd "q" push-line
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does is set aside the current commandline, give you a fresh commandline to modify, and then pop the current line back. I typically use it in situations like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vi some/dir/some/file&amp;lt;ESC&amp;gt;q
$ mkdir -p some/dir/some/
$ vi some/dir/some/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;AUTO_PUSHD&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In both zsh and bash you can use &lt;code&gt;pushd some/dir&lt;/code&gt; to change directory into&lt;code&gt;some/dir&lt;/code&gt;, and then if you use &lt;code&gt;popd&lt;/code&gt; you'll change directory back to where you came from. If you set the &lt;code&gt;AUTO_PUSHD&lt;/code&gt; option, &lt;code&gt;cd&lt;/code&gt; will automatically&lt;code&gt;pushd&lt;/code&gt; for you. I'll then use &lt;code&gt;popd&lt;/code&gt; (or my alias for it, the symettrical&lt;code&gt;mk&lt;/code&gt;) to get back to where I started.&lt;/p&gt;

&lt;h2&gt;
  
  
  global aliases
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;zsh&lt;/code&gt; you can have aliases that are allowed anywhere in a commandline. I use &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;L&lt;/code&gt; constantly below, and always forget that I've defined &lt;code&gt;V&lt;/code&gt; even though I do that action manually a lot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias -g G="| grep"
alias -g L="| $PAGER"
alias -g V="| vim -"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ long-out-is-long G needle # finds lines matching needle
$ curl https://blog.afoolishmanifest.com/ L # runs with pager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pass arguments! This works fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls G -P '^f'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  History Events
&lt;/h1&gt;

&lt;p&gt;This tip has been shared a lot over the years, but honestly it's one of my favorites, and I feel like it saves me &lt;em&gt;so&lt;/em&gt; much time and typing! Let's say you're on the command line, and you issue the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir a-really-long-directory-name
$ cd a-really-long-directory-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's no fun having to type that long directory name twice, is it? Fortunately, you can use &lt;em&gt;history expansion&lt;/em&gt; (see "History Expansion" in &lt;code&gt;man zshexpn&lt;/code&gt; for more details) to save yourself some time here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can use &lt;code&gt;!$&lt;/code&gt; to refer to the last argument of the previous command - so that &lt;code&gt;cd&lt;/code&gt; above could be written as &lt;code&gt;cd !$&lt;/code&gt;. This is probably my most-used expansion, especially with &lt;code&gt;cd&lt;/code&gt; - I have even tweaked my &lt;code&gt;cd&lt;/code&gt; so that if my previous command was a &lt;code&gt;git clone&lt;/code&gt;, &lt;code&gt;cd !$&lt;/code&gt; will do the right thing and change directory into the repository I just cloned!&lt;/li&gt;
&lt;li&gt;You can use &lt;code&gt;!^&lt;/code&gt; to refer to the first argument of the previous command - I sometimes will do something like &lt;code&gt;mv file.txt dir/&lt;/code&gt; and then &lt;code&gt;vim !$/!^&lt;/code&gt; to edit the file I just moved!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!!&lt;/code&gt; expands to the entire previous command line - you'll often come across this in the idiom &lt;code&gt;sudo !!&lt;/code&gt; to re-invoke a command with sudo permissions, and I will sometimes do &lt;code&gt;time !!&lt;/code&gt; to time a command I just ran. Another way I use this is diffing output of commands - I'll do something like &lt;code&gt;some-command &amp;gt; /tmp/good-results&lt;/code&gt;, run &lt;code&gt;some-other-command&lt;/code&gt; and eyeball the results, and then do &lt;code&gt;combine &amp;lt;(!!) not /tmp/good-results&lt;/code&gt; to compare the two!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!*&lt;/code&gt; expands to the entire previous command line's arguments - so the same as &lt;code&gt;!!&lt;/code&gt;, just without the command itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;...:h&lt;/code&gt;, &lt;code&gt;...:t&lt;/code&gt; allows you to do some path manipulation on the expansion - &lt;code&gt;:h&lt;/code&gt; removes a trailing path component, &lt;code&gt;:t&lt;/code&gt; extracts the trailing path component, etc. It comes in handy if you do something like &lt;code&gt;vim long/path/to/a/file.txt&lt;/code&gt;, and then you want to search the directory &lt;code&gt;file.txt&lt;/code&gt; is in - it's just an &lt;code&gt;ack SEARCH_STRING !$:h&lt;/code&gt; away!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!!:2&lt;/code&gt; allows you to refer to the second argument of the previous command - you can use any number &lt;code&gt;n&lt;/code&gt; in &lt;code&gt;!!:n&lt;/code&gt;, but honestly I only ever find myself really using &lt;code&gt;!!:2&lt;/code&gt;, because at a point it's easier to copy and paste or something rather than count how many tokens into your command line the argument is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We hope you found something useful that you're itching to try out here! If you're interested in checking out our zsh configs, here they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hoelzro/zsh-config"&gt;Rob's&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/frioux/dotfiles/tree/main/zsh"&gt;fREW's&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many thanks to fREW for contributing to this post!&lt;/p&gt;

</description>
      <category>zsh</category>
    </item>
    <item>
      <title>Discovering your Prometheus via PromQL</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Sat, 26 Sep 2020 22:04:17 +0000</pubDate>
      <link>https://dev.to/hoelzro/discovering-your-prometheus-via-promql-2n8i</link>
      <guid>https://dev.to/hoelzro/discovering-your-prometheus-via-promql-2n8i</guid>
      <description>&lt;h1&gt;
  
  
  Discovering your Prometheus via PromQL
&lt;/h1&gt;

&lt;p&gt;Knowing about all the metrics available on a Prometheus instance can sometimes feel impossible, so I'd like to share a trick I devised to making exploring those metrics easier!&lt;/p&gt;

&lt;p&gt;One of Prometheus' strengths is its ability to ingest a good deal of data from a wide variety of data sources, due to the simplicity of its metrics format. However, if you work at an organization with multiple teams managing Prometheus exporters, you can end up feeling a little lost or overwhelmed with all of the metrics available to you, especially if you're on an infrastructure team that makes use of lower-level metrics for its alert rules. These kinds of metrics often come from third-party exporters your team didn't write, so you might not know all of the metrics available to you.&lt;/p&gt;

&lt;p&gt;Prometheus &lt;em&gt;does&lt;/em&gt; offer up lists of metrics in an autocomplete dialog when you start typing a query, and you can accomplish something similar if you're using Grafana. However, that workflow is more suited to "known unknowns" rather than "unknown unknowns" - it's more like searching a library for a book via the catalog search opposed to just browsing the stacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  List all metrics for a given exporter
&lt;/h3&gt;

&lt;p&gt;Let's say you want a list of all metrics exported by Prometheus when it scrapes itself - the trick inquestion looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;count({job="prometheus"}) by ( __name__ )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Depending on your experience with Prometheus, you may or may not have seen that &lt;code&gt;__name__&lt;/code&gt; label before. &lt;code&gt;__name__&lt;/code&gt; is a special label in Prometheus - it's the name of the metric itself. In fact, &lt;code&gt;foo{bar="baz"}&lt;/code&gt; is really just sugar for &lt;code&gt;{ __name__ ="foo",bar="baz"}&lt;/code&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Listing all "namespaces" for a given exporter
&lt;/h3&gt;

&lt;p&gt;Running this against my own Prometheus, I get 202 results - here's a small sampling:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_tsdb_isolation_low_watermark{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_tsdb_lowest_timestamp_seconds{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_rule_group_duration_seconds_sum{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_sd_file_read_errors_total{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_tsdb_compactions_total{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;go_memstats_gc_sys_bytes{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_tsdb_head_min_time{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_tsdb_head_truncations_failed_total{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_tsdb_compaction_chunk_size_bytes_count{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;net_conntrack_dialer_conn_established_total{}&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_tsdb_head_chunks_removed_total{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus_tsdb_tombstone_cleanup_seconds_count{}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are two important takeaways here - first, the metrics aren't ordered by name, and in fact, the result ordering can change between executions. Because of this, I will often run these kinds of exploratory queries using &lt;a href="https://github.com/prometheus/prometheus/tree/master/cmd/promtool"&gt;promtool&lt;/a&gt; and pipe them to tools like &lt;code&gt;jq&lt;/code&gt; or &lt;code&gt;sort&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also see that the metrics follow a sort of namespacing scheme. We can use &lt;a href="https://prometheus.io/docs/prometheus/latest/querying/functions/#label_replace"&gt;label_replace&lt;/a&gt; to extract the first level of "namespaces":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;count(label_replace({job="prometheus"}, "metric_namespace", "$1", " __name__", "^([^_]+).*")) by(metric_namespace)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;...which gives us a list that looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{metric_namespace="prometheus"}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{metric_namespace="promhttp"}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{metric_namespace="scrape"}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{metric_namespace="up"}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{metric_namespace="go"}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{metric_namespace="net"}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{metric_namespace="process"}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This listing can give us some hints on additional filters to use to zero in on what we're looking for - for example, if I'm interested in low-level metrics about how Prometheus communicates with things over the network, I can dig into the &lt;code&gt;net&lt;/code&gt; "namespace" by adding &lt;code&gt;__name__ =~"net_.*"&lt;/code&gt; to my vector selector! Since these "namespaces" are not documented within the metrics themselves, you often need to explore each one and see what you find to figure out which each one is about.&lt;/p&gt;

&lt;h3&gt;
  
  
  List all metrics for all exporters
&lt;/h3&gt;

&lt;p&gt;You could tweak that first query slightly to list metrics for &lt;em&gt;every&lt;/em&gt; job in your Prometheus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;count({ __name__!=""}) by ( __name__ )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;One noteworthy thing about this query when compared to the first one is the &lt;code&gt;__name__!=""&lt;/code&gt; bit - it might seem a bit strange if you're not used to it! It's just one possible shorthand for "give me all time series". The reason for that part is that PromQL requires that a vector selector has at least one label match that doesn'tmatch the empty string, so you can't use something like &lt;code&gt;{}&lt;/code&gt; or &lt;code&gt;{ __name__ =~".*"}&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  List all time series for a given exporter
&lt;/h3&gt;

&lt;p&gt;Another change you could make to that initial query would be to list all &lt;em&gt;time series&lt;/em&gt; - rather than metrics - that an exporter offers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{job="prometheus"}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This allows you to see all label permutations for each metric. Be warned though: this can result in a &lt;em&gt;lot&lt;/em&gt; of output, so I recommend adding some additional filters as you explore. This is another situation where I'd reach for &lt;code&gt;promtool&lt;/code&gt;, which I mentioned above. It's not uncommon for me to use &lt;code&gt;promtool&lt;/code&gt; to dump all of the time series to a file and then use shell tools (or even just explore the file in &lt;code&gt;vim&lt;/code&gt;!) to get a better idea of what is available to me.&lt;/p&gt;




&lt;p&gt;I hope you found this helpful, and that you now feel empowered to explore what kinds of metrics are available on Prometheus instances you use every day! Do you have any neat PromQL tricks you've discovered? If so, let me know!&lt;/p&gt;

&lt;p&gt;Many thanks to &lt;a href="https://blog.afoolishmanifesto.com/"&gt;Frew Schmidt&lt;/a&gt;, &lt;a href="https://genehack.org/"&gt;John Anderson&lt;/a&gt;, &lt;a href="https://xdg.me/"&gt;David Golden&lt;/a&gt;, &lt;a href="https://www.jonathanyeong.com/"&gt;Jonathan Yeong&lt;/a&gt;, and &lt;a href="https://lizlam.github.io/"&gt;Liz Lam&lt;/a&gt; for reviewing this post!&lt;/p&gt;

</description>
      <category>prometheus</category>
      <category>monitoring</category>
      <category>observability</category>
      <category>devops</category>
    </item>
    <item>
      <title>What do you do with five minute chunks of time?</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Sat, 12 Sep 2020 02:01:53 +0000</pubDate>
      <link>https://dev.to/hoelzro/what-do-you-do-with-five-minute-chunks-of-time-2m4c</link>
      <guid>https://dev.to/hoelzro/what-do-you-do-with-five-minute-chunks-of-time-2m4c</guid>
      <description>&lt;h1&gt;
  
  
  What do you do with five minute chunks of time?
&lt;/h1&gt;

&lt;p&gt;During my morning planning today, I was thinking about the highest priority work task I had on my plate. Without getting too into the details, I introduced a new component into our system, and it's been failing in... &lt;em&gt;interesting&lt;/em&gt; ways. I can issue a command to invoke that failure, but it takes about five minutes to run its course. I figured that I might be alternating several times between provoking this failure and analyzing the new findings - so, I thought to myself: what's the best way to spend that time? Here are some thoughts I had on that:&lt;/p&gt;

&lt;h2&gt;
  
  
  Check Slack
&lt;/h2&gt;

&lt;p&gt;This is what I used to do, since helping others out at work &lt;em&gt;is&lt;/em&gt; part of my job, but it would often send my attention into a downward spiral. This is probably a bad idea - fortunately, I still get notifications for messages to me or my team's group if people need help, and &lt;a href="https://hoelz.ro/blog/making-slack-go-slack-jawed-reducing-the-mental-load-around-slack-notifications"&gt;I have a system in place&lt;/a&gt; to help me cut down on the other distractions Slack offers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work on another task for five minutes
&lt;/h2&gt;

&lt;p&gt;This seems like the right answer, but I think this one is also a trap! Five minutes is a fair amount of time, but in my opinion, it's too short to justify the heavy cost of a context switch. Out of all likelihood, you're just going to end up doing the other task (or worse, both tasks) poorly. After all, your brain isn't a computer - if you think context switching is bad for your OS, it's worse for your mind!&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate it into a longer cycle
&lt;/h2&gt;

&lt;p&gt;In this case, I mean write some code to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trigger the failure&lt;/li&gt;
&lt;li&gt;Wait for it to pass&lt;/li&gt;
&lt;li&gt;Record data&lt;/li&gt;
&lt;li&gt;Maybe wait a minute or two&lt;/li&gt;
&lt;li&gt;Repeat, say, for an hour&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After that's started running, spend that hour working on the &lt;em&gt;second-most&lt;/em&gt; important thing for the day, and then spend a deliberate chunk of time analyzing the results once those are in.&lt;/p&gt;

&lt;p&gt;This has the advantage that you can reduce those expensive context switches while still collecting a lot of information for later analysis. Unfortunately, it &lt;em&gt;does&lt;/em&gt; take time to write that code, and what if the first iteration yielded enough information for you to figure things out? As always, use your best judgement here!&lt;/p&gt;

&lt;h2&gt;
  
  
  Make that five minute chunk into a fifteen minute one
&lt;/h2&gt;

&lt;p&gt;If you don't need to babysit the situation in question, trigger the issue and switch contexts to something else, but know (and be content with!) the fact that the thing will be finished for a while before you get back to it. The point here - surprise surprise - is to avoid the context switch!&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce the wait time
&lt;/h2&gt;

&lt;p&gt;This doesn't apply to my problem today, but let's say you have a test suite that takes five minutes to run. It &lt;em&gt;might&lt;/em&gt; be worth the effort to cut its run time in half if you can manage it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Just sit with it
&lt;/h2&gt;

&lt;p&gt;This is hard, because it can feel like a waste of time. But it keeps the current task loaded in your mind, and gives your mind a chance to let some of the facts settle and consider the bigger picture. You might think of an aspect of what you're working on that you hadn't considered, or you might be able to plan the kinds of analyses you'll run on the data you collect to get to your next cycle (or perhaps even the end of the task!) more quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, what'd I end up doing?
&lt;/h2&gt;

&lt;p&gt;I went with the last one: I just sat with it. Or rather, I walked with it - I paced around my office and thought about the problem from different angles, and I wrote down some of my thoughts and hypotheses on my whiteboard. I feel like that allowed me to reach the solution more quickly, consider what error messages I'd been seeing that were red herrings, and take into account other things in play.&lt;/p&gt;

&lt;p&gt;Like most things in software development - or even in life in general - that doesn't mean this is always the right solution. On a day where other developers are having more problems, I might be checking Slack more to chime in and help any way I can, or on a different type of task, I might invest some time in that automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Others?
&lt;/h2&gt;

&lt;p&gt;Do you have strategies you employ in this situation that I didn't think of? If so, please let me know!&lt;/p&gt;

&lt;h2&gt;
  
  
  More on this from other people
&lt;/h2&gt;

&lt;p&gt;If the idea of sitting with a problem and letting your mind do its thing resonates with you, you might enjoy the &lt;a href="https://www.youtube.com/watch?v=f84n5oFoZBc"&gt;talk&lt;/a&gt; "Hammock Driven Development" by Rich Hickey. I've never used Clojure in anger, but I always like hearing what Rich has to say, which &lt;em&gt;really&lt;/em&gt; makes me want to give it a shot!&lt;/p&gt;

&lt;p&gt;I took a &lt;a href="https://peterakkies.net/products/big-picture-productivity"&gt;course&lt;/a&gt; on productivity this summer, and the creator Peter wrote a &lt;a href="https://peterakkies.net/blog/being-productive-brushing-teeth"&gt;blog post&lt;/a&gt; on "toothbrush productivity", which this situation reminded me of. Having read books and taken other courses on productivity in the past, I found Peter's ideas refreshing - much like brushing your teeth - so I recommended checking it out!&lt;/p&gt;

&lt;p&gt;This idea also got me thinking: what's the smallest time slice you should consider dividing your time into? I chose a fairly arbitrary number of fifteen minutes here, but I'm wondering if that's too small. The &lt;a href="https://en.wikipedia.org/wiki/Pomodoro_Technique"&gt;Pomodoro Technique&lt;/a&gt; hints that 25 minutes is better, and my fellow Milwaukeean developer Joe Clermont wrote a &lt;a href="https://joelclermont.com/post/2020-09/benefits-of-keeping-a-work-journal/"&gt;blog post today&lt;/a&gt; about keeping a work journal and thinking in 30-60 minute chunks.&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>Save time by focusing on the result - using Prometheus' textfile collector to avoid writing an exporter</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Mon, 07 Sep 2020 19:43:59 +0000</pubDate>
      <link>https://dev.to/hoelzro/save-time-by-focusing-on-the-result-using-prometheus-textfile-collector-to-avoid-writing-an-exporter-4dp0</link>
      <guid>https://dev.to/hoelzro/save-time-by-focusing-on-the-result-using-prometheus-textfile-collector-to-avoid-writing-an-exporter-4dp0</guid>
      <description>&lt;h1&gt;
  
  
  Save time by focusing on the result - using Prometheus' textfile collector to avoid writing an exporter
&lt;/h1&gt;

&lt;p&gt;My friend &lt;a href="https://blog.afoolishmanifesto.com"&gt;Frew&lt;/a&gt;, as well as others, have said that our job as developers is not to write code, but rather to solve problems. I had a win in this regard relatively recently where I saved myself a good chunk of time by leveraging a Prometheus component for a purpose it may not have been designed for, but is remarkably well suited to.&lt;/p&gt;

&lt;p&gt;A few months ago, I had a problem with my server - dhcpcd would get into a weird state roughly every 2 weeks and lose its IPv4 address, which is not great for a server that is hosting your personal site! I turned on debug logging for dhcpcd (of which there is a &lt;em&gt;lot&lt;/em&gt; - I ended up putting the logs in their own journald namespace to enforce a separate disk quota!), and I noticed that dhcpcd started spitting out some strange errors roughly 24 hours before the address got lost.&lt;/p&gt;

&lt;p&gt;I have &lt;a href="https://hoelz.ro/blog/using-aws-lambda-to-improve-lambda-error-reports"&gt;monitoring in place&lt;/a&gt; to detect when my server is down, but it's no fun being under the gun investigating what's going on with dhcpcd while your site is down. I also use &lt;a href="https://prometheus.io"&gt;Prometheus&lt;/a&gt; to monitor various things, so to give myself some breathing room and time to investigate, I figured I could write a Prometheus exporter (Prometheus' term for a program that collects metrics for a Prometheus instance) for journald to export metrics about entry counts for &lt;code&gt;(namespace, unit, priority)&lt;/code&gt; tuples, and then preemptively alert when &lt;code&gt;(dhcpcd, dhcpd@eth0, ERROR)&lt;/code&gt;'s rate goes bananas.&lt;/p&gt;

&lt;p&gt;Now, writing a Prometheus exporter isn't super hard, but it &lt;em&gt;is&lt;/em&gt; work - and while a journald exporter might not be a bad tool to have in the future, I wasn't 100% confident I would need something so comprehensive. Was there a way I could get these logging metrics into my Prometheus without having to do the work of writing an exporter? Yes - through a very useful feature in Prometheus' node exporter: the textfile collector!&lt;/p&gt;

&lt;p&gt;Node exporter is an exporter you can run that reports various metrics about the system it's running on: CPU usage, memory usage, process count, disk usage, etc. It does this through a series of &lt;em&gt;collectors&lt;/em&gt; - you tell which collectors you're interested in when you start up node exporter. It will invoke the ones you asked for when it's scraped and serve up their metrics.&lt;/p&gt;

&lt;p&gt;One of these collectors, as I mentioned, is the textfile collector. All it does is look for &lt;code&gt;*.prom&lt;/code&gt; files in a directory of your choosing and serves up the metrics it parses out of them. This is ostensibly to cover things like custom system metrics that aren't available in node-exporter's considerable cadre of collectors, or things that would be difficult to add.&lt;/p&gt;

&lt;p&gt;Fortunately for us, it also happens to work very well for trying out new metrics very quickly! You could write a cronjob to dump metrics out to a file - the format is &lt;a href="https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format"&gt;very simple&lt;/a&gt; - and have some metrics to play with to decide whether or not you should invest more time on them in the future.&lt;/p&gt;

&lt;p&gt;Instead of spending my time writing a journald exporter, I spent 10 minutes crafting a Perl program to dump this data into a file for node exporter to pick up. And I haven't felt the need for a journald exporter since - this just goes to show that if you focus on solving the problem rather than trying to throw code at it, you can save yourself a lot of time!&lt;/p&gt;

&lt;p&gt;Of course, as with all things in software, there are tradeoffs - if you know &lt;em&gt;for sure&lt;/em&gt; you will need such an exporter in the future, obviously it's cheaper to just write the exporter. So use your best judgement!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>prometheus</category>
    </item>
    <item>
      <title>Surprising Logic in the Firefox Codebase</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Tue, 25 Aug 2020 21:01:54 +0000</pubDate>
      <link>https://dev.to/hoelzro/surprising-logic-in-the-firefox-codebase-1jkd</link>
      <guid>https://dev.to/hoelzro/surprising-logic-in-the-firefox-codebase-1jkd</guid>
      <description>&lt;h1&gt;
  
  
  Surprising Logic in the Firefox Codebase
&lt;/h1&gt;

&lt;p&gt;Recently, I was curious about how Firefox's reader view implements its estimated reading time, so I spent a little time looking through the Firefox codebase. It turns out that it uses the &lt;a href="https://github.com/mozilla/readability"&gt;readability package&lt;/a&gt; to extract the content of a page. Once it has that, it gets the length of that content and divides it by an estimated reading speed - and it was this part that surprised me:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://searchfox.org/mozilla-central/rev/fa7f47027917a186fb2052dee104cd06c21dd76f/toolkit/components/reader/ReaderMode.jsm#598-629"&gt;https://searchfox.org/mozilla-central/rev/fa7f47027917a186fb2052dee104cd06c21dd76f/toolkit/components/reader/ReaderMode.jsm#598-629&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="cm"&gt;/**
   * Returns the reading speed of a selection of languages with likely variance.
   *
   * Reading speed estimated from a study done on reading speeds in various languages.
   * study can be found here: http://iovs.arvojournals.org/article.aspx?articleid=2166061
   *
   * @return object with characters per minute and variance. Defaults to English
   * if no suitable language is found in the collection.
   */&lt;/span&gt;
  &lt;span class="nx"&gt;_getReadingSpeedForLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readingSpeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;987&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;118&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;612&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;de&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1078&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;121&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;998&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;126&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;he&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;833&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;130&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;950&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;140&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;357&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;56&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;978&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;143&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;916&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;126&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;913&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;145&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ru&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;986&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;175&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;885&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;145&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;917&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;156&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1054&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;156&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;readingSpeed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;readingSpeed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&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;The fact that the reading speed is calculated in &lt;em&gt;characters&lt;/em&gt; per minute - rather than words per minute - is what piques my interest, at least for English. I've seen many anecdotes about how English readers read in terms of whole words, rather than individual characters. There are many examples, such as &lt;a href="https://www.dictionary.com/e/typoglycemia/"&gt;this one&lt;/a&gt;, circulating the Internet of paragraphs with scrambled letters, but the only real research I was able to track down was &lt;em&gt;Dyslexia: Cultural Diversity and Biological Unity&lt;/em&gt; (which I have yet to read 😅), which talks about how English and Italian dyslexics process words differently.&lt;/p&gt;

&lt;p&gt;I find this property of English intriguing! It makes me wonder if we could develop a sort of "perceptual hash" for English misspellings, akin to image perceptual hashes, so that things like "careful" and "craeful" would hash to the same value, or even "definitely" and "definately" - although that latter example would probably be handled by a soundex function or something. I also wonder about a sort of "language aware distance function" that would be aware of common spelling mistakes such as "definately". Additionally, my notes from &lt;em&gt;ElasticSearch: The Definitive Guide&lt;/em&gt; mention that "Damerau observed that 80% of human misspellings have an edit distance of 1", so maybe that could factor in? One thing that might throw a wrench into this is how much context factors how we read words.&lt;/p&gt;

&lt;p&gt;If you know of research or existing algorithms/projects in this space, or if you have related ideas, please feel free to chime in - I'd love to hear about them!&lt;/p&gt;

</description>
      <category>linguistics</category>
      <category>codedive</category>
    </item>
    <item>
      <title>Making Slack go slack-jawed: reducing the mental load around Slack notifications</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Tue, 11 Aug 2020 17:47:51 +0000</pubDate>
      <link>https://dev.to/hoelzro/making-slack-go-slack-jawed-reducing-the-mental-load-around-slack-notifications-1o1j</link>
      <guid>https://dev.to/hoelzro/making-slack-go-slack-jawed-reducing-the-mental-load-around-slack-notifications-1o1j</guid>
      <description>&lt;h1&gt;
  
  
  Making Slack go slack-jawed: reducing the mental load around Slack notifications
&lt;/h1&gt;

&lt;p&gt;As I mentioned in a &lt;a href="https://hoelz.ro/blog/strategies-observations-and-open-questions-for-working-from-home"&gt;previous post&lt;/a&gt;, I use Slack at work, and I find it to be a constant source of distraction. I have taken to shutting it off for much of the day, which has the obvious disadvantage that if people &lt;em&gt;need&lt;/em&gt; to reach me, they can't! In that post, I admit my weakness of needing to acknowledge unread badges and proposed a sort of "narrowing" features for Slack. I wanted to follow up on that and discuss some of the solutions I thought of, as well as the one I'm currently using.&lt;/p&gt;

&lt;p&gt;Last month, I wrote a program I called "slack-jawed" - it took a list of Slack teams and set you to "do not disturb" for each of those teams, plus it muted a select set of channels for a period of time. That way, I could say &lt;code&gt;slack-jawed 2h&lt;/code&gt; and go heads-down for two hours, but I would still get notifications for direct messages or for messages on channels that I should be paying attention to. Unfortunately, Slack's &lt;a href="https://api.slack.com/changelog/2020-02-legacy-test-token-creation-to-retire"&gt;removal of personal access tokens&lt;/a&gt; and migration to only using app-based access tokens has sunk any hopes I had of using that - I can't install my slack-jawed app on every Slack team I'm on!&lt;/p&gt;

&lt;p&gt;Another thing I tried is making use of Slack's sidebar sections for channels - basically, all of the non-essential work channels go into a "Fun" section. This works pretty well, but the systray icon still has an unread badge on it, and like I said, needing to acknowledge that is a weakness of mine.. Fortunately, I managed to find a solution that is Good Enough™ and took me next to no time to develop!&lt;/p&gt;

&lt;p&gt;The recurring theme of my problem with Slack and distraction is that damn systray icon - I see it in the corner of my screen and feel compelled to acknowledge it. While working on focus and discipline is a worthwhile goal, and one that I'm striving towards, I figure why burn willpower if I don't have to - I use a &lt;a href="https://awesomewm.org"&gt;powerful window manager that lets me do pretty much whatever I want&lt;/a&gt;! So I dug into Awesome's systray widget a bit, seeing if there's a way I could filter out the Slack systray icon using a little bit of Lua code!&lt;/p&gt;

&lt;p&gt;Unfortunately, the freedesktop systray protocol is anything but simple - it actually uses XEmbed to embed application windows in the systray window! - and the logic for Awesome is written in C. If it were in Lua, I could at worst just copy the widget, make some modifications, and just run that - but since it's in C, I would have to fork the Awesome codebase and maintain my own package, and I don't know if that's worth the time. However, skimming over the systray protocol got me thinking - if systray icons are just regular windows, can't I just hide them?&lt;/p&gt;

&lt;p&gt;To continue down this road, we need a few more tools in our toolbelt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;xwininfo&lt;/code&gt; - dumps out attributes (such as size, position, and child windows) about a window or a set of windows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xprop&lt;/code&gt; - dumps out X properties attached to a window&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xdotool&lt;/code&gt; - conducts various X operations (such as moving, minimizing, and hiding) on windows without needing to write a program using Xlib or XCB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I did a little experiment - first I did &lt;code&gt;xwininfo -root -tree&lt;/code&gt; to dump the entire window hierarchy on my desktop and used that to find the Slack systray icon's window ID, which I'll call &lt;code&gt;$slack_icon_id&lt;/code&gt;. Then I used &lt;code&gt;xdotool windowunmap $slack_icon_id&lt;/code&gt; and voila - it hid the icon! With a little more sleuthing by way of &lt;code&gt;xprop&lt;/code&gt;, I was able to find a good set of criteria for identifying the Slack icon (it uses "slack" as its window class and it has the &lt;code&gt;_XEMBED_INFO&lt;/code&gt; property), and with that, I have a little script that I run in a &lt;code&gt;while&lt;/code&gt; loop that keep that pesky icon hidden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;id &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;xdotool search &lt;span class="nt"&gt;--class&lt;/span&gt; slack&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
&lt;/span&gt;xprop &lt;span class="nt"&gt;-id&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; _XEMBED_INFO &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; xdotool windowunmap &lt;span class="nv"&gt;$id&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This works, but honestly I'd rather have a little more diversity in the Slack client application space - especially an open source alternative. I've tried Scudcloud and Rambox in the past, but they both have their shortcomings. If anyone has any recommendations for a third-party Slack client that would help me manage distractions better, please let me know!&lt;/p&gt;

</description>
      <category>slack</category>
    </item>
    <item>
      <title>A little mistake I made in Go</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Sun, 26 Jul 2020 14:52:28 +0000</pubDate>
      <link>https://dev.to/hoelzro/a-little-mistake-i-made-in-go-4485</link>
      <guid>https://dev.to/hoelzro/a-little-mistake-i-made-in-go-4485</guid>
      <description>&lt;h1&gt;
  
  
  A little mistake I made in Go
&lt;/h1&gt;

&lt;p&gt;I was working on a little Go project &lt;del&gt;last night&lt;/del&gt;  &lt;del&gt;a few nights ago&lt;/del&gt; last weekend (boy it takes me way longer to finish blog posts these days!), and I wrote something like the following code, which has a bug in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;/*
  type payload struct {
    filename string // the name of the file to search
    resultsChan chan []byte // the channel along which to return results
  }
*/&lt;/span&gt;

&lt;span class="n"&gt;incomingWorkChan&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&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;workUnit&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;incomingWorkChan&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resultsChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// make sure we always close the channel we're sending results back on&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;doWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resultsChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// error handling elided for brevity&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;I'm sure most seasoned Gophers can spot the problem here immediately, but this tripped me up for a bit, so I thought I'd share this so that others can benefit from my mistake. The problem here is that &lt;code&gt;defer&lt;/code&gt; is &lt;em&gt;function&lt;/em&gt;-scoped, not block-scoped, so Go basically builds up a long list of channels to close via a long list of deferred functions to run when the goroutine eventually exits. Not only is this is a waste of memory, but since I'm trying to close a channel here, and there's another goroutine elsewhere in the program that depends on that channel getting closed to proceed, my program deadlocks, which is never a good thing!&lt;/p&gt;

&lt;p&gt;There are three solutions I can think of off the top of my head:&lt;/p&gt;

&lt;h2&gt;
  
  
  Just avoid &lt;code&gt;defer&lt;/code&gt; entirely
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&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;workUnit&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;incomingWorkChan&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;doWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resultsChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resultsChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c"&gt;// error handling elided for brevity&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;I'm not a huge fan of this, since if you add code to that loop in the future you need to be extra careful that you &lt;code&gt;close(workUnit.resultsChan)&lt;/code&gt; in every possible case. It &lt;em&gt;is&lt;/em&gt;, however, what I ended up doing, since I don't forsee that loop's body growing much - hopefully it doesn't bite me! 🤞&lt;/p&gt;

&lt;h2&gt;
  
  
  Call an anonymous function in the for loop and have the &lt;code&gt;defer close(...)&lt;/code&gt; happen there:
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&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;workUnit&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;incomingWorkChan&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resultsChan&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;doWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resultsChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c"&gt;// error handling elided for brevity&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;Although this solution doesn't have the same future-proofing problems as the previous, I'm not a huge fan - I don't really like the "inline anonymous function call" in Go, except for &lt;code&gt;go func() { ... }()&lt;/code&gt; and &lt;code&gt;defer func() { ... }()&lt;/code&gt;. I'm not really sure why I'm ok with those two forms and not the other - maybe it's because I prefer block-based scoping and the the &lt;code&gt;func() { ... }()&lt;/code&gt; feels like a hack to introduce something akin to block-based scoping here? Or maybe because the others are more established Go idioms in my mind?&lt;/p&gt;

&lt;p&gt;Minor segue - one thing I find interesting about Go is the number of idioms present in the use of the language. Establishment of idioms is not specific to Go by any means - I think that's just a result of the language being &lt;em&gt;used&lt;/em&gt; - but one of the selling points of Go is its simplicity, and that makes me think of Esperanto in a way. In particular, &lt;a href="http://archive.is/4xyuE"&gt;this quote&lt;/a&gt; I read when I was getting into constructed languages oh so long ago:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As for Esperanto, I don't know if Esperantists speak the language at home for their children to hear so that they learn it as a (second) native tongue. If they do, the kids will probably be producing changes very slowly over the years (if they do the same with their own children, and so on). This perhaps would horrify doctor Zamenhof and his followers, but it would be a sure sign that the language is indeed used for communication and is alive, a natural(ized) language among peers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Call a named function with the channel, and have the &lt;code&gt;defer close(...)&lt;/code&gt; happen within that function:
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;someFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultsChan&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultsChan&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;doWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultsChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&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;workUnit&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;incomingWorkChan&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;someFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// error handling elided for brevity&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;This could easily happen in &lt;code&gt;doWork&lt;/code&gt; itself, but I kind of like decoupling the closing of the channel from the generation of the results - if other functions are needed to generate results, I don't need to modify &lt;code&gt;someFunc&lt;/code&gt; later on, or wonder why my program is trying to write to a closed channel. This is probably the solution I like the most, since you're protected from forgetting a &lt;code&gt;close(workUnit.resultsChan)&lt;/code&gt; but you don't have to deal with the anonymous function weirdness.&lt;/p&gt;

&lt;h2&gt;
  
  
  (Your idea here!)
&lt;/h2&gt;

&lt;p&gt;What about you? Have you encountered this problem in the past, and if so, how did you solve it? Let me know!&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Using udev to disable my infrared camera on Linux</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Fri, 01 May 2020 01:30:49 +0000</pubDate>
      <link>https://dev.to/hoelzro/using-udev-to-disable-my-infrared-camera-on-linux-1gm8</link>
      <guid>https://dev.to/hoelzro/using-udev-to-disable-my-infrared-camera-on-linux-1gm8</guid>
      <description>&lt;h1&gt;
  
  
  Using udev to disable my infrared camera on Linux
&lt;/h1&gt;

&lt;p&gt;My work laptop has two cameras - one of the cameras is a regular, run-of-the-mill camera, and since I'm remote (well, due to COVID-19, I guess almost &lt;em&gt;everyone&lt;/em&gt; is these days!), I use it for video meetings a lot. The other camera is an infrared camera, and to be honest, I'm not sure what it's for. All I know is that because it's &lt;code&gt;/dev/video0&lt;/code&gt;, most applications treat the infrared camera as the default.&lt;/p&gt;

&lt;p&gt;Normally Firefox remembers which camera I used last (I typically use a &lt;em&gt;third&lt;/em&gt;, external USB camera), but lately I've been moving throughout the house throughout the work day, trying to change my surroundings, so Firefox is constantly confused as to which cameras are attached to my laptop. I'm sick of needing to manually select &lt;em&gt;away&lt;/em&gt; from the IR camera all the time, so I decided to disable the IR camera for once and for all.&lt;/p&gt;

&lt;p&gt;Looking around on the Internet for "disable Linux camera" provides no shortage of solutions - the most popular of which is "cover it up", which I've done with a piece of duct tape, but doesn't solve the camera selection problem. The next most popular solution is to the blacklist the &lt;code&gt;uvcvideo&lt;/code&gt; kernel module, which &lt;em&gt;will&lt;/em&gt; disable my IR camera, but along with all of my &lt;em&gt;other&lt;/em&gt; cameras, so that's a no-go. Fortunately, in my travels, I stumbled upon &lt;code&gt;bConfigurationValue&lt;/code&gt;, which if set to 0 will disable a camera. So now all I needed to do was find the camera's USB bus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo lshw
&amp;lt;snip&amp;gt;
    description: Generic USB 
    bus info: usb@1:5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;...and then navigate to its location in sysfs, and write a 0 to &lt;code&gt;bConfigurationValue&lt;/code&gt; to disable the camera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo 0 | sudo tee /sys/bus/usb/devices/1-5/bConfigurationValue
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And it works! Or doesn't, in this case! 🎉&lt;/p&gt;

&lt;p&gt;Now, this disables the camera, but it's not permanent - it'll be reenabled the next time I reboot. I &lt;em&gt;could&lt;/em&gt; write a systemd unit that runs on every boot to just write 0 to that sysfs file, but I wanted to do things &lt;em&gt;properly&lt;/em&gt; and use udev, since that's what it's there for.&lt;/p&gt;

&lt;p&gt;I took way too much time figuring out how to do this with udev; the short version is that I used &lt;code&gt;udevadm --monitor&lt;/code&gt; to watch for changes, and then I did &lt;code&gt;rmmod uvcvideo &amp;amp;&amp;amp; modprobe uvcvideo&lt;/code&gt; to force the kernel to run the udev rules. I also used a bogus &lt;code&gt;RUN{program}+="..."&lt;/code&gt; statement in my udev rules to spit out debugging information to a file in &lt;code&gt;/tmp/&lt;/code&gt; for situations in which I found &lt;code&gt;udevadm --monitor&lt;/code&gt; lacking (although in retrospect I probably could've used &lt;code&gt;udevadm test&lt;/code&gt;). I learned some interesting things from this exploration - I learned that there's an important difference between &lt;code&gt;ATTR&lt;/code&gt; and &lt;code&gt;ATTRS&lt;/code&gt; (the latter is what I wanted), and I learned that you need to match an attribute in your dev in order to interpolate &lt;code&gt;$attrs{attribute}&lt;/code&gt; into a &lt;code&gt;RUN&lt;/code&gt; statement. However, no matter what I did, I couldn't seem to get &lt;code&gt;bConfigurationValue&lt;/code&gt; to get updated! I decided to walk away and pick this up again the next morning.&lt;/p&gt;

&lt;p&gt;Well, I'm glad I did that, because something dawned on me - looking at my &lt;code&gt;RUN{program}&lt;/code&gt; output, I noticed that &lt;code&gt;devpath&lt;/code&gt; was always something underneath &lt;code&gt;/sys/bus/usb/devices/1-5&lt;/code&gt; - so that directory must represent some sort of controller higher up in the device hierarchy, rather than the camera itself. Looking at the output for &lt;code&gt;udevadm info --attribute-walk --path=/sys/bus/usb/devices/1-5&lt;/code&gt;, I noticed that it's governed by the &lt;code&gt;usb&lt;/code&gt; module, rather than &lt;code&gt;uvcvideo&lt;/code&gt;, so that makes sense why my udev rules weren't getting triggered, since I was only reloading the video module. I was loathe to remove and load the &lt;code&gt;usb&lt;/code&gt; module to try this out, but running &lt;code&gt;udevadm test&lt;/code&gt; seemed to apply the rule just fine!&lt;/p&gt;

&lt;p&gt;This ended up being the magic rule I needed to add to make my change permanent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACTION!="add|change", GOTO="camera_end"

ATTRS{idVendor}=="5986",ATTRS{idProduct}=="1141",ATTR{bConfigurationValue}="0"

LABEL="camera_end"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So that removes a major annoyance of mine with my cameras - the next step is to try to figure out if I can change the enumeration order so that if the external web camera is plugged in, it comes up first and is preferred. A &lt;a href="https://searchfox.org/mozilla-central/rev/7fd1c1c34923ece7ad8c822bee062dd0491d64dc/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc#226-244"&gt;cursory look at the Firefox code&lt;/a&gt; seems to indicate that it always starts from &lt;code&gt;/dev/video0&lt;/code&gt;, and as far as I understand udev, I can't really shuffle the order of those entries around. Some alternatives that have come to mind are 1) disabling the internal camera if the external camera is plugged in (more udev, yay!), or figuring out where Firefox stores its preferred camera information and messing with that. I rather like the first approach, especially since I have a script that I use to take my picture for Slack and it would benefit from this as well.&lt;/p&gt;

</description>
      <category>udev</category>
      <category>linux</category>
    </item>
    <item>
      <title>Strategies, Observations, and Open Questions for Working From Home</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Sun, 22 Mar 2020 14:08:07 +0000</pubDate>
      <link>https://dev.to/hoelzro/strategies-observations-and-open-questions-for-working-from-home-4knp</link>
      <guid>https://dev.to/hoelzro/strategies-observations-and-open-questions-for-working-from-home-4knp</guid>
      <description>&lt;p&gt;With the COVID-19 crisis forcing more people to sequester themselves at home and work remotely, there has been no shortage of articles describing how to maintain your productivity and sanity when working from home. I've been working from home for 3½ years, and I've learned a lot in the meantime, so I thought I'd share my own insights on the matter! There are a lot of commonalities among the various articles - things like "get dressed", "use a walk around the block as a commute" - I won't rehash these. Rather, I'd like to introduce you to some tips and tricks I haven't really seen discussed elsewhere - on top of these, there are some open questions I've been mulling over in my head for some time. If you read this post and have some thoughts on potential solutions to these questions, please chime in!&lt;/p&gt;

&lt;h1&gt;
  
  
  Observation: Multitasking is more alluring
&lt;/h1&gt;

&lt;p&gt;This is something I just noticed, since I recently visited the home office in Santa Monica - I find that it's way more tempting to try to multitask, which in my opinion is a killer of both productivity and quality of work. Let me provide a more concrete example - let's say your teammate has a question for you about how something works. If you're in an office, they might drop by your desk, or they might catch you on your way to the kitchen, or they might Slack you and ask if you could drop by their desk. Whether it's at your desk, their desk, or chatting in the kitchen, they typically have your undivided attention - even if you see notifications coming in, social conventions keep your focus on the person you're helping.&lt;/p&gt;

&lt;p&gt;In contrast, if I see a request for help in Slack, I can be chatting with that person, but I can also flip to another window to make a change to some code so that can run in the background for a bit and I can answer another question in another channel and I can check out the latest post in the gaming channel and oh I'll just run upstairs and start another pot of coffee brewing and...&lt;/p&gt;

&lt;p&gt;You see where I'm going with this? It's &lt;em&gt;way&lt;/em&gt; too easy to multitask here!&lt;/p&gt;

&lt;p&gt;What do we do about this? I'm a compulsive "I need to acknowledge unread badges" kind of person, so I wish Slack had a "narrowing" feature (akin to Emacs' &lt;a href="https://www.emacswiki.org/emacs/Narrowing"&gt;narrowing&lt;/a&gt; feature) so that I could more easily focus on one conversation. Alternatively, I'd like to get into more of a habit of starting up a video chat when helping someone - I think the nature of video chat would facilitate faster communication and enforce some of those social conventions I mentioned above.&lt;/p&gt;

&lt;h1&gt;
  
  
  Strategy: Show up five minutes early for video meetings
&lt;/h1&gt;

&lt;p&gt;If you're going to a meeting in an office, sometimes you'll wrap up what you're doing five minutes before the meeting to grab a cup of coffee or something, and maybe you'll walk with a coworker with the same agenda and you'll chat along the way. And naturally some casual conversation will happen among the first few arrivals at the room while they wait for everyone else to show up. I've been thinking about employing a strategy of explicitly showing up on the video call a few minutes early to encourage this kind of informal chat and just do a little catching up with my teammates.&lt;/p&gt;

&lt;h1&gt;
  
  
  Strategy: Change your Slack profile picture every day
&lt;/h1&gt;

&lt;p&gt;A few months ago, &lt;a href="https://blog.plover.com/"&gt;Mark Dominus&lt;/a&gt; started to change his Slack picture every day and mentioned it at our company's fall Tech Week. I thought this was a cool idea, so I started doing it myself, and I eventually automated most of it (the script is a abominable pile of Python that's currently &lt;em&gt;not&lt;/em&gt; broken, but I'd be happy to clean it up and share it if enough people are interested!). This has served as a conversation starter among people at work, and humanizes people who are sometimes just a screen name in Slack. My coworker Jeremy even started a dedicated channel at work - &lt;code&gt;#ilooklikethistoday&lt;/code&gt; - for more of a "river of profile changes" feel, which has been a real hit!&lt;/p&gt;

&lt;h1&gt;
  
  
  Strategy: Treat yourself if the home office is getting treated
&lt;/h1&gt;

&lt;p&gt;Now, this tip doesn't really apply during the COVID-19 crisis, but I think it's good to keep in mind. Occasionally, the home office will have an all hands meeting, and we remotes tune in via Zoom. The locals often get something like donuts provided, and sadly those don't hold up over Ethernet. So, rather than feel jealous, I've taken to picking up donuts for my family and enjoying them during the all hands meeting. I also post a picture on Slack - it's another conversation starter!&lt;/p&gt;

&lt;h1&gt;
  
  
  Strategy: Adjust your start/end time
&lt;/h1&gt;

&lt;p&gt;Now, this one only applies if your home office is in a different time zone, the time difference is small enough that such an adjustment makes sense, and if your employer is comfortable with more flexible hours (although chances are they'd &lt;em&gt;prefer&lt;/em&gt; this!), but I personally found it harder to work my typical 9-5 in Central Time with Californa being two hours behind. I adjusted my schedule to something more like 10-6, which helped a ton! Well, until recently - my kiddo has been more demanding and more adventurous lately, so my wife often needs my help earlier than 6, so I'm working on adjusting my schedule to help my family more.&lt;/p&gt;

&lt;h1&gt;
  
  
  Strategy: During video meetings, sometimes you need to be a little rude
&lt;/h1&gt;

&lt;p&gt;I hate being rude - I'm not saying I never am, but I try not to be and when I realize I &lt;em&gt;may&lt;/em&gt; have been rude in retrospect, I agonize over it. That being said, it's often hard to get a word in edgewise when you're in a video meeting with some participants in a meeting room at an office, and some participants on camera. For one, you don't have your body language to help you - but the amount of time we allocate for pauses in conversation is surprisingly short, as is mentioned in an &lt;a href="https://lingthusiasm.com/post/177316898321/transcript-lingthusiasm-episode-23-when-nothing"&gt;episode&lt;/a&gt; of one of my favorite podcasts, Lingthusiasm:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There’s actually so little pausing in some interactional – in many interactional contexts – that it is too short for the brain to actually, really have prepared for it without doing some clever anticipation about how that person’s speech goes, and how interaction goes. So, the average time between me saying something and someone replying is, like, 200 milliseconds, which is, like, the speed of a blink.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;200 milliseconds is &lt;em&gt;so&lt;/em&gt; short, and with the extra latency introduced by talking over the Internet, you're at an even further disadvantage. I was talking with my boss about this, and he said "sometimes you have to be a little rude", which I've tried to take to heart. So now I try to butt in a little bit, even if the first word out of my mouth during the interjection is just "Sorry!".&lt;/p&gt;

&lt;h1&gt;
  
  
  Question: Keeping work at work (time)
&lt;/h1&gt;

&lt;p&gt;I have a bad habit - sometimes I'll work my regular hours, and then at the end of my day I'll leave my work laptop on so I can "check a few things" or "finish one more thing" or "make a small tweak to my program" after dinner and chores. ZipRecruiter certainly doesn't expect this of me, and the company is very clear on the importance of work/life balance. This is something I struggle with personally, and I would appreciate any tips about how to combat this! It might be as simple as "close your laptop at the end of your day - no exceptions"!&lt;/p&gt;

&lt;h1&gt;
  
  
  Question: Maintaining social connections and absorbing insights while remote
&lt;/h1&gt;

&lt;p&gt;This one is hard - I don't know if there's going to be an answer for this. I really like going out to Santa Monica to visit - every time I get a chance to catch up with coworkers, and chat about cool random things. And missing out on that sucks - when Brian Fitzpatrick left Google, it came up again a few days later on Slack, and I wondered aloud what ever happened with his Perkeep project. Someone replied "yeah, we were talking about that on Friday", and it bummed me out to not have been part of that conversation. Obviously I don't expect my coworkers to hold every in-person conversation on Slack, nor do I expect them to invite me to a Hangout so I can dial in to informal office chat, but I'd really like to see if there's some sort of middle ground I can access as a remote.&lt;/p&gt;

&lt;p&gt;Well, I hope you found that helpful - and if you have any feedback on my thoughts on the matter, or if you have ideas on how to resolve some of the questions I'm pondering, feel free to reach out!&lt;/p&gt;

</description>
      <category>remote</category>
    </item>
    <item>
      <title>Using SQLite's fsdir to effect a quick search index</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Tue, 25 Feb 2020 14:08:02 +0000</pubDate>
      <link>https://dev.to/hoelzro/using-sqlite-s-fsdir-to-effect-a-quick-search-index-823</link>
      <guid>https://dev.to/hoelzro/using-sqlite-s-fsdir-to-effect-a-quick-search-index-823</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://hoelz.ro/blog/using-sqlites-fsdir-to-effect-a-quick-search-index"&gt;https://hoelz.ro/blog/using-sqlites-fsdir-to-effect-a-quick-search-index&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://www.ziprecruiter.com/hiring/technology"&gt;work&lt;/a&gt;, we have a wiki we use for knowledge sharing.  It's implemented on top of &lt;a href="https://github.com/jgm/gitit"&gt;gitit&lt;/a&gt;, which works pretty well for our purposes, but sometimes I find the search functionality lacking.  What I'd like is something that has more advanced full text search-like features - things like stemming (so that searching for "databases" finds documents that mention "database") and ranking by query relevance (so that documents that mention "database" many times are ranked higher than those that just mention "database" once).&lt;/p&gt;

&lt;p&gt;Well, SQLite happens to have two pieces of functionality that can help in this regard: the &lt;a href="https://www.sqlite.org/fts5.html"&gt;FTS extension&lt;/a&gt;, which implements those features I just mentioned, and the &lt;a href="https://sqlite.org/src/file/ext/misc/fileio.c"&gt;fsdir table-valued function&lt;/a&gt;, which we can use to treat the filesystem like a table.  Since gitit stores its pages in a Git repository, all we need to do is clone the repository and run a few SQL snippets to build our search index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone work:wiki
&lt;span class="nv"&gt;$ &lt;/span&gt;sqlite3 wiki-search.db &lt;span class="s2"&gt;"create virtual table page_search using fts5 (path, page, tokenize='porter unicode61')"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sqlite3 wiki-search.db &lt;span class="s2"&gt;"insert into page_search select name, data from fsdir('wiki')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can search the wiki and benefit from those features: &lt;code&gt;select path, snippet(page_search, 1, '', '', '...', 30) from page_search where page_search match 'my query' order by rank&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Granted, it would be nice to do things like extract the front matter for each page, along with things like edit dates, but this works well enough.&lt;/p&gt;

</description>
      <category>sqlite</category>
      <category>search</category>
    </item>
    <item>
      <title>Unsung Heroes of the Command Line</title>
      <dc:creator>Rob Hoelz</dc:creator>
      <pubDate>Thu, 03 Jan 2019 20:16:09 +0000</pubDate>
      <link>https://dev.to/hoelzro/unsung-heroes-of-the-command-line-3oal</link>
      <guid>https://dev.to/hoelzro/unsung-heroes-of-the-command-line-3oal</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://hoelz.ro/blog/unsung-heroes-of-the-command-line"&gt;https://hoelz.ro/blog/unsung-heroes-of-the-command-line&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since the command line is my primary way of interacting with my computer, I often will take steps to optimize my usage of it.  Most command line users know the basics: &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, etc - these are instrumental building blocks in using the command line.  However, there are often more specialized programs out there that can really save you some time, and I'd like to share a few notable examples of these kinds of specialized programs that have made my life easier over the last year or two.  If you don't know them, hopefully you'll find them useful; if you &lt;em&gt;do&lt;/em&gt; know them, hopefully it will just indicate that we both have good taste in our tools. =)&lt;/p&gt;

&lt;h1&gt;
  
  
  mojo
&lt;/h1&gt;

&lt;p&gt;Over the past few years, I find myself doing more and more work with data online.  Calling out to various web services or fetching content from a URL has become an essential part of being a developer.  When it comes down to simply fetching the data, I reach for &lt;code&gt;curl&lt;/code&gt;.  However, if I have to do some slicing and dicing, I look to my good friend &lt;code&gt;mojo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mojo&lt;/code&gt; is a command line utility shipped with the &lt;a href="http://mojolicious.org/"&gt;Mojolicious&lt;/a&gt; project - it's a kind of web toolkit for Perl programmers.  You don't need to be skilled in Perl to make use of &lt;code&gt;mojo&lt;/code&gt;, however.  &lt;code&gt;mojo get&lt;/code&gt; allows you to fetch the contents at a URL, select particular elements via CSS3 selectors, and then transform the result using various methods.  For example, let's say I want to get the &lt;code&gt;src&lt;/code&gt; attribute of each &lt;code&gt;img&lt;/code&gt; element on a page.  This is how I could invoke &lt;code&gt;mojo get&lt;/code&gt; to get the job done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ mojo get http://example.com img attr src
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;img&lt;/code&gt; part is of course the element selector; the arguments following it are a method name (any method in &lt;a href="https://metacpan.org/pod/Mojo::DOM"&gt;Mojo::DOM&lt;/a&gt; may be used) plus any arguments for the method.  I've found &lt;code&gt;mojo get&lt;/code&gt; to be invaluable in scraping a document and extracting some content from it for use further down the command line.&lt;/p&gt;

&lt;h1&gt;
  
  
  jq
&lt;/h1&gt;

&lt;p&gt;Continuing along the online data trend, I find myself working with JSON pretty often too. Instead of writing a script to extract data from a JSON, you can use &lt;a href="https://stedolan.github.io/jq/"&gt;jq&lt;/a&gt;.  It's a handy program that describes itself as "sed for JSON data", and I feel like it lives up to this idea.  Just invoking the &lt;code&gt;.&lt;/code&gt; operator (which selects the current object) will pretty-print a chunk of JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ cat chunk-of.json | jq .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can select a particular key in a document by adding it after the &lt;code&gt;.&lt;/code&gt; operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ cat chunk-of.json | jq .result
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;jq's&lt;/code&gt; selector syntax is pretty easy to understand and to recall - I can reliably remember how to do the basics each time I use it. For other instances, the documentation is always available for consultation!&lt;/p&gt;

&lt;h1&gt;
  
  
  uniprops &amp;amp; unichars
&lt;/h1&gt;

&lt;p&gt;I try to write software to be Unicode-aware, which often means something like checking for the &lt;code&gt;Letter&lt;/code&gt; property rather than simply looking for &lt;code&gt;a-z&lt;/code&gt; and &lt;code&gt;A-Z&lt;/code&gt;.  To help me in this effort, I make use of two scripts from the &lt;a href="https://metacpan.org/pod/Unicode::Tussle"&gt;Unicode::Tussle&lt;/a&gt; CPAN distribution: &lt;code&gt;uniprops&lt;/code&gt; and &lt;code&gt;unichars&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;uniprops&lt;/code&gt;, you can give a character or codepoint, and it will tell you about all of the Unicode properties that character exhibits.  For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ uniprops а
    U+0430 ‹а› \N{CYRILLIC SMALL LETTER A}
        \w \pL \p{LC} \p{L_} \p{L&amp;amp;} \p{Ll}
        All Alnum X_POSIX_Alnum Alpha X_POSIX_Alpha Alphabetic Any Assigned
           InCyrillic Cyrillic Is_Cyrillic ID_Continue Is_IDC Cased Cased_Letter LC
           Changes_When_Casemapped CWCM Changes_When_Titlecased CWT
           Changes_When_Uppercased CWU Cyrl Ll L Gr_Base Grapheme_Base Graph
           X_POSIX_Graph GrBase IDC ID_Start IDS Letter L_ Lowercase_Letter Lower
           X_POSIX_Lower Lowercase Print X_POSIX_Print Unicode Word X_POSIX_Word
           XID_Continue XIDC XID_Start XIDS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;unichars&lt;/code&gt; approaches the property problem from the opposite angle; instead of telling you which properties a character has, it gives you a list of which Unicode characters have a particular property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $ unichars '\p{Cyrillic}
     Ѐ  U+0400 CYRILLIC CAPITAL LETTER IE WITH GRAVE
     Ё  U+0401 CYRILLIC CAPITAL LETTER IO
     Ђ  U+0402 CYRILLIC CAPITAL LETTER DJE
     Ѓ  U+0403 CYRILLIC CAPITAL LETTER GJE
     Є  U+0404 CYRILLIC CAPITAL LETTER UKRAINIAN IE
     Ѕ  U+0405 CYRILLIC CAPITAL LETTER DZE
     І  U+0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
     Ї  U+0407 CYRILLIC CAPITAL LETTER YI
     Ј  U+0408 CYRILLIC CAPITAL LETTER JE
     Љ  U+0409 CYRILLIC CAPITAL LETTER LJE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  combine
&lt;/h1&gt;

&lt;p&gt;If you've done text processing on the command line, there's a good chance you're familiar with &lt;code&gt;comm(1)&lt;/code&gt;.  It's a program that you give two text files along with an option switch that indicates different set operations to perform on the lines in those files.  For example, &lt;code&gt;comm -13 A B&lt;/code&gt; prints lines unique to B, and &lt;code&gt;comm -12 A B&lt;/code&gt; prints lines in both A and B.  From my examples, you get a good idea of how difficult &lt;code&gt;comm&lt;/code&gt; can be to use; I would always have to consult the man page or sit and think the invocation I would need to make to get the results I needed.  Frustrated, I cried out to the Internet:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--3y0D17HA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/657312556336308224/dnotbjAW_normal.jpg" alt="[ɹɑb hɛlt͡s] profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        [ɹɑb hɛlt͡s]
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/hoelzro"&gt;@hoelzro&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      comm(1) has the weirdest way of specifying what you want, but it's so useful =/
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      16:37 PM - 30 Dec 2015
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=682239111978192897" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=682239111978192897" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      1
      &lt;a href="https://twitter.com/intent/like?tweet_id=682239111978192897" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;Fortunately, a fellow programmer heard my plea:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--8VOqm4ip--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/723545063/vnaum-20071207-212657_normal.jpg" alt="Vladislav Naumov profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Vladislav Naumov
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @vnaum
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://twitter.com/hoelzro"&gt;@hoelzro&lt;/a&gt; COMBINE(1) is way more straightforward if you want to compare sets of lines.&lt;br&gt;apt-get install moreutils
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      17:02 PM - 30 Dec 2015
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=682245404579250176" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=682245404579250176" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      1
      &lt;a href="https://twitter.com/intent/like?tweet_id=682245404579250176" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;Ever since that fateful day, I have not used &lt;code&gt;comm&lt;/code&gt; once.  &lt;code&gt;combine&lt;/code&gt; does exactly what I need, and does so using a syntax that's so straightforward.  My examples from above change to &lt;code&gt;combine B not A&lt;/code&gt; and &lt;code&gt;combine A and B&lt;/code&gt;, respectively.&lt;/p&gt;

&lt;h1&gt;
  
  
  Discovering More Tools
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;combine&lt;/code&gt; is part of the moreutils package, available on many platforms; I recommend looking at other tools in the package to see if you find something else that solves a problem you have, or better yet, a problem you didn't &lt;em&gt;know&lt;/em&gt; you had!&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;jq&lt;/code&gt; apppeals to you, but you work with other kinds of structured data, you may find this repository on GitHub interesting:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dbohdan/structured-text-tools"&gt;https://github.com/dbohdan/structured-text-tools&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Are you there any specialized tools you feel make your life a lot easier?  If so, please let me know!&lt;/p&gt;

</description>
      <category>commandline</category>
      <category>cli</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
