<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>music &amp;mdash; musicmatzes blog</title>
    <link>https://beyermatthias.de/tag:music</link>
    <description></description>
    <pubDate>Wed, 06 May 2026 11:46:18 +0200</pubDate>
    <item>
      <title>Writing a Prometheus MPD exporter</title>
      <link>https://beyermatthias.de/writing-a-prometheus-mpd-exporter</link>
      <description>&lt;![CDATA[Today, I challenged myself to write a prometheus exporter for&#xA;MPD&#xA;in Rust.&#xA;&#xA;  Shut up and show me the code!&#xA;&#xA;Here you go&#xA;and&#xA;here you go for submitting patches.&#xA;&#xA;The challenge&#xA;&#xA;I recently started monitoring my server with prometheus and grafana.&#xA;I am no-way a professional user of these pieces of software, but I slowly got&#xA;everything up and running.&#xA;I learned about timeseries databases at university, so the basic concept of&#xA;prometheus was not new to me.&#xA;Grafana was, though.&#xA;I then started learning about prometheus exporters and how they are working and&#xA;managed to setup node exporters for all my devices and imported their metrics&#xA;into a nice grafana dashboard I downloaded from the official website.&#xA;&#xA;I figured, that writing an exporter would make me understand the whole thing&#xA;even better.&#xA;So what would be better than exporting music data to my prometheus and plotting&#xA;it with grafana?&#xA;Especially because my nickname online is &#34;musicmatze&#34;, right?&#xA;&#xA;So I started writing a prometheus exporter for MPD.&#xA;And because my language of choice is Rust, I wrote it in Rust.&#xA;Rust has good libraries available for everything I needed to do to export basic&#xA;MPD metrics to prometheus and even a prometheus exporter library exists!&#xA;&#xA;The libraries I decided to use&#xA;&#xA;Note that this article was written using&#xA;prometheus-mpd-exporter v0.1.0&#xA;of the prometheus-mpd-exporter code.&#xA;The current codebase might differ, but this was the first working&#xA;implementation.&#xA;&#xA;So, the scope of my idea was set.&#xA;Of course, I needed a library to talk to my music player daemon.&#xA;And because async libraries would be better, since I would essentially write a&#xA;kind of a web-server, it should be async.&#xA;Thankfully, asyncmpd exists.&#xA;&#xA;Next, I needed a&#xA;prometheus helper library.&#xA;The examples in this library work with hyper.&#xA;I was not able to implement my idea with hyper though (because of some weird&#xA;borrowing error), but thankfully, actix-web worked just&#xA;fine.&#xA;&#xA;Besides that I used a bunch of convenience libraries:&#xA;&#xA;anyhow and thiserror for error handling&#xA;envlogger and log for logging&#xA;structopt for CLI parsing&#xA;getset, parse-display and itertools to be able to write less code&#xA;&#xA;The first implementation&#xA;&#xA;The first implementation took me about four hours to write, because I had to&#xA;understand the actix-web infrastructure first (and because I tried it with&#xA;hyper in the first place, which did not work for about three of that four&#xA;hours).&#xA;&#xA;The boilerplate of the program includes&#xA;&#xA;Defining an ApplicationError type for easy passing-around of errors that&#xA;  happen during the runtime of the program&#xA;Defining an Opt as a commandline interface definition using structopt&#xA;&#xA;[actixweb::main]&#xA;async fn main() -  Result(), ApplicationError {&#xA;    let  = envlogger::init();&#xA;    log::info!(&#34;Starting...&#34;);&#xA;    let opt = Opt::fromargs();&#xA;&#xA;    let prometheusbindaddr = format!(&#34;{}:{}&#34;, opt.bindaddr, opt.bindport);&#xA;    let mpdconnectstring = format!(&#34;{}:{}&#34;, opt.mpdserveraddr, opt.mpdserverport);&#xA;&#xA;The main() function then sets up the logging and parses the commandline&#xA;arguments. Thanks to envlogger and structopt, that&#39;s easy.&#xA;The main() function also acts as the actixweb::main function and is async&#xA;because of that.&#xA;It also returns a Result(), ApplicationError, so I can easily fail during&#xA;the setup phase of the program.&#xA;&#xA;Next, I needed to setup the connection to MPD and wrap that in a Mutex, so it&#xA;can be shared between request handlers.&#xA;&#xA;    log::debug!(&#34;Connecting to MPD = {}&#34;, mpdconnectstring);&#xA;    let mpd = asyncmpd::MpdClient::new(&amp;*mpdconnectstring)&#xA;        .await&#xA;        .map(Mutex::new)?;&#xA;&#xA;    let mpd = web::Data::new(mpd);&#xA;&#xA;And then setup the HttpServer instance for actix-web, and run it.&#xA;&#xA;    HttpServer::new(move || {&#xA;        App::new()&#xA;            .appdata(mpd.clone()) // add shared state&#xA;            .wrap(middleware::Logger::default())&#xA;            .route(&#34;/&#34;, web::get().to(index))&#xA;            .route(&#34;/metrics&#34;, web::get().to(metrics))&#xA;    })&#xA;    .bind(prometheusbindaddr)?&#xA;    .run()&#xA;    .await&#xA;    .maperr(ApplicationError::from)&#xA;} // end of main()&#xA;&#xA;Now comes the fun part, tho.&#xA;First of all, I have setup the connection to MPD.&#xA;In the above snippet, I add routes to the HttpServer for a basic index endpoint&#xA;as well as for the /metrics endpoint prometheus fetches the metrics from.&#xA;&#xA;Lets have a look at the index handler first, to get a basic understanding of&#xA;how it works:&#xA;&#xA;async fn index(: web::DataMutex&lt;MpdClient  , : HttpRequest) -  impl Responder {&#xA;    HttpResponse::build(StatusCode::OK)&#xA;        .contenttype(&#34;text/text; charset=utf-8&#34;)&#xA;        .body(String::from(&#34;Running&#34;))&#xA;}&#xA;&#xA;This function gets called every time someone accesses the service without&#xA;specifying an endpoint, for example curl localhost:9123 would result in this&#xA;function being called.&#xA;&#xA;Here, I can get the web::DataMutex&lt;MpdClient   object instance that&#xA;actix-web handles for us as well as a HttpRequest object to get information&#xA;about the request itself.&#xA;Because I don&#39;t need this data here, the variables are not bound (``).&#xA;I added them to be able to extend this function later on easily.&#xA;&#xA;I return a simple 200 (that&#39;s the StatusCode::OK here) with a simple&#xA;Running body.&#xA;curling would result in a simple response:&#xA;&#xA;$ curl 127.0.0.1:9123&#xA;Running&#xA;&#xA;Now, lets have a look at the /metrics endpoint.&#xA;First of all, the signature of the function is the same:&#xA;&#xA;async fn metrics(mpddata: web::DataMutex&lt;MpdClient  , : HttpRequest) -  impl Responder {&#xA;    match metricshandler(mpddata).await {&#xA;        Ok(text) =  {&#xA;            HttpResponse::build(StatusCode::OK)&#xA;                .contenttype(&#34;text/text; charset=utf-8&#34;)&#xA;                .body(text)&#xA;        }&#xA;&#xA;        Err(e) =  {&#xA;            HttpResponse::build(StatusCode::INTERNALSERVERERROR)&#xA;                .contenttype(&#34;text/text; charset=utf-8&#34;)&#xA;                .body(format!(&#34;{}&#34;, e))&#xA;        }&#xA;    }&#xA;}&#xA;&#xA;but here, we bind the mpd client object to mpddata, because we want to&#xA;actually use that object.&#xA;We then call a function metricshandler() with that object, wait for the&#xA;result (because that function itself is async, too), and match the result.&#xA;If the result is Ok(), we get the result text and return a 200 with the&#xA;text as the body.&#xA;If the result is an error, which means that fetching the data from MPD somehow&#xA;resulted in an error, we return an internal server error (500) and the error&#xA;message as body of the response.&#xA;&#xA;Now, to the metricshandler() function, which is where the real work happens.&#xA;&#xA;async fn metricshandler(mpddata: web::DataMutex&lt;MpdClient  ) -  ResultString, ApplicationError {&#xA;    let mut mpd = mpddata.lock().unwrap();&#xA;    let stats = mpd.stats().await?;&#xA;&#xA;    let instance = String::new(); // TODO&#xA;&#xA;First of all, we extract the actual MpdClient object from the&#xA;web::DataMutex&lt;   wrapper.&#xA;Them, we ask MPD to get some stats() and wait for the result.&#xA;&#xA;After that, we create a variable we don&#39;t fill yet, which we later push in the&#xA;release without solving the &#34;TODO&#34; marker and when we blog about what we did, we&#xA;feel ashamed about it.&#xA;&#xA;Next, we create Metric objects for each metric we record from MPD and render&#xA;all of them into one big String object.&#xA;&#xA;    let res = vec![&#xA;        Metric::new(&#34;mpduptime&#34;      , stats.uptime      , &#34;The uptime of mpd&#34;, &amp;instance).intometric()?,&#xA;        Metric::new(&#34;mpdplaytime&#34;    , stats.playtime    , &#34;The playtime of the current playlist&#34;, &amp;instance).intometric()?,&#xA;        Metric::new(&#34;mpdartists&#34;     , stats.artists     , &#34;The number of artists&#34;, &amp;instance).intometric()?,&#xA;        Metric::new(&#34;mpdalbums&#34;      , stats.albums      , &#34;The number of albums&#34;, &amp;instance).intometric()?,&#xA;        Metric::new(&#34;mpdsongs&#34;       , stats.songs       , &#34;The number of songs&#34;, &amp;instance).intometric()?,&#xA;        Metric::new(&#34;mpddbplaytime&#34; , stats.dbplaytime , &#34;The database playtime&#34;, &amp;instance).intometric()?,&#xA;        Metric::new(&#34;mpddbupdate&#34;   , stats.dbupdate   , &#34;The updates of the database&#34;, &amp;instance).intometric()?,&#xA;    ]&#xA;    .intoiter()&#xA;    .map(|m| {&#xA;        m.render()&#xA;    })&#xA;    .join(&#34;\n&#34;);&#xA;&#xA;    log::debug!(&#34;res = {}&#34;, res);&#xA;    Ok(res)&#xA;}&#xA;&#xA;Lastly, we return that String object from our handler implementation.&#xA;&#xA;The Metric object implementation my own, we&#39;ll focus on that now.&#xA;It will help a bit with the interface of the prometheusexporterbase API&#xA;interface.&#xA;&#xA;But first, I need to explain the Metric type:&#xA;&#xA;pub struct Metric&#39;a, T: IntoNumMetric {&#xA;    name: &amp;&#39;static str,&#xA;    value: T,&#xA;    description: &amp;&#39;static str,&#xA;    instance: &amp;&#39;a str,&#xA;}&#xA;&#xA;The Metric type is a type that holds a name for a metric, its value and some&#xA;description (and the aforementioned irrelevant instance).&#xA;But because the metrics we collect can be of different types (for example a&#xA;8-bit unsigned integer u8 or a 32-bit unsigned integer u32), I made that&#xA;type abstract over it.&#xA;The type of the metric value must implement a IntoNumMetric trait, though.&#xA;That trait is a simple helper trait:&#xA;&#xA;use numtraits::Num;&#xA;pub trait IntoNumMetric {&#xA;    type Output: Num + Display + Debug;&#xA;&#xA;    fn intonummetric(self) -  Self::Output;&#xA;}&#xA;&#xA;And I implemented it for std::time::Duration, u8, u32 and i32 - the&#xA;implementation itself is trivial and I won&#39;t show it here.&#xA;&#xA;Now, I was able to implement the Metric::intometric() function shown above:&#xA;&#xA;impl&#39;a, T: IntoNumMetric + Debug Metric&#39;a, T {&#xA;    // Metric::new() implementation, hidden here&#xA;&#xA;    pub fn intometric&#39;b(self) -  ResultPrometheusMetric&lt;&#39;b  {&#xA;        let instance = PrometheusInstance::new()&#xA;            .withlabel(&#34;instance&#34;, self.instance)&#xA;            .withvalue(self.value.intonummetric())&#xA;            .withcurrenttimestamp()&#xA;            .maperr(Error::from)?;&#xA;&#xA;        let mut m = PrometheusMetric::new(self.name, MetricType::Counter, self.description);&#xA;        m.renderandappendinstance(&amp;instance);&#xA;        Ok(m)&#xA;    }&#xA;}&#xA;&#xA;This function is used for converting a Metric object into the appropriate&#xA;PrometheusMetric object from prometheusexporterbase.&#xA;&#xA;The implementation is, of course, also generic over the type the Metric object&#xA;holds.&#xA;A PrometheusInstance is created, a label &#34;instance&#34; is added (empty, you know&#xA;why... :-( ).&#xA;Then, the value is added to that instance using the conversion from the&#xA;IntoNumMetric trait.&#xA;The current timestamp is added as well, or an error is returned if that fails.&#xA;&#xA;Last but not least, a new PrometheusMetric object is created with the&#xA;appropriate name and description, and the instance is rendered to it.&#xA;&#xA;And that&#39;s it!&#xA;&#xA;Deploying&#xA;&#xA;The code is there now.&#xA;But of course, I still needed to deploy this to my hosts and make it available&#xA;in my prometheus and grafana instances.&#xA;&#xA;Because I use NixOS, I wrote a nix package definition and a&#xA;nix service defintion for it,&#xA;making the endpoint available to my prometheus instance via my wireguard&#xA;network.&#xA;&#xA;After that, I was able to add queries to my grafana instance, for example:&#xA;&#xA;mpddbplaytime / 60 / 60 / 24&#xA;&#xA;to display the DB playtime of an instance of my MPD in days.&#xA;&#xA;I&#39;m not yet very proficient in grafana and the query language, and also the&#xA;service implementation is rather minimal, so there cannot be that much metrics&#xA;yet.&#xA;&#xA;Either way, it works!&#xA;&#xA;A basic dashboard for MPD stats&#xA;&#xA;Next steps and closing words&#xA;&#xA;The next steps are quite simple.&#xA;First of all, I want to make more stats available to prometheus. Right now, only&#xA;the basic statistics of the database are exported.&#xA;&#xA;The asyncmpd crate makes a lot of&#xA;other status information&#xA;available.&#xA;&#xA;Also, I want to get better with grafana queries and make some nice-looking&#xA;graphs for my dashboard.&#xA;&#xA;Either way, that challenge took me longer than I anticipated in the first place&#xA;(&#34;I can hack this in 15 minutes&#34; - famous last words)!&#xA;But it was fun nonetheless!&#xA;&#xA;The outcome of this little journey is on&#xA;crates.io&#xA;and I will also submit a PR to nixpkgs to make it available there, too.&#xA;&#xA;If you want to contribute patches to the sourcecode, which I encourage you to&#xA;do, feel free to send me patches!&#xA;&#xA;tags: #prometheus #grafana #rust #mpd #music&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Today, I challenged myself to write a prometheus exporter for
<a href="https://www.musicpd.org/">MPD</a>
in Rust.</p>

<blockquote><p>Shut up and show me the code!</p></blockquote>

<p><a href="https://git.beyermatthi.as/prometheus-mpd-exporter/">Here you go</a>
and
<a href="https://sr.ht/~matthiasbeyer/prometheus-mpd-exporter/">here you go for submitting patches</a>.</p>

<h1 id="the-challenge" id="the-challenge">The challenge</h1>

<p>I recently started monitoring my server with prometheus and grafana.
I am no-way a professional user of these pieces of software, but I slowly got
everything up and running.
I learned about timeseries databases at university, so the basic concept of
prometheus was not new to me.
Grafana was, though.
I then started learning about prometheus exporters and how they are working and
managed to setup node exporters for all my devices and imported their metrics
into a nice grafana dashboard I downloaded from the official website.</p>

<p>I figured, that writing an exporter would make me understand the whole thing
even better.
So what would be better than exporting music data to my prometheus and plotting
it with grafana?
Especially because my nickname online is “musicmatze”, right?</p>

<p>So I started writing a prometheus exporter for MPD.
And because my language of choice is Rust, I wrote it in Rust.
Rust has good libraries available for everything I needed to do to export basic
MPD metrics to prometheus and even a prometheus exporter library exists!</p>

<h1 id="the-libraries-i-decided-to-use" id="the-libraries-i-decided-to-use">The libraries I decided to use</h1>

<p>Note that this article was written using
<a href="https://git.beyermatthi.as/prometheus-mpd-exporter/tag/?h=v0.1.0">prometheus-mpd-exporter v0.1.0</a>
of the prometheus-mpd-exporter code.
The current codebase might differ, but this was the first working
implementation.</p>

<p>So, the scope of my idea was set.
Of course, I needed a library to talk to my music player daemon.
And because async libraries would be better, since I would essentially write a
kind of a web-server, it should be async.
Thankfully, <a href="https://docs.rs/async-mpd/">async_mpd</a> exists.</p>

<p>Next, I needed a
<a href="https://github.com/MindFlavor/prometheus_exporter_base">prometheus helper library</a>.
The examples in this library work with hyper.
I was not able to implement my idea with hyper though (because of some weird
borrowing error), but thankfully, <a href="https://actix.rs/">actix-web</a> worked just
fine.</p>

<p>Besides that I used a bunch of convenience libraries:</p>
<ul><li><code>anyhow</code> and <code>thiserror</code> for error handling</li>
<li><code>env_logger</code> and <code>log</code> for logging</li>
<li><code>structopt</code> for CLI parsing</li>
<li><code>getset</code>, <code>parse-display</code> and <code>itertools</code> to be able to write less code</li></ul>

<h1 id="the-first-implementation" id="the-first-implementation">The first implementation</h1>

<p>The first implementation took me about four hours to write, because I had to
understand the <code>actix-web</code> infrastructure first (and because I tried it with
<code>hyper</code> in the first place, which did not work for about three of that four
hours).</p>

<p>The boilerplate of the program includes</p>
<ul><li>Defining an <code>ApplicationError</code> type for easy passing-around of errors that
happen during the runtime of the program</li>
<li>Defining an <code>Opt</code> as a commandline interface definition using <code>structopt</code></li></ul>

<pre><code class="language-rust">#[actix_web::main]
async fn main() -&gt; Result&lt;(), ApplicationError&gt; {
    let _ = env_logger::init();
    log::info!(&#34;Starting...&#34;);
    let opt = Opt::from_args();

    let prometheus_bind_addr = format!(&#34;{}:{}&#34;, opt.bind_addr, opt.bind_port);
    let mpd_connect_string = format!(&#34;{}:{}&#34;, opt.mpd_server_addr, opt.mpd_server_port);
</code></pre>

<p>The <code>main()</code> function then sets up the logging and parses the commandline
arguments. Thanks to <code>env_logger</code> and <code>structopt</code>, that&#39;s easy.
The <code>main()</code> function also acts as the <code>actix_web::main</code> function and is <code>async</code>
because of that.
It also returns a <code>Result&lt;(), ApplicationError&gt;</code>, so I can easily fail during
the setup phase of the program.</p>

<p>Next, I needed to setup the connection to MPD and wrap that in a Mutex, so it
can be shared between request handlers.</p>

<pre><code class="language-rust">    log::debug!(&#34;Connecting to MPD = {}&#34;, mpd_connect_string);
    let mpd = async_mpd::MpdClient::new(&amp;*mpd_connect_string)
        .await
        .map(Mutex::new)?;

    let mpd = web::Data::new(mpd);
</code></pre>

<p>And then setup the <code>HttpServer</code> instance for actix-web, and run it.</p>

<pre><code class="language-rust">    HttpServer::new(move || {
        App::new()
            .app_data(mpd.clone()) // add shared state
            .wrap(middleware::Logger::default())
            .route(&#34;/&#34;, web::get().to(index))
            .route(&#34;/metrics&#34;, web::get().to(metrics))
    })
    .bind(prometheus_bind_addr)?
    .run()
    .await
    .map_err(ApplicationError::from)
} // end of main()
</code></pre>

<p>Now comes the fun part, tho.
First of all, I have setup the connection to MPD.
In the above snippet, I add routes to the HttpServer for a basic index endpoint
as well as for the <code>/metrics</code> endpoint prometheus fetches the metrics from.</p>

<p>Lets have a look at the <code>index</code> handler first, to get a basic understanding of
how it works:</p>

<pre><code class="language-rust">async fn index(_: web::Data&lt;Mutex&lt;MpdClient&gt;&gt;, _: HttpRequest) -&gt; impl Responder {
    HttpResponse::build(StatusCode::OK)
        .content_type(&#34;text/text; charset=utf-8&#34;)
        .body(String::from(&#34;Running&#34;))
}
</code></pre>

<p>This function gets called every time someone accesses the service without
specifying an endpoint, for example <code>curl localhost:9123</code> would result in this
function being called.</p>

<p>Here, I can get the <code>web::Data&lt;Mutex&lt;MpdClient&gt;&gt;</code> object instance that
actix-web handles for us as well as a <code>HttpRequest</code> object to get information
about the request itself.
Because I don&#39;t need this data here, the variables are not bound (<code>_</code>).
I added them to be able to extend this function later on easily.</p>

<p>I return a simple <code>200</code> (that&#39;s the <code>StatusCode::OK</code> here) with a simple
<code>Running</code> body.
<code>curl</code>ing would result in a simple response:</p>

<pre><code>$ curl 127.0.0.1:9123
Running
</code></pre>

<p>Now, lets have a look at the <code>/metrics</code> endpoint.
First of all, the signature of the function is the same:</p>

<pre><code class="language-rust">async fn metrics(mpd_data: web::Data&lt;Mutex&lt;MpdClient&gt;&gt;, _: HttpRequest) -&gt; impl Responder {
    match metrics_handler(mpd_data).await {
        Ok(text) =&gt; {
            HttpResponse::build(StatusCode::OK)
                .content_type(&#34;text/text; charset=utf-8&#34;)
                .body(text)
        }

        Err(e) =&gt; {
            HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR)
                .content_type(&#34;text/text; charset=utf-8&#34;)
                .body(format!(&#34;{}&#34;, e))
        }
    }
}
</code></pre>

<p>but here, we bind the mpd client object to <code>mpd_data</code>, because we want to
actually use that object.
We then call a function <code>metrics_handler()</code> with that object, wait for the
result (because that function itself is <code>async</code>, too), and match the result.
If the result is <code>Ok(_)</code>, we get the result text and return a <code>200</code> with the
text as the body.
If the result is an error, which means that fetching the data from MPD somehow
resulted in an error, we return an internal server error (<code>500</code>) and the error
message as body of the response.</p>

<p>Now, to the <code>metrics_handler()</code> function, which is where the real work happens.</p>

<pre><code class="language-rust">async fn metrics_handler(mpd_data: web::Data&lt;Mutex&lt;MpdClient&gt;&gt;) -&gt; Result&lt;String, ApplicationError&gt; {
    let mut mpd = mpd_data.lock().unwrap();
    let stats = mpd.stats().await?;

    let instance = String::new(); // TODO
</code></pre>

<p>First of all, we extract the actual <code>MpdClient</code> object from the
<code>web::Data&lt;Mutex&lt;_&gt;&gt;</code> wrapper.
Them, we ask MPD to get some <code>stats()</code> and wait for the result.</p>

<p>After that, we create a variable we don&#39;t fill yet, which we later push in the
release without solving the “TODO” marker and when we blog about what we did, we
feel ashamed about it.</p>

<p>Next, we create <code>Metric</code> objects for each metric we record from MPD and render
all of them into one big <code>String</code> object.</p>

<pre><code class="language-rust">    let res = vec![
        Metric::new(&#34;mpd_uptime&#34;      , stats.uptime      , &#34;The uptime of mpd&#34;, &amp;instance).into_metric()?,
        Metric::new(&#34;mpd_playtime&#34;    , stats.playtime    , &#34;The playtime of the current playlist&#34;, &amp;instance).into_metric()?,
        Metric::new(&#34;mpd_artists&#34;     , stats.artists     , &#34;The number of artists&#34;, &amp;instance).into_metric()?,
        Metric::new(&#34;mpd_albums&#34;      , stats.albums      , &#34;The number of albums&#34;, &amp;instance).into_metric()?,
        Metric::new(&#34;mpd_songs&#34;       , stats.songs       , &#34;The number of songs&#34;, &amp;instance).into_metric()?,
        Metric::new(&#34;mpd_db_playtime&#34; , stats.db_playtime , &#34;The database playtime&#34;, &amp;instance).into_metric()?,
        Metric::new(&#34;mpd_db_update&#34;   , stats.db_update   , &#34;The updates of the database&#34;, &amp;instance).into_metric()?,
    ]
    .into_iter()
    .map(|m| {
        m.render()
    })
    .join(&#34;\n&#34;);

    log::debug!(&#34;res = {}&#34;, res);
    Ok(res)
}
</code></pre>

<p>Lastly, we return that <code>String</code> object from our handler implementation.</p>

<p>The <code>Metric</code> object implementation my own, we&#39;ll focus on that now.
It will help a bit with the interface of the <code>prometheus_exporter_base</code> API
interface.</p>

<p>But first, I need to explain the <code>Metric</code> type:</p>

<pre><code class="language-rust">pub struct Metric&lt;&#39;a, T: IntoNumMetric&gt; {
    name: &amp;&#39;static str,
    value: T,
    description: &amp;&#39;static str,
    instance: &amp;&#39;a str,
}
</code></pre>

<p>The <code>Metric</code> type is a type that holds a name for a metric, its value and some
description (and the aforementioned irrelevant <code>instance</code>).
But because the metrics we collect can be of different types (for example a
8-bit unsigned integer <code>u8</code> or a 32-bit unsigned integer <code>u32</code>), I made that
type abstract over it.
The type of the metric value must implement a <code>IntoNumMetric</code> trait, though.
That trait is a simple helper trait:</p>

<pre><code class="language-rust">use num_traits::Num;
pub trait IntoNumMetric {
    type Output: Num + Display + Debug;

    fn into_num_metric(self) -&gt; Self::Output;
}
</code></pre>

<p>And I implemented it for <code>std::time::Duration</code>, <code>u8</code>, <code>u32</code> and <code>i32</code> – the
implementation itself is trivial and I won&#39;t show it here.</p>

<p>Now, I was able to implement the <code>Metric::into_metric()</code> function shown above:</p>

<pre><code class="language-rust">impl&lt;&#39;a, T: IntoNumMetric + Debug&gt; Metric&lt;&#39;a, T&gt; {
    // Metric::new() implementation, hidden here

    pub fn into_metric&lt;&#39;b&gt;(self) -&gt; Result&lt;PrometheusMetric&lt;&#39;b&gt;&gt; {
        let instance = PrometheusInstance::new()
            .with_label(&#34;instance&#34;, self.instance)
            .with_value(self.value.into_num_metric())
            .with_current_timestamp()
            .map_err(Error::from)?;

        let mut m = PrometheusMetric::new(self.name, MetricType::Counter, self.description);
        m.render_and_append_instance(&amp;instance);
        Ok(m)
    }
}
</code></pre>

<p>This function is used for converting a <code>Metric</code> object into the appropriate
<code>PrometheusMetric</code> object from <code>prometheus_exporter_base</code>.</p>

<p>The implementation is, of course, also generic over the type the <code>Metric</code> object
holds.
A <code>PrometheusInstance</code> is created, a label “instance” is added (empty, you know
why... :–( ).
Then, the value is added to that instance using the conversion from the
<code>IntoNumMetric</code> trait.
The current timestamp is added as well, or an error is returned if that fails.</p>

<p>Last but not least, a new <code>PrometheusMetric</code> object is created with the
appropriate name and description, and the instance is rendered to it.</p>

<p>And that&#39;s it!</p>

<h1 id="deploying" id="deploying">Deploying</h1>

<p>The code is there now.
But of course, I still needed to deploy this to my hosts and make it available
in my prometheus and grafana instances.</p>

<p>Because I use <a href="https://nixos.org">NixOS</a>, I wrote a nix package definition and a
nix service defintion for it,
making the endpoint available to my prometheus instance via my wireguard
network.</p>

<p>After that, I was able to add queries to my grafana instance, for example:</p>

<pre><code>mpd_db_playtime / 60 / 60 / 24
</code></pre>

<p>to display the DB playtime of an instance of my MPD in days.</p>

<p>I&#39;m not yet very proficient in grafana and the query language, and also the
service implementation is rather minimal, so there cannot be that much metrics
yet.</p>

<p>Either way, it works!</p>

<p><img src="/prometheus-mpd-exporter-grafana-dashboard.png" alt="A basic dashboard for MPD stats"></p>

<h1 id="next-steps-and-closing-words" id="next-steps-and-closing-words">Next steps and closing words</h1>

<p>The next steps are quite simple.
First of all, I want to make more stats available to prometheus. Right now, only
the basic statistics of the database are exported.</p>

<p>The <code>async_mpd</code> crate makes a lot of
<a href="https://docs.rs/async-mpd/0.4.0/async_mpd/struct.Status.html">other status information</a>
available.</p>

<p>Also, I want to get better with grafana queries and make some nice-looking
graphs for my dashboard.</p>

<p>Either way, that challenge took me longer than I anticipated in the first place
(“I can hack this in 15 minutes” – famous last words)!
But it was fun nonetheless!</p>

<p>The outcome of this little journey is on
<a href="https://crates.io/crates/prometheus-mpd-exporter">crates.io</a>
and I will also submit a PR to nixpkgs to make it available there, too.</p>

<p>If you want to contribute patches to the sourcecode, which I encourage you to
do, feel free to <a href="https://git-send-email.io">send me patches</a>!</p>

<p>tags: <a href="https://beyermatthias.de/tag:prometheus" class="hashtag"><span>#</span><span class="p-category">prometheus</span></a> <a href="https://beyermatthias.de/tag:grafana" class="hashtag"><span>#</span><span class="p-category">grafana</span></a> <a href="https://beyermatthias.de/tag:rust" class="hashtag"><span>#</span><span class="p-category">rust</span></a> <a href="https://beyermatthias.de/tag:mpd" class="hashtag"><span>#</span><span class="p-category">mpd</span></a> <a href="https://beyermatthias.de/tag:music" class="hashtag"><span>#</span><span class="p-category">music</span></a></p>
]]></content:encoded>
      <guid>https://beyermatthias.de/writing-a-prometheus-mpd-exporter</guid>
      <pubDate>Sun, 03 Jan 2021 16:40:15 +0100</pubDate>
    </item>
    <item>
      <title>34c3</title>
      <link>https://beyermatthias.de/34c3</link>
      <description>&lt;![CDATA[34c3 was awesome. I prepared a blog article as my recap, though I failed to&#xA;provide enough content. That&#39;s why I will simply list my &#34;toots&#34; from mastodon&#xA;here, as a short recap for the whole congress.&#xA;&#xA;(2017-12-26,  4:04 PM) - Arrived at #34c3&#xA;(2017-12-27,  9:55 AM) - Hi #31c3 ! Arrived in Adams, am excited for the intro talk in less than 65 min! smallYes, I got the tag wrong on this one/small&#xA;(2017-12-27, 10:01 AM) - Oh my god I&#39;m so excited about #34c3 ... this is huge, girls and boys! The best congress ever is about to start!&#xA;(2017-12-27, 10:25 AM) - Be awesome to eachother #34c3 ... so far it works beautifully!&#xA;(2017-12-27, 10:31 AM) - #34c3 first mate is empty.&#xA;(2017-12-27, 10:46 AM) - #34c3 - less than 15 minutes. Oh MY GOOOOOOOOOD&#xA;(2017-12-27, 10:49 AM) - Kinda sad that #fefe won&#39;t do the Fnord this year at #34c3 ... but I also think that this year was to shitty to laugh about it, right?&#xA;(2017-12-27, 10:51 AM) - #34c3 oh my good 10 minutes left!&#xA;(2017-12-27, 11:02 AM) - #34c3 GO GO GO GO!&#xA;(2017-12-27, 11:16 AM) - Vom Spieltrieb zur Wissbegierig! #34c3&#xA;(2017-12-27, 12:17 PM) - People asked me things because I am wearing a #nixos T-shirt! Awesome! #34c3&#xA;(2017-12-27, 12:59 PM) - I really hope i will be able to talk to the #secushare people today #34c3&#xA;(2017-12-27,  1:44 PM) - I talked to even more people about #nixos ... and also about #rust ... #34c3 barely started and is already awesome!&#xA;(2017-12-27,  4:28 PM) - Just found a seat in Adams. Awesome! #34c3&#xA;(2017-12-27,  8:16 PM) -  Single girls of #34c3 - where are you?&#xA;(2017-12-28, 10:25 AM) - Day 2 at #34c3 ... Yeah! Today there will be the #mastodon #meetup ... Really looking forward to that!&#xA;(2017-12-28, 12:32 PM) - Just saw ads for a #rust #wayland compositor on an info screen at #34c3 - yeah, awesome!&#xA;(2017-12-28, 12:37 PM) - First mate today. Boom. I&#39;m awake! #34c3&#xA;(2017-12-28, 12:42 PM) - #mastodon ads on screen! Awesome! #34c3&#xA;(2017-12-28, 12:45 PM) - #taskwarrior ads on screen - #34c3&#xA;(2017-12-28,  3:14 PM) - I think I will not publish a blog post about the #34c3 but simply list all my toots and post that as an blog article. Seems to be much easier.&#xA;(2017-12-28,  3:15 PM) - #34c3 does not feel like a hacker event (at least not like the what I&#39;m used to) because there are so many (beautiful) women around here.&#xA;(2017-12-28,  3:36 PM) - The food in the congress center in Leipzig at #34c3 is REALLY expensive IMO. 8.50 for a burger with some fries is too expensive. And it is even less than the Chili in Hamburg was.&#xA;(2017-12-28,  3:43 PM) - Prepare your toots! #mastodon meetup in less than 15 minutes! #34c3&#xA;(2017-12-28,  3:50 PM) - #34c3 Hi #mastodon #meetup !&#xA;(2017-12-28,  3:55 PM) - Whuha... there are much more people than I&#39;ve expected here at the #mastodon #meetup #34c3&#xA;(2017-12-28,  4:03 PM) - Ok. Small #meetup - or not so small. Awesome. Room is packed. #34c3 awesomeness!&#xA;(2017-12-28,  4:09 PM) - 10 minutes in ... and we&#39;re already discussing pineapples. Community ftw! #34c3 #mastodon #meetup&#xA;(2017-12-28,  4:46 PM) - Limiting sharing of #toots does only work if all instances behave! #34c3 #mastodon #meetup&#xA;(2017-12-28,  4:56 PM) - Who-is-who #34c3 #mastodon #meetup doesn&#39;t work for me... because I don&#39;t know the 300 usernames from the top of my head...&#xA;(2017-12-28,  5:17 PM) - From one #meetup to the next: #nixos ! #34c3&#xA;(2017-12-28,  5:57 PM) - Unfortunately the #nixos community has no space for their #meetup at #34c3 ... kinda ad-hoc now!&#xA;(2017-12-28,  7:58 PM) - Now... Where are all the single ladies? #34c3&#xA;(2017-12-28,  9:27 PM) - #34c3 can we have #trance #music please?&#xA;(2017-12-28,  9:38 PM) - Where are my fellow #34c3 #mastodon #meetup people? Get some #toots posted, come on!&#xA;(2017-12-29,  1:44 AM) - Day 2 ends for me now. #34c3&#xA;(2017-12-29, 10:30 AM) - Methodisch Inkorrekt. Approx. 1k people waiting in line. Not nice. #34c3&#xA;(2017-12-29, 10:43 AM) - Damn. Notebook battery ran out of power last night. Cannot check mails and other unimportant things while waiting in line.  One improvement proposal for #34c3 - more power lines outside hackcenter!&#xA;(2017-12-29, 10:44 AM) - Nice. Now the wlan is breaking down. #34c3&#xA;(2017-12-29, 10:57 AM) - LAOOOOLAAA through the hall! We did it #34c3 !&#xA;(2017-12-30,  3:45 AM) - 9h Party. Straight. I&#39;m dead. #34c3&#xA;(2017-12-30,  9:08 PM) - After some awesome days at the #34c3 I am intellectually burned out now. That&#39;s why the #trance #techno #rave yesterday was exactly the right thing to do!&#xA;(2017-12-30, 11:35 PM) - Where can I get the set from yesterday night Chaos Stage #34c3 ??? Would love to trance into the next year with it!&#xA;(2017-12-31, 11:05 PM) - My first little #34c3 congress résumé: I should continue on #imag and invest even more time.  Not that I do not continue it, but progress is slowing down with the last months of my masters thesis... Understandable I guess.&#xA;&#xA;That was my congress. Yes, there are few toots after 28th... because I was&#xA;really tired by then and also had people to talk to all the time, so little&#xA;time for microblogging there. All in all: It was the best congress so far!&#xA;&#xA;tags: #ccc #social&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>34c3 was awesome. I prepared a blog article as my recap, though I failed to
provide enough content. That&#39;s why I will simply list my “toots” from mastodon
here, as a short recap for the whole congress.</p>
<ul><li>(2017-12-26,  4:04 PM) – Arrived at <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-27,  9:55 AM) – Hi <a href="https://beyermatthias.de/tag:31c3" class="hashtag"><span>#</span><span class="p-category">31c3</span></a> ! Arrived in Adams, am excited for the intro talk in less than 65 min! <small>Yes, I got the tag wrong on this one</small></li>
<li>(2017-12-27, 10:01 AM) – Oh my god I&#39;m so excited about <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> ... this is huge, girls and boys! The best congress ever is about to start!</li>
<li>(2017-12-27, 10:25 AM) – Be awesome to eachother <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> ... so far it works beautifully!</li>
<li>(2017-12-27, 10:31 AM) – <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> first mate is empty.</li>
<li>(2017-12-27, 10:46 AM) – <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> – less than 15 minutes. Oh MY GOOOOOOOOOD</li>
<li>(2017-12-27, 10:49 AM) – Kinda sad that <a href="https://beyermatthias.de/tag:fefe" class="hashtag"><span>#</span><span class="p-category">fefe</span></a> won&#39;t do the Fnord this year at <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> ... but I also think that this year was to shitty to laugh about it, right?</li>
<li>(2017-12-27, 10:51 AM) – <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> oh my good 10 minutes left!</li>
<li>(2017-12-27, 11:02 AM) – <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> GO GO GO GO!</li>
<li>(2017-12-27, 11:16 AM) – Vom Spieltrieb zur Wissbegierig! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-27, 12:17 PM) – People asked me things because I am wearing a <a href="https://beyermatthias.de/tag:nixos" class="hashtag"><span>#</span><span class="p-category">nixos</span></a> T-shirt! Awesome! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-27, 12:59 PM) – I really hope i will be able to talk to the <a href="https://beyermatthias.de/tag:secushare" class="hashtag"><span>#</span><span class="p-category">secushare</span></a> people today <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-27,  1:44 PM) – I talked to even more people about <a href="https://beyermatthias.de/tag:nixos" class="hashtag"><span>#</span><span class="p-category">nixos</span></a> ... and also about <a href="https://beyermatthias.de/tag:rust" class="hashtag"><span>#</span><span class="p-category">rust</span></a> ... <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> barely started and is already awesome!</li>
<li>(2017-12-27,  4:28 PM) – Just found a seat in Adams. Awesome! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-27,  8:16 PM) –  Single girls of <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> – where are you?</li>
<li>(2017-12-28, 10:25 AM) – Day 2 at <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> ... Yeah! Today there will be the <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a> ... Really looking forward to that!</li>
<li>(2017-12-28, 12:32 PM) – Just saw ads for a <a href="https://beyermatthias.de/tag:rust" class="hashtag"><span>#</span><span class="p-category">rust</span></a> <a href="https://beyermatthias.de/tag:wayland" class="hashtag"><span>#</span><span class="p-category">wayland</span></a> compositor on an info screen at <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> – yeah, awesome!</li>
<li>(2017-12-28, 12:37 PM) – First mate today. Boom. I&#39;m awake! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-28, 12:42 PM) – <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> ads on screen! Awesome! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-28, 12:45 PM) – <a href="https://beyermatthias.de/tag:taskwarrior" class="hashtag"><span>#</span><span class="p-category">taskwarrior</span></a> ads on screen – <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-28,  3:14 PM) – I think I will not publish a blog post about the <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> but simply list all my toots and post that as an blog article. Seems to be much easier.</li>
<li>(2017-12-28,  3:15 PM) – <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> does not feel like a hacker event (at least not like the what I&#39;m used to) because there are so many (beautiful) women around here.</li>
<li>(2017-12-28,  3:36 PM) – The food in the congress center in Leipzig at <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> is REALLY expensive IMO. 8.50 for a burger with some fries is too expensive. And it is even less than the Chili in Hamburg was.</li>
<li>(2017-12-28,  3:43 PM) – Prepare your toots! <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> meetup in less than 15 minutes! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-28,  3:50 PM) – <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> Hi <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a> !</li>
<li>(2017-12-28,  3:55 PM) – Whuha... there are much more people than I&#39;ve expected here at the <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a> <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-28,  4:03 PM) – Ok. Small <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a> – or not so small. Awesome. Room is packed. <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> awesomeness!</li>
<li>(2017-12-28,  4:09 PM) – 10 minutes in ... and we&#39;re already discussing pineapples. Community ftw! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a></li>
<li>(2017-12-28,  4:46 PM) – Limiting sharing of <a href="https://beyermatthias.de/tag:toots" class="hashtag"><span>#</span><span class="p-category">toots</span></a> does only work if all instances behave! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a></li>
<li>(2017-12-28,  4:56 PM) – Who-is-who <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a> doesn&#39;t work for me... because I don&#39;t know the 300 usernames from the top of my head...</li>
<li>(2017-12-28,  5:17 PM) – From one <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a> to the next: <a href="https://beyermatthias.de/tag:nixos" class="hashtag"><span>#</span><span class="p-category">nixos</span></a> ! <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-28,  5:57 PM) – Unfortunately the <a href="https://beyermatthias.de/tag:nixos" class="hashtag"><span>#</span><span class="p-category">nixos</span></a> community has no space for their <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a> at <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> ... kinda ad-hoc now!</li>
<li>(2017-12-28,  7:58 PM) – Now... Where are all the single ladies? <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-28,  9:27 PM) – <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> can we have <a href="https://beyermatthias.de/tag:trance" class="hashtag"><span>#</span><span class="p-category">trance</span></a> <a href="https://beyermatthias.de/tag:music" class="hashtag"><span>#</span><span class="p-category">music</span></a> please?</li>
<li>(2017-12-28,  9:38 PM) – Where are my fellow <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> <a href="https://beyermatthias.de/tag:mastodon" class="hashtag"><span>#</span><span class="p-category">mastodon</span></a> <a href="https://beyermatthias.de/tag:meetup" class="hashtag"><span>#</span><span class="p-category">meetup</span></a> people? Get some <a href="https://beyermatthias.de/tag:toots" class="hashtag"><span>#</span><span class="p-category">toots</span></a> posted, come on!</li>
<li>(2017-12-29,  1:44 AM) – Day 2 ends for me now. <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-29, 10:30 AM) – Methodisch Inkorrekt. Approx. 1k people waiting in line. Not nice. <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-29, 10:43 AM) – Damn. Notebook battery ran out of power last night. Cannot check mails and other unimportant things while waiting in line.  One improvement proposal for <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> – more power lines outside hackcenter!</li>
<li>(2017-12-29, 10:44 AM) – Nice. Now the wlan is breaking down. <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-29, 10:57 AM) – LAOOOOLAAA through the hall! We did it <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> !</li>
<li>(2017-12-30,  3:45 AM) – 9h Party. Straight. I&#39;m dead. <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a></li>
<li>(2017-12-30,  9:08 PM) – After some awesome days at the <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> I am intellectually burned out now. That&#39;s why the <a href="https://beyermatthias.de/tag:trance" class="hashtag"><span>#</span><span class="p-category">trance</span></a> <a href="https://beyermatthias.de/tag:techno" class="hashtag"><span>#</span><span class="p-category">techno</span></a> <a href="https://beyermatthias.de/tag:rave" class="hashtag"><span>#</span><span class="p-category">rave</span></a> yesterday was exactly the right thing to do!</li>
<li>(2017-12-30, 11:35 PM) – Where can I get the set from yesterday night Chaos Stage <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> ??? Would love to trance into the next year with it!</li>
<li>(2017-12-31, 11:05 PM) – My first little <a href="https://beyermatthias.de/tag:34c3" class="hashtag"><span>#</span><span class="p-category">34c3</span></a> congress résumé: I should continue on <a href="https://beyermatthias.de/tag:imag" class="hashtag"><span>#</span><span class="p-category">imag</span></a> and invest even more time.  Not that I do not continue it, but progress is slowing down with the last months of my masters thesis... Understandable I guess.</li></ul>

<p>That was my congress. Yes, there are few toots after 28th... because I was
really tired by then and also had people to talk to all the time, so little
time for microblogging there. All in all: It was the best congress so far!</p>

<p>tags: <a href="https://beyermatthias.de/tag:ccc" class="hashtag"><span>#</span><span class="p-category">ccc</span></a> <a href="https://beyermatthias.de/tag:social" class="hashtag"><span>#</span><span class="p-category">social</span></a></p>
]]></content:encoded>
      <guid>https://beyermatthias.de/34c3</guid>
      <pubDate>Mon, 01 Jan 2018 16:38:38 +0100</pubDate>
    </item>
    <item>
      <title>The development of a music taste</title>
      <link>https://beyermatthias.de/the-development-of-a-music-taste</link>
      <description>&lt;![CDATA[It is funny how a music taste changes over time.&#xA;&#xA;When I started listening to music extensively, which was about 10 years ago (at&#xA;the time, the nickname &#34;musicmatze&#34; came up, btw), I mostly listened to German&#xA;rap and HipHop. I did not listen to &#34;known&#34; artists at the time. Some names I&#xA;can remember are Pidvalid, Syntheciser, End and Alligatoah (yeah kids, take that&#xA;I listened to Alligatoah before it was cool).&#xA;&#xA;After I graduated from middle school and entered the Gymnasium, my friends all&#xA;listened to Heavy Metal. Soonish, I discovered Heavy Metal myself and found my&#xA;new favourite genre - Melodic Death Metal. MDM is my favourite genre till today,&#xA;but at the time it was the only genre I liked. Bands were Norther, Insomnium,&#xA;Dark Tranquillity, Soilwork and In Flames - my favourite bands still today.&#xA;&#xA;When I turned 18, and finaly was able to stay at parties all night, I developed&#xA;a more broad music taste, including Death Metal, Black Metal, Nu Metal, Neue&#xA;Deutsche Härte, Metalcore and also, a bit later, Hardcore, Screamo, Brutal&#xA;Deathcore and other related genres.&#xA;&#xA;When I was around 20 and 21 I discovered EBM, Industrial and Dark Wave, a bit&#xA;later also Aggrotech. This led me to like electronic music - which led me to&#xA;love EDM and especially Trance and Psy Trance but also Hardstyle when I was&#xA;about 24 until today.&#xA;&#xA;I really have no point here - I just wanted to write down that a I think it is&#xA;amusing how a music taste changes over time.&#xA;&#xA;Btw - I rarely listen to HipHop anymore. And if I do, not German one. Today I&#xA;think German music (with the exception of Rammstein and a few other Bands) just&#xA;sucks.&#xA;&#xA;tags:  #music&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>It is funny how a music taste changes over time.</p>

<p>When I started listening to music extensively, which was about 10 years ago (at
the time, the nickname “musicmatze” came up, btw), I mostly listened to German
rap and HipHop. I did not listen to “known” artists at the time. Some names I
can remember are Pidvalid, Syntheciser, End and Alligatoah (yeah kids, take that
– I listened to Alligatoah before it was cool).</p>

<p>After I graduated from middle school and entered the Gymnasium, my friends all
listened to Heavy Metal. Soonish, I discovered Heavy Metal myself and found my
new favourite genre – Melodic Death Metal. MDM is my favourite genre till today,
but at the time it was the <em>only</em> genre I liked. Bands were Norther, Insomnium,
Dark Tranquillity, Soilwork and In Flames – my favourite bands still today.</p>

<p>When I turned 18, and finaly was able to stay at parties all night, I developed
a more broad music taste, including Death Metal, Black Metal, Nu Metal, Neue
Deutsche Härte, Metalcore and also, a bit later, Hardcore, Screamo, Brutal
Deathcore and other related genres.</p>

<p>When I was around 20 and 21 I discovered EBM, Industrial and Dark Wave, a bit
later also Aggrotech. This led me to like electronic music – which led me to
love EDM and especially Trance and Psy Trance but also Hardstyle when I was
about 24 until today.</p>

<p>I really have no point here – I just wanted to write down that a I think it is
amusing how a music taste changes over time.</p>

<p>Btw – I rarely listen to HipHop anymore. And if I do, not German one. Today I
think German music (with the exception of Rammstein and a few other Bands) just
sucks.</p>

<p>tags:  <a href="https://beyermatthias.de/tag:music" class="hashtag"><span>#</span><span class="p-category">music</span></a></p>
]]></content:encoded>
      <guid>https://beyermatthias.de/the-development-of-a-music-taste</guid>
      <pubDate>Thu, 26 Oct 2017 16:38:21 +0200</pubDate>
    </item>
    <item>
      <title>One new mobile phone</title>
      <link>https://beyermatthias.de/one-new-mobile-phone</link>
      <description>&lt;![CDATA[It happened that I ordered a new mobile phone for myself. And because I don&#39;t&#xA;want to have a google account, pre-installed facebook Apps and the like, it is&#xA;a OnePlus One.&#xA;&#xA;!-- more --&#xA;&#xA;The order&#xA;&#xA;Well, to order a oneplusone you can go to amazon and order it for like 433&#xA;Euro (64 GB version). Or you go to the oneplus store and order it for 299&#xA;Euro, which is what I did. I also ordered a screen protector and a case for&#xA;it, to protect it even more.&#xA;&#xA;But well, if you want to order from the manufacturer, you have to register on&#xA;their page, which pissed me off a bit. The second problem was that you can&#xA;only pay via Paypal and because you order from non-germany, you have to setup&#xA;your Paypal account with a credit card. I do not have a Paypal account&#xA;(because it really sucks) - so I asked someone else to pay the order for me&#xA;and transfered the money to the persons bank account.&#xA;&#xA;Unboxing&#xA;&#xA;The One arrived seven days after I ordered it. It was absolutely nicely boxed,&#xA;though some boxing was rather ridiculous: I understand that the power adapter&#xA;has to be boxed separately, because of adapter variants. I do not understand&#xA;why it has to be boxed two times - one time in a box, another time in a&#xA;plastic-foil.&#xA;&#xA;Screen protector&#xA;&#xA;I also ordered a screen protector. It was shiped together with the One, nice!&#xA;I applied it accordingly to&#xA;this tutorial and I managed to&#xA;get only one really tiny bubble under the protector foil. Well done!&#xA;&#xA;Update!&#xA;&#xA;Of course, when starting the One for the first time, it asks you to insert&#xA;your google account data, etc. etc. I do not have a google account, so I was&#xA;really pleasant surprised when I was offered a &#34;skip&#34; button!&#xA;&#xA;As I do not have a micro/nano-SIM card yet, I also skipped the SIM setup.&#xA;&#xA;Then, after some localization and skipping, I was offered to update the phone&#xA;from CM 11 to CM 12. I did this, of course. It was really fast, although it&#xA;said it would take up to 20 minutes. While updating it told me that 128 Apps&#xA;are optimized. I always thought CM comes without Apps preinstalled? Well... We&#xA;will see.&#xA;&#xA;Initial setup&#xA;&#xA;After applying the update I was asked for several things again, including&#xA;google account data. Of course I switched off what I could switch off.&#xA;&#xA;I also disabled everything the google apps want to do. None of the google&#xA;apps should now be able to access my texts or other data. I hope.&#xA;&#xA;The initial setup was fun. I really enjoyed using the device. It is a bit&#xA;heavier than my old device, but it is also a few inches bigger, so that&#39;s&#xA;completely okay.&#xA;&#xA;I really hope I can get my SIM card as soon as possible, so I can start using&#xA;the device properly.&#xA;&#xA;What really bothered me in the first place: I have a 64 GB device. I can use&#xA;about 55 GB of this - copying music will be painful! But well... I solved that&#xA;&#34;issue&#34;. I found out, that one can install git-annex on Android. So I did that&#xA;and just imported my complete music library. It took a rather long time to&#xA;create the 44771 files and 3913 folders on the device. But having git-annex&#xA;available on my Android device is a great plus point. So I don&#39;t have to care&#xA;about syncing the files and so on, I just can use git-annex and it does&#xA;everything for me. I just have to be careful with the available memory, as 55&#xA;GB can not handle my 500 GB music library, of course.&#xA;&#xA;And, just you know, I do not use the web interface. I use the terminal&#xA;emulator, of course!&#xA;&#xA;But then, the disillusion: The music player didn&#39;t work with the symlinks&#xA;git-annex generates. So I removed the git-annex repository from the device and&#xA;copied music to it as I am used to.&#xA;&#xA;Sync with owncloud&#xA;&#xA;Well, syncing with owncloud was a topic. I thought it would be easy, but it&#xA;wasn&#39;t.&#xA;&#xA;There is this DavDroid app on both the market and F-Droid, which can by used&#xA;to sync between caldav/carddav servers and the device. I entered my owncloud&#xA;data and synced, but it just won&#39;t work.&#xA;&#xA;So I filed a bug in the forum of the developers, but they couldn&#39;t help me&#xA;with my problem (though they responded really quickly and were really nice at&#xA;all).&#xA;&#xA;So I tried other synchronization apps and finally landed with&#xA;CalDAV Sync Adapter and carddav sync free which worked.&#xA;&#xA;Now I have everything set up to use the oneplusone as my daily driver! Yay!&#xA;&#xA;A gem I found&#xA;&#xA;I found a nice app in the f-droid as well: whohasmystuff, which can be used&#xA;to keep track of things you lend to someone. Really nice and basic app. It&#xA;just works(tm).&#xA;&#xA;tags:  #android #cmmod #linux #media #music #open source #software #oneplusone&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>It happened that I ordered a new mobile phone for myself. And because I don&#39;t
want to have a google account, pre-installed facebook Apps and the like, it is
a OnePlus One.</p>



<h1 id="the-order" id="the-order">The order</h1>

<p>Well, to order a oneplusone you can go to amazon and order it for like 433
Euro (64 GB version). Or you go to the oneplus store and order it for 299
Euro, which is what I did. I also ordered a screen protector and a case for
it, to protect it even more.</p>

<p>But well, if you want to order from the manufacturer, you have to register on
their page, which pissed me off a bit. The second problem was that you can
only pay via Paypal and because you order from non-germany, you have to setup
your Paypal account with a credit card. I do not have a Paypal account
(because it really sucks) – so I asked someone else to pay the order for me
and transfered the money to the persons bank account.</p>

<h1 id="unboxing" id="unboxing">Unboxing</h1>

<p>The One arrived seven days after I ordered it. It was absolutely nicely boxed,
though some boxing was rather ridiculous: I understand that the power adapter
has to be boxed separately, because of adapter variants. I do not understand
why it has to be boxed two times – one time in a box, another time in a
plastic-foil.</p>

<h1 id="screen-protector" id="screen-protector">Screen protector</h1>

<p>I also ordered a screen protector. It was shiped together with the One, nice!
I applied it accordingly to
<a href="https://www.youtube.com/watch?v=dS6JZiQcbM4">this tutorial</a> and I managed to
get only one really tiny bubble under the protector foil. Well done!</p>

<h1 id="update" id="update">Update!</h1>

<p>Of course, when starting the One for the first time, it asks you to insert
your google account data, etc. etc. I do not have a google account, so I was
really pleasant surprised when I was offered a “skip” button!</p>

<p>As I do not have a micro/nano-SIM card yet, I also skipped the SIM setup.</p>

<p>Then, after some localization and skipping, I was offered to update the phone
from CM 11 to CM 12. I did this, of course. It was <em>really</em> fast, although it
said it would take up to 20 minutes. While updating it told me that 128 Apps
are optimized. I always thought CM comes without Apps preinstalled? Well... We
will see.</p>

<h1 id="initial-setup" id="initial-setup">Initial setup</h1>

<p>After applying the update I was asked for several things <em>again</em>, including
google account data. Of course I switched off what I could switch off.</p>

<p>I also disabled <em>everything</em> the google apps want to do. None of the google
apps should now be able to access my texts or other data. I hope.</p>

<p>The initial setup was fun. I really enjoyed using the device. It is a bit
heavier than my old device, but it is also a few inches bigger, so that&#39;s
completely okay.</p>

<p>I really hope I can get my SIM card as soon as possible, so I can start using
the device properly.</p>

<p>What really bothered me in the first place: I have a 64 GB device. I can use
about 55 GB of this – copying music will be painful! But well... I solved that
“issue”. I found out, that one can install git-annex on Android. So I did that
and just imported my complete music library. It took a rather long time to
create the 44771 files and 3913 folders on the device. But having git-annex
available on my Android device is a great plus point. So I don&#39;t have to care
about syncing the files and so on, I just can use git-annex and it does
everything for me. I just have to be careful with the available memory, as 55
GB can not handle my 500 GB music library, of course.</p>

<p>And, just you know, I do not use the web interface. I use the terminal
emulator, of course!</p>

<p>But then, the disillusion: The music player didn&#39;t work with the symlinks
git-annex generates. So I removed the git-annex repository from the device and
copied music to it as I am used to.</p>

<h1 id="sync-with-owncloud" id="sync-with-owncloud">Sync with owncloud</h1>

<p>Well, syncing with owncloud was a topic. I thought it would be easy, but it
wasn&#39;t.</p>

<p>There is this <code>DavDroid</code> app on both the market and F-Droid, which can by used
to sync between caldav/carddav servers and the device. I entered my owncloud
data and synced, but it just won&#39;t work.</p>

<p>So I filed a bug in the forum of the developers, but they couldn&#39;t help me
with my problem (though they responded really quickly and were really nice at
all).</p>

<p>So I tried other synchronization apps and finally landed with
<code>CalDAV Sync Adapter</code> and <code>carddav sync free</code> which worked.</p>

<p>Now I have everything set up to use the oneplusone as my daily driver! Yay!</p>

<h1 id="a-gem-i-found" id="a-gem-i-found">A gem I found</h1>

<p>I found a nice app in the f-droid as well: <code>whohasmystuff</code>, which can be used
to keep track of things you lend to someone. Really nice and basic app. It
just works™.</p>

<p>tags:  <a href="https://beyermatthias.de/tag:android" class="hashtag"><span>#</span><span class="p-category">android</span></a> <a href="https://beyermatthias.de/tag:cmmod" class="hashtag"><span>#</span><span class="p-category">cmmod</span></a> <a href="https://beyermatthias.de/tag:linux" class="hashtag"><span>#</span><span class="p-category">linux</span></a> <a href="https://beyermatthias.de/tag:media" class="hashtag"><span>#</span><span class="p-category">media</span></a> <a href="https://beyermatthias.de/tag:music" class="hashtag"><span>#</span><span class="p-category">music</span></a> <a href="https://beyermatthias.de/tag:open" class="hashtag"><span>#</span><span class="p-category">open</span></a> source <a href="https://beyermatthias.de/tag:software" class="hashtag"><span>#</span><span class="p-category">software</span></a> <a href="https://beyermatthias.de/tag:oneplusone" class="hashtag"><span>#</span><span class="p-category">oneplusone</span></a></p>
]]></content:encoded>
      <guid>https://beyermatthias.de/one-new-mobile-phone</guid>
      <pubDate>Sun, 04 Oct 2015 16:37:42 +0200</pubDate>
    </item>
    <item>
      <title>How mpdscribble did not scrobble - because of the system time!</title>
      <link>https://beyermatthias.de/how-mpdscribble-did-not-scrobble-because-of-the-system-time</link>
      <description>&lt;![CDATA[I started my music server after I came yesterday. I mounted the external&#xA;music hard drive, I started mpd. Then I wanted to start mpdscribble to&#xA;scrobble to my last.fm account. But it did not start. Here&#39;s why.&#xA;&#xA;!-- more --&#xA;&#xA;First, I thought the configuration must be broken somehow. But it looked fine.&#xA;I had no package update, as I&#39;m running Debian stable on this machine - so no&#xA;updates for these packages!&#xA;&#xA;I double- and trible-checked the configuration, the method how I started mpd&#xA;and so on. But I couldn&#39;t find an issue. The log file was not written, the&#xA;journal was slowly filled.&#xA;&#xA;After a while, I noticed that the logfile had an entry for writing to the&#xA;syslog daemon, which resulted the log to be in /var/log/daemon.log and there&#xA;I found it: The system time seem&#39;d to be broken. And yes, it was the&#xA;28th Dec. 2014. I don&#39;t know why, but I guess that&#39;s the time when the BIOS&#xA;battery run out of energy or something like this. Anyways, fixed the system&#xA;time and got it working!&#xA;&#xA;tags:  #music #server #mpd&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>I started my music server after I came yesterday. I mounted the external
music hard drive, I started mpd. Then I wanted to start mpdscribble to
scrobble to my last.fm account. But it did not start. Here&#39;s why.</p>



<p>First, I thought the configuration must be broken somehow. But it looked fine.
I had no package update, as I&#39;m running Debian stable on this machine – so no
updates for these packages!</p>

<p>I double- and trible-checked the configuration, the method how I started mpd
and so on. But I couldn&#39;t find an issue. The log file was not written, the
journal was slowly filled.</p>

<p>After a while, I noticed that the logfile had an entry for writing to the
syslog daemon, which resulted the log to be in <code>/var/log/daemon.log</code> and there
I found it: The system time seem&#39;d to be broken. And yes, it was the
28th Dec. 2014. I don&#39;t know why, but I guess that&#39;s the time when the BIOS
battery run out of energy or something like this. Anyways, fixed the system
time and got it working!</p>

<p>tags:  <a href="https://beyermatthias.de/tag:music" class="hashtag"><span>#</span><span class="p-category">music</span></a> <a href="https://beyermatthias.de/tag:server" class="hashtag"><span>#</span><span class="p-category">server</span></a> <a href="https://beyermatthias.de/tag:mpd" class="hashtag"><span>#</span><span class="p-category">mpd</span></a></p>
]]></content:encoded>
      <guid>https://beyermatthias.de/how-mpdscribble-did-not-scrobble-because-of-the-system-time</guid>
      <pubDate>Thu, 08 Jan 2015 16:37:28 +0100</pubDate>
    </item>
  </channel>
</rss>