LW IT Solutions
« Blog Overview /Digital Analytics/Tutorials / Tutorial: Die Containerversion finden, in der sich...
This post in other languages:

Tutorial: Die Containerversion finden, in der sich ein Tag geändert hat

Tutorial: Die Containerversion finden, in der sich ein Tag geändert hat
Inhalt
  1. Was die Versionsliste zeigt und was sie verbirgt
  2. Die Versionen über die API holen
  3. Die Prüfsumme, die eine Änderung anzeigt
  4. Die Version finden, dann zwei vergleichen
  5. Kontingent und die örtliche Kopie
  6. Die Frage, die die API nicht beantwortet
  7. Fragen und Antworten
  8. Quellen

Eine Zahl in einem Bericht ändert sich an einem Dienstag. Der Container wurde in diesem Monat viermal veröffentlicht, und die Versionsliste sagt, wer es getan hat und wann – und überhaupt nichts darüber, was sich darin bewegt hat.

Vier Versionen zu öffnen und zu lesen ist möglich. Vierzig ist es nicht, und vierzig sind es üblicherweise, wenn es jemandem auffällt. Es folgt der Weg, der zuerst die Version findet und erst danach zwei vergleicht.

Acht Containerversionen mit Datum, Veröffentlichendem und der Prüfsumme eines Tags, die beiden Zeilen hervorgehoben, in denen sich diese Prüfsumme geändert hat
Die Prüfsumme eines Elements über acht Versionen. Zwei Zeilen unterscheiden sich von der darüber, und nur diese beiden lohnen sich zu öffnen.

Was die Versionsliste zeigt und was sie verbirgt

Jede Veröffentlichung erzeugt eine Version, und diese Version ist eine vollständige, unveränderliche Kopie des Containers zu diesem Zeitpunkt. Die Liste davon zeigt vier Dinge: eine Nummer, einen Namen, wer veröffentlicht hat und wann.

Nicht zeigt sie den Inhalt. Zwei Versionen können sich in einem Zeichen einer Variablen unterscheiden, und die Liste sieht so oder so gleich aus. Die Änderungszahl neben einer Version hilft ein wenig und führt viel in die Irre, denn sie zählt die Änderungen im Arbeitsbereich und nicht den Unterschied zur vorigen Version – ein Arbeitsbereich, in dem etwas geändert und wieder zurückgenommen wurde, erhöht die Zahl, ohne etwas zu verändern.

Das eine Feld, das die Frage unmittelbar beantworten würde, sind die Versionsnotizen. Es ist freier Text, es wird beim Veröffentlichen geschrieben, und es ist in nahezu jedem existierenden Container leer. Es auszufüllen ist die billigstmögliche Verbesserung dieses ganzen Problems – und sie hilft erst ab jetzt.

Die Versionen über die API holen

Die Tag-Manager-API liefert dieselben Versionen, die die Oberfläche zeigt, und den vollständigen Inhalt jeder einzelnen. Lesezugriff genügt.

# Umfang: https://www.googleapis.com/auth/tagmanager.readonly

BASIS="https://tagmanager.googleapis.com/tagmanager/v2"
PFAD="accounts/6000000001/containers/7000000002"

# 1 die Liste der Versionen - kurz, ohne Inhalt
curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASIS/$PFAD/version_headers" | jq -r \
  '.containerVersionHeader[] | [.containerVersionId, .name] | @tsv'

# 2 eine vollstaendige Version
curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASIS/$PFAD/versions/41" > version-41.json

Zwei Dinge zum ersten Aufruf lohnen sich zu wissen. Er liefert Köpfe statt Inhalt, was ihn günstig macht und der richtige Weg zum Aufzählen ist. Und er ist seitenweise: Ein Container mit vielen Versionen liefert ein nextPageToken, und ein Skript, das es übergeht, arbeitet stillschweigend nur mit der neuesten Seite.

Der zweite Aufruf ist der teure, denn er liefert den ganzen Container. Das ist der Grund für den nächsten Abschnitt: vierzig vollständige Versionen zu holen, um ein Tag zu vergleichen, sind viele Daten für eine Frage.

Die Prüfsumme, die eine Änderung anzeigt

Jedes Element eines Containers trägt eine fingerprint-Angabe, und sie ändert sich, sobald dieses Element bearbeitet wird. Die Prüfsumme eines Tags über die Versionen zu vergleichen beantwortet die Frage deshalb, ohne sonst irgendetwas zu vergleichen.

import json, glob

GESUCHT = "25"   # tagId: GA4 - Event - purchase

vorher = None
for datei in sorted(glob.glob("version-*.json"),
                    key=lambda d: int(d.split("-")[1].split(".")[0])):
    daten = json.load(open(datei))
    tags  = {t["tagId"]: t for t in daten.get("tag", [])}
    tag   = tags.get(GESUCHT)

    if tag is None:
        print(f"{datei:16s} nicht vorhanden")
    else:
        marke = tag["fingerprint"]
        hinweis = "GEAENDERT" if vorher and marke != vorher else ""
        print(f"{datei:16s} {marke}  {hinweis}")
        vorher = marke

Die Ausgabe ist eine Zeile je Version, und die interessanten melden sich von selbst. Ein Tag, das dreißig Versionen lang unberührt blieb und sich dann einmal änderte, erzeugt genau eine markierte Zeile – und diese Zeile ist die Antwort.

Zwei Eigenschaften der Prüfsumme verdienen eine Anmerkung. Sie ist undurchsichtig – sie sagt, dass sich etwas geändert hat, nicht was -, und für diesen Schritt genügt das. Und sie ändert sich bei jeder Bearbeitung einschließlich einer Umbenennung: Ein umbenanntes Tag erscheint als geändert, ohne sich anders zu verhalten. Ein Abgleich über die tagId statt über den Namen ändert daran nichts, übersteht aber die Umbenennung, die das Element überhaupt erst schwer auffindbar gemacht hat.

Die Version finden, dann zwei vergleichen

Ist die Version bekannt, findet der Vergleich zwischen zwei Dateien statt und nicht zwischen vierzig – und er lohnt sich auf einer vereinheitlichten Kopie statt auf dem rohen Export.

# nur das gesuchte Tag aus beiden Versionen, sortiert
for v in 43 44; do
  jq -S --arg n "GA4 - Event - purchase" \
    '.tag[] | select(.name == $n)' version-$v.json > tag-$v.json
done

diff -u tag-43.json tag-44.json

Das -S macht das Ergebnis lesbar. Ohne es kommen die JSON-Schlüssel in der Reihenfolge zurück, die die API erzeugt hat, und ein Vergleich zweier bedeutungsgleicher Objekte kann dutzende Zeilen lang sein. Die Schlüssel zu sortieren beseitigt das vollständig, und übrig bleibt die eigentliche Änderung.

Drei Felder lohnen sich im Vergleich zu übergehen, weil sie sich von allein ändern: fingerprint, das hier zum Finden der Version dient und nicht zu ihrer Beschreibung, sowie jedes Zeitstempel- oder Pfadfeld, das die Versionsnummer trägt. Alles andere, was sich unterscheidet, ist ein echter Unterschied.

Kontingent und die örtliche Kopie

Die Tag-Manager-API ist je Minute und je Tag begrenzt, und eine Schleife über vierzig vollständige Versionen läuft schnell dagegen. Der Fehlschlag ist eine 429-Antwort, und ein Skript ohne Pause macht aus einer Untersuchung eine für den Rest der Stunde gesperrte API.

for v in $(cat versionen.txt); do
  [ -f "version-$v.json" ] && continue          # schon vorhanden
  curl -s -H "Authorization: Bearer $TOKEN" \
    "$BASIS/$PFAD/versions/$v" > "version-$v.json"
  sleep 2
done

Das Überspringen einer vorhandenen Datei ist die wichtige Zeile. Versionen sind unveränderlich, eine einmal geholte Version muss also nie erneut geholt werden – und ein Verzeichnis davon wird zu einem Archiv, das die nächste Frage beantwortet, ohne die API überhaupt anzufassen.

Dieses Archiv lohnt sich absichtlich anzulegen statt als Nebenprodukt. Eine wöchentliche Aufgabe, die die neuesten Versionen holt, kostet nichts – und damit steht der Verlauf auch für einen Container zur Verfügung, dessen Zugang später jemandem abhandenkommt.

Die Frage, die die API nicht beantwortet

Drei Dinge bleiben außen vor, und zu wissen, welche das sind, erspart eine lange Suche nach etwas, das nicht da ist.

Das erste ist, wer ein bestimmtes Element geändert hat. Eine Version hält fest, wer sie veröffentlicht hat, und eine Veröffentlichung kann die Arbeit mehrerer Personen aus mehreren Wochen enthalten. Eine Urheberschaft je Element hat die API nicht, und die Oberfläche auch nicht.

Das zweite ist alles, was nie zu einer Version wurde. Eine Änderung, die in einem Arbeitsbereich gemacht und vor der Veröffentlichung zurückgenommen wurde, hinterlässt keine Spur, und ein gelöschter Arbeitsbereich ebenso wenig. Der Verlauf ist ein Verlauf von Veröffentlichungen, nicht von Bearbeitungen.

Das dritte ist die Wirkung. Zwei Versionen können sich in einer Auslösebedingung unterscheiden, die zehn Prozent häufiger zutrifft, und nichts im Container sagt das – der Unterschied ist eine Zeile Konfiguration, und seine Folge steht in den Daten. Deshalb ist dieses ganze Verfahren die erste Hälfte einer Untersuchung: Es liefert den Zeitpunkt und die Änderung, und die zweite Hälfte ist die Prüfung, ob sich die Zahlen zu diesem Zeitpunkt ebenfalls bewegt haben.

Fragen und Antworten

Findet der Vergleich der Prüfsumme auch eine Änderung an einem Trigger oder einer Variablen, die das Tag benutzt?

Nein, und das ist die wichtigste Lücke dieses Verfahrens. Die fingerprint-Angabe eines Tags ändert sich nur, wenn das Tag selbst bearbeitet wird. Das Tag verweist auf seine Trigger über deren IDs und auf Variablen über ihren Namen in doppelten geschweiften Klammern. Ändert jemand die Auslösebedingung eines Triggers oder den Wert einer Variablen, bleibt das Tag Zeichen für Zeichen gleich und mit ihm seine Prüfsumme, obwohl es sich nun anders verhält.

Die Schleife aus dem Artikel lässt sich deshalb auf die Abhängigkeiten ausdehnen: Aus dem Tag werden die IDs in firingTriggerId und blockingTriggerId gelesen und die Namen aller {{…}}-Verweise gesammelt; danach werden die Prüfsummen dieser Trigger in daten["trigger"] und dieser Variablen in daten["variable"] über die Versionen genauso verglichen wie die des Tags.

Auch das reicht nicht ganz, denn Variablen können auf andere Variablen verweisen. Gründlich ist es, die Verweise zu verfolgen, bis keine neuen mehr hinzukommen; einfacher ist es, bei der Suche nach der Version gleich die Prüfsummen aller Elemente des Containers zu vergleichen.

Was passiert, wenn ein Abruf in der Schleife mit 429 scheitert?

Dann entsteht trotzdem eine Datei. curl -s schreibt ohne weitere Option auch eine Fehlerantwort in die Ausgabe, und version-44.json enthält dann die JSON-Fehlermeldung der API statt eines Containers. Beim nächsten Lauf gilt die Datei als vorhanden und wird übersprungen, und genau die Zeile, die das Archiv wertvoll macht, hält den Fehler fest.

Das Auswertungsskript meldet das nicht, sondern führt in die Irre: daten.get("tag", []) liefert für die Fehlermeldung eine leere Liste, und die Version erscheint als „nicht vorhanden“, als wäre das Tag darin gelöscht worden.

Die Option -f allein genügt nicht, denn die Umleitung mit > legt die Datei schon an, bevor curl antwortet; dann bliebe eine leere Datei liegen, die ebenso übersprungen würde. Sicher ist der Weg über eine temporäre Datei, die erst umbenannt wird, wenn curl erfolgreich war und jq -e .containerVersionId einen Wert findet.

Ist die Version, in der sich das Tag geändert hat, auch die, die am fraglichen Tag live war?

Nicht zwingend. Jede Veröffentlichung erzeugt eine Version, aber nicht jede Version wird veröffentlicht: Eine Version lässt sich auch anlegen, ohne sie live zu schalten, und eine ältere Version lässt sich später erneut veröffentlichen, etwa um eine Änderung rückgängig zu machen. Die Versionsnummer gibt deshalb die Reihenfolge der Entstehung an, nicht die der Veröffentlichung.

Für den Abgleich mit dem Bericht zählt, wann welche Version live ging. Findet der Vergleich die Änderung in Version 44, ist zu klären, ob und wann Version 44 veröffentlicht wurde und ob danach wieder eine ältere Version galt.

Welche Berechtigung braucht die wöchentliche Archivaufgabe?

Lesezugriff genügt, wie im Artikel angegeben, mit dem Umfang tagmanager.readonly. Läuft die Aufgabe über ein Dienstkonto, muss dessen E-Mail-Adresse im Tag Manager als Nutzer mit Leseberechtigung für das Konto oder den Container eingetragen sein; der Umfang allein gewährt keinen Zugriff. Mit bloßer Leseberechtigung kann die Aufgabe nichts veröffentlichen, ein abhandengekommener Schlüssel legt dann den Inhalt des Containers offen, verändert ihn aber nicht.

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in Touch

Briefly describe your project or inquiry for a tailored response. This site is protected by reCAPTCHA.

Kommentar schreiben

Abweichende Zahlen aus anderen Konten und Rückfragen zur Einrichtung sind hier willkommen.

Die E-Mail-Adresse wird nicht veröffentlicht. Pflichtfelder sind mit einem Stern versehen.

ALL ARTICLES & CATEGORIES

CCTV

Diese Rubrik per RSS verfolgen

Cloud & AI

Diese Rubrik per RSS verfolgen

Data Privacy

Alle 14 Artikel dieser Rubrik Diese Rubrik per RSS verfolgen

Digital Analytics

Alle 52 Artikel dieser Rubrik Diese Rubrik per RSS verfolgen

Digital Marketing

Alle 35 Artikel dieser Rubrik Diese Rubrik per RSS verfolgen

IT & Networks

Alle 17 Artikel dieser Rubrik Diese Rubrik per RSS verfolgen

Music Production

Alle 11 Artikel dieser Rubrik Diese Rubrik per RSS verfolgen

Raspberry Pi

Diese Rubrik per RSS verfolgen

Smart Home

Alle 18 Artikel dieser Rubrik Diese Rubrik per RSS verfolgen

Web Entwicklung

Diese Rubrik per RSS verfolgen

WordPress-Plugins & Tricks

Diese Rubrik per RSS verfolgen