Clustering events from a log file – with grep

written on 2019-06-24

Sometimes you need to track down an error in a software system by looking at log files.

Here's the useful "two greps and a count" pattern that has emerged for me:

Step 1: find the log lines that represent interesting events

Let's say you have a suspicion that there has been problems with your database connection. So let's find all lines that are related to that:

$ grep mariadb error.log

This will output possibly a lot of log lines that report errors regarding the database driver. When did they start? When did they stop? Let's take the next step.

Step 2: find some pattern to cluster by

In many cases, it is useful to find out about the distribution of the events over time. Let's extract the hour from each of the lines

$ grep mariadb error.log | grep -o " [0-9][0-9]:"

" [0-9][0-9]:" will match the hour part of the log line time stamp (adapt to match the time formatting of your particular log file). The -o switch will output only the matching part, not the entire line. The output will look like this:

 07:
 07:
 08:

Of course, you can choose to cluster by something other than hours, for example by the database server hostname, if you suspect that some host causes more error messages than the others.

Step 3: count occurrences per cluster

$ grep mariadb error.log | grep -o " [0-9][0-9]:" | sort | uniq -c

This will group and count the occurrences by the pattern extracted in step 2. The uniq command will reduce the input to, well, unique lines, the -c switch will tell you how often each line occurred. We also need sort, because uniq expects the input to be ordered, so the duplicate lines are next to each other.

  2  07:
249  08:
 80  09:
 16  10:

So, of those interesting log lines, most appeared at eight-something, so during the hour between 08:00 and 08:59. Now you know when your trouble started and where to keep looking.

Checkliste für pragmatischen Ubuntu-Arbeitsplatz

written on 2019-06-02, last updated on 2019-10-09

Pragmatischer und performanter Arbeitsplatz, wenn es einfach nur funktionieren soll.

  • Neueste LTS als Minimal Image auf USB-Stick laden (bei schneller Verbindung)
  • xubuntu-desktop als Installations-Set wählen
  • Automatische Updates an
  • MS Fonts installieren, damit erhaltene Word-Dokumente „normal“ aussehen
  • LibreOffice konfigurieren: Immer .docx speichern, damit andere Leute die Dateien lesen können
  • In Firefox uBlock Origin installieren
  • Thunderbird einrichten (Google Mail hat separaten OAuth-Login, es lohnt sich kein „App-Passwort“)
  • Thunderbird: Standardsprache de_DE einstellen (für Rechtschreibkorrektur)
  • Thunderbird: In „Verfassen“ die Kontakt-Seitenleiste einblenden
  • Drucker einrichten
  • Shotwell installieren, bestehende Fotos importieren, „Watch“-Funktion aktivieren
  • Wichtige Launcher umbenennen („Internet“, „E-Mail“, „Word“, „Fotos“)
  • Launcher-Menü aufräumen, Unwichtiges raus
  • TeamViewer installieren (manuelle Installation, die nur bei Bedarf einen Daemon startet), Launcher auf den Desktop
  • Standarduser zum Starten konfigurieren (lightdm-Config)
  • Starten ohne Password konfigurieren (in Benutzer-Einstellungen)
  • GRUB-Timeout kürzer, für schnelleres Booten
  • Backups mit Deja-Dup auf externer Festplatte einrichten, Integration im Thunar-Dateimanager einrichten.

Wann ein Static Site Generator Sinn macht (und warum Lektor eine gute Wahl ist)

written on 2019-06-01, last updated on 2019-06-02

Die meisten auf Content fokussierten Websites werden auf Basis eines Content Management Systems (CMS) implementiert. Oft mit Wordpress, manchmal auch mit Drupal oder TYPO3, selten auch mal mit Schwergewichten wie Adobe Experience Manager oder Sitecore.

Gemeinsam haben diese CMS, dass die Website jeweils dynamisch aus Daten und Templates beim Abruf zusammengebaut wird. Bestenfalls liegt noch ein kurzzeitiger Cache dazwischen.

Dieses Pattern hat sich bewährt und bringt einen großen Vorteil: Es kapselt die Technik einer Website weg. Statt zu programmieren, können wir, nun ja, Content managen. Mit Hilfe eines CMS kann ich Inhalte komfortabel und flexibel ergänzen und aktualisieren.

Dafür muss ich aber an einigen Stellen zusätzlichen Aufwand in Kauf nehmen:

Die Technik muss generisch mit allen denkbaren Inhalten zurechtkommen. Das Layout der Seite ist nicht für eine bestimmte Überschriftenlänge passend, sondern für alle Überschriften, die sich ein Content Manager in der Zukunft ausdenken könnte.

Die Server, die die Website ausliefern, müssen performant genug sein, um ständig die abgerufenen Seiten dynamisch nach aktuellem Stand des Contents neu zu generieren. Komplexität und Anforderungen für den Server steigen.

Und nicht zuletzt muss dieses komplexere Setup auch noch sicher sein und bleiben. Je mehr Logik ein System enthält, desto mehr kann auch schief gehen. In jedem verbreiteten CMS werden irgendwann Sicherheitslücken gefunden – und meist auch behoben. Ich muss meinen Server aktuell halten.

Lohnt sich das alles?

Content Manager sollen Inhalte komfortabel und flexibel ändern können.

Oft stellt sich heraus, dass diese Anforderung an eine Website überhaupt nicht stimmt.

Je seltener eine Website aktualisiert wird, desto weniger sind die „Content Manager“ mit dem CMS vertraut. Egal, ob Freelancer oder Agentur: Oft rufen Kund_innen einfach an, wenn sie eine Änderung an ihrer Website haben wollen, oder schreiben eine E-Mail. Das ist auch total OK, denn dafür haben sie ja ihre Agentur.

Nur: Dann können wir auch die ganze komfortable Oberfläche eines CMS in Frage stellen, wenn am Ende doch Expert_innen die Inhalte pflegen.

Alternative: ein Static Site Generator

Bei einem Static Site Generator werden ebenfalls Inhalte in einem generischen Template als Seiten ausgegeben. Aber dieser Prozess passiert nur zum Zeitpunkt der Aktualisierung auf einem Computer, der kein öffentlicher Server ist. Am Ende hat er eine Reihe statischer HTML-Seiten erzeugt, die die komplette Website darstellen.

Diese statischen Seiten lassen sich viel leichter sicher und performant veröffentlichen als eine CMS-Applikation.

Welches Vorgehen für welche Seite?

Diagramm mit zwei Achsen: Lebensdauer und Aktualisierungshäufigkeit

Wenn eine Website voraussichtlich nicht mehr aktualisiert werden soll und eventuell nur für eine kurze Lebensdauer vorgesehen ist, lohnt sich der Aufwand für ein generisches Template nicht. Dann kann ich die statische Seite auch manuell zusammenbauen.

Wenn die Seite häufig aktualisiert wird (und die Content Manager daher entsprechend vertraut mit dem CMS sind), lohnt sich ein CMS.

In vielen anderen Fällen macht ein Static Site Generator Sinn.

Warum Lektor

Lektor ist ein Static Site Generator. Es gibt davon hunderte, https://www.staticgen.com hat eine filterbare Liste. Meine Gründe, für dieses Blog auf Lektor zu setzen sind:

  • Lektor basiert auf Python. Im Idealfall muss ich mich mit der Programmierung des Tools nicht auseinandersetzen, aber meist ist das doch irgendwann nötig.
  • Templates für Lektor werden mit dem Templating-System Jinja2 gebaut. Es gehört zum „Pallets“ Stack, rund um das HTTP-Framework Flask und ist entsprechend bewährt und weit verbreitet.
  • Lektor hat eine einfache Admin-Oberfläche, die ich lokal im Browser aufrufen kann. Damit kann ich neue Posts anlegen und schreiben – ein bisschen Komfort ist doch ganz gut.

A simplistic approach to auto-detect the language of a blog post

written on 2019-05-07

HTML wants you to declare the language of your page.

<html lang="en">

would say: "this page is in English". Why is it useful? Once the browser knows the language of your page, it can apply automatic hyphenation for example.

I like writing in both English and German. So the language might vary between blog posts. In order to correctly declare the language in the HTML of each post, I could add a field, where I explicitly state either "en" or "de" for each blog post that I create.

But writing is about content, not about wrangling meta-data. So there's got to be a better way, right? Let's use some AI algorithm.

The 100 most common words

Take the most common 100 words from both languages. For each word, check if it occurs in the blog post. The language which has more word occurrences wins.

Luckily, Wikipedia has lists of the most common 100 words for both English and German.

This can even be implemented in the (slightly limited) Jinja templating language, which Lektor (the static site generator that this blog is based on) uses:

{%- macro auto_detect_lang(text) -%}
  {%- set de_words = ["der", "die", "und", "..."]  -%}
  {%- set en_words = ["the", "be", "to", "..."] -%}

  {%- set contained_de_words = [] -%}
  {%- set contained_en_words = []  -%}

  {%- for word in de_words -%}
    {%- if word in text -%}
      {%- do contained_de_words.append(word) -%}
    {%- endif %}
  {%- endfor %}

  {%- for word in en_words -%}
    {%- if word in text -%}
      {%- do contained_en_words.append(word) -%}
    {%- endif %}
  {%- endfor %}

  {%- if contained_de_words|length > contained_en_words|length -%}
  de
  {%- else -%}
  en
  {%- endif %}
{%- endmacro %}

You can find the full code in the blog's git repository.

This will be my blog.

written on 2019-05-06

It will be

  • made with a static site generator (probably Lektor)
  • pragmatic
  • written in Markdown
  • fast and standards-compliant
  • generated and deployed via CI

Future extensions will add

  • search (maybe with a pre-generated index by Lunr.js)

The source is at https://gitlab.com/pixelistik/blog/