: WWDC 2020, . , , — - , . , , - WWDC
Die meisten von Ihnen haben wahrscheinlich an einer Anwendung gearbeitet oder arbeiten derzeit daran, für die ein Großteil der Funktionalität von der Kommunikation mit dem Server über HTTP abhängt. Wenn die Dinge nicht wie erwartet funktionieren oder Sie nur einen Codebereich verstehen möchten, mit dem Sie noch nicht vertraut sind, ist es oft hilfreich, die HTTP-Anforderungen zwischen der Anwendung und dem Server zu überprüfen. Welche Anfragen wurden gestellt? Was genau sendet der Server? Dazu verwenden Sie wahrscheinlich Tools wie Charles Proxy oder Wireshark .
Diese Tools sind jedoch oft recht schwierig zu verwenden und insbesondere einzurichten. Möglicherweise müssen Sie Ihr eigenes SSL-Zertifikat einrichten und viele nicht triviale Schritte ausführen, damit das Gerät ihm vertraut. Sie zeigen auch viele Informationen an, die Sie möglicherweise nie benötigen, um Ihre Anwendung zu verstehen. Gleichzeitig ist es schwierig, sie dem zuzuordnen, was in Ihrer Anwendung geschieht. Was wäre, wenn ich Ihnen sagen würde, dass es Tools gibt, die den größten Teil dieser Arbeit erledigen können, deren Einrichtung viel weniger Aufwand erfordert und die Informationen auf eine Weise anzeigen, die viel vergleichbarer ist mit dem, was tatsächlich in Ihrer Anwendung geschieht? ?
In Vorbereitung auf die WWDC 1 der nächsten Woche habe ich einige Gespräche von früheren WWDCs (erneut) gesehen. Wie auch immer, ich habe völlig übersehen, dass die Kernwerkzeuge neu geschrieben wurden, um sie zu vereinheitlichen und das Erstellen von benutzerdefinierten Werkzeugen für Xcode 10 zu vereinfachen . Außerdem erwies sich WWDC 2019 als eine großartige Einführung in die Werkzeuge , die ich all die Jahre vermisst habe.
Cool, jetzt können Sie Ihre eigenen Werkzeuge schreiben, um Dinge zu messen, die Werkzeuge normalerweise nicht messen. Aber was können Sie messen und wie einfach ist das? Ich würde sagen: "fast alles" und "nicht so sehr schwierig, schnell genug". In der Regel müssen Sie lediglich eine XML-Datei schreiben, in der Sie erfahren, wie Sie Wegweiserzeiger aus Ihrem Code in Daten zur Anzeige in Tools konvertieren. Das dafür erforderliche XML ist nicht besonders ausgefallen. Das Haupthindernis ist, dass der "Code", den Sie schreiben, wahrscheinlich sehr anders ist als Sie es gewohnt sind. Es gibt nur sehr wenige Beispiele. Die Dokumentation gibt nur einen allgemeinen Überblick darüber, und obwohl Xcode tatsächlich recht ist Überprüft XML-Dateien streng, es gibt keine automatische Vervollständigung und es gibt wenig, was Ihnen das Auffinden von Fehlern erleichtern kann. Aber nach ein wenig ZeitSie können die Elemente finden, die Sie benötigen, und wenn Sie ein Beispiel zum Anpassen des Codes haben, können Sie die Dinge ziemlich schnell erledigen. Hier werde ich nur ein Beispiel geben und versuchen, alle nützlichen Links aufzulisten.
Aber fangen wir am Anfang an: Ich möchte, dass jeder von Ihnen, der zuvor Charles oder Wireshark verwendet hat, um Ihre Anwendung zu debuggen oder eine Anwendung entwickelt hat, die viele HTTP-Anforderungen stellt, in der Lage ist, ein für Ihre Anwendung angepasstes HTTP-Trace-Tool zu erstellen, oder zumindest ein Rahmen. Es wird so aussehen:
Ich habe ungefähr einen Tag gebraucht, um diesen Prototyp zu erstellen und zu debuggen (nachdem ich mir die relevanten WWDC-Videos angesehen hatte). Wenn Sie an nichts anderem als dem Code interessiert sind, können Sie ihn hier sehen .
Überblick
Der einfachste Weg, ein benutzerdefiniertes Tool zu erstellen, ist die Verwendung von os_signpost . Genau das werden wir hier tun. Sie verwenden es , Wegweiser Zeiger anmelden .Event oder .BEGIN und .end . Anschließend richten Sie ein benutzerdefiniertes Tool ein, um diese os_signpost-Intervalle zu analysieren und zusätzliche Werte zu extrahieren, die Sie angemeldet haben. Sie legen fest, wie sie im Diagramm angezeigt werden, wie sie gruppiert werden, welche gefiltert werden sollen und wie die Strukturen von Listen oder Bäumen / Flussdiagrammen im Detailfenster des Tools dargestellt werden. ...
Wir möchten ein Tool erstellen, das alle HTTP-Anforderungen, die unsere Webbibliothek durchlaufen, als Intervalle (Start + Ende) anzeigt, damit wir sehen können, wie lange sie dauern, und sie mit anderen Ereignissen in unserer Anwendung korrelieren können. In diesem Artikel verwende ich Alamofire als Netzwerkbibliothek des Tools und Wordpress als meine Profilierungsanwendung, einfach weil sie Open Source sind. Sie können jedoch problemlos den gesamten Code an Ihre Netzwerkbibliothek anpassen.
Schritt 0: Schauen Sie sich die Instruments App an
- ( 411 WWDC 2019) — . «Orientation», , (instruments), (tracks), (lanes), (traces), (templates), (detail view) . .
- ( 410 WWDC 2018), , . , «Architecture» ( , , ) «Intermediate». , , , - . , , . , .
1: signpost-
Wir möchten unser Tool auf dem Wegweiser aufbauen, dh wir werden unsere Daten über den Wegweiser protokollieren. Alamofire sendet jedes Mal eine Benachrichtigung, wenn eine Anfrage beginnt oder endet. Wir brauchen also nur so etwas 2 :
NotificationCenter.default.addObserver(forName: Notification.Name.Task.DidResume, object: nil, queue: nil) { (notification) in
guard let task = notification.userInfo?[Notification.Key.Task] as? URLSessionTask,
let request = task.originalRequest,
let url = request.url else {
return
}
let signpostId = OSSignpostID(log: networking, object: task)
os_signpost(.begin, log: SignpostLog.networking, name: "Request", signpostID: signpostId, "Request Method %{public}@ to host: %{public}@, path: %@, parameters: %@", request.httpMethod ?? "", url.host ?? "Unknown", url.path, url.query ?? "")
}
NotificationCenter.default.addObserver(forName: Notification.Name.Task.DidComplete, object: nil, queue: nil) { (notification) in
guard let task = notification.userInfo?[Notification.Key.Task] as? URLSessionTask else { return }
let signpostId = OSSignpostID(log: networking, object: task)
let statusCode = (task.response as? HTTPURLResponse)?.statusCode ?? 0
os_signpost(.end, log: SignpostLog.networking, name: "Request", signpostID: signpostId, "Status: %@, Bytes Received: %llu, error: %d, statusCode: %d", "Completed", task.countOfBytesReceived, task.error == nil ? 0 : 1, statusCode)
}
Wenn die Anfrage beginnt, protokollieren wir den Wegweiser
.begin, wenn sie abgeschlossen ist, fügen wir den Wegweiser hinzu .end. Um das Ende des Anrufs mit dem entsprechenden Beginn des Anrufs abzugleichen signpostId, wird sichergestellt, dass das richtige Intervall geschlossen wird, wenn mehrere Anforderungen gleichzeitig auftreten. Idealerweise sollten wir signpostIdim Anforderungsobjekt speichern , um sicherzustellen, dass wir dasselbe für .beginund verwenden .end. Ich wollte den Typ jedoch nicht Requestin Alamofire bearbeiten , daher habe ich beschlossen, OSSignpostID(log:, object:)das ID-Objekt zu verwenden und an ihn zu übergeben. Wir verwenden das Basisobjekt, URLSessionTaskda es in beiden Fällen dasselbe ist, sodass OSSignpostID(log:, object:)wir denselben Bezeichner zurückgeben können, wenn wir es mehrmals aufrufen.
Wir protokollieren Daten mit Formatzeichenfolgen. Sie sollten die beiden Argumente wahrscheinlich immer durch eine genau definierte Zeichenfolge trennen, um das Parsen auf der Werkzeugseite und das Parsen zu vereinfachen. Bitte beachten Sie, dass Sie die Daten im
.endAnruf nicht protokollieren müssen, wenn Sie sie bereits angemeldet haben .begin. Sie werden in einem Intervall zusammengefasst und Sie haben Zugriff darauf.
Schritt 2: Erstellen Sie ein neues benutzerdefiniertes Werkzeugprojekt in Xcode.
Befolgen Sie die Schritte unter Erstellen von benutzerdefinierten Instrumenten (Sitzung 410 von WWDC 2018) oder Hilfe zur Instruments-App - Erstellen eines Toolbox-Projekts , um ein neues Toolbox-Projekt in Xcode zu erstellen. Dadurch erhalten Sie ein grundlegendes Xcode-Projekt mit
.instrpkgeinem. Wir werden dort alle Details angeben.
Schritt 3: Den Rest erledigen
Grundsätzlich befolgen Sie die in der Hilfe zur Instrumenten-App beschriebenen Schritte - Erstellen Sie ein Werkzeug aus Wegweiserdaten . Obwohl die Beschreibungen aller Schritte hier korrekt sind, fehlen ihnen dennoch viele Details. Daher ist es am besten, ein Beispiel für ein echtes benutzerdefiniertes Tool vor sich zu haben. Sie können meine hier ansehen . Grundsätzlich benötigen Sie die folgenden Teile:
Schema
Hier erfahren Sie, wie Sie die Daten aus Ihren Wegweiserzeigern in Variablen analysieren, die Sie verwenden können. Sie definieren eine Vorlage, die Variablen aus Ihren Protokollnachrichten extrahiert und auf Spalten verteilt.
<os-signpost-interval-schema>
<id>org-alamofire-networking-schema</id>
<title>Alamofire Networking Schema</title>
<subsystem>"org.alamofire"</subsystem>
<category>"networking"</category>
<name>"Request"</name>
<start-pattern>
<message>"Request Method " ?http-method " to host: " ?host ", path: " ?url-path ", parameters: " ?query-parameters</message>
</start-pattern>
<end-pattern>
<message>"Status: " ?completion-status ", Bytes Received: " ?bytes-received ", error: " ?errored ", statusCode: " ?http-status-code</message>
</end-pattern>
<column>
<mnemonic>column-http-method</mnemonic>
<title>HTTP Method</title>
<type>string</type>
<expression>?http-method</expression>
</column>
<!-- -->
</os-signpost-interval-schema>
mnemonicist die Kennung, auf die Sie später in dieser Spalte verweisen werden. Aus irgendeinem Grund fand ich es etwas seltsam, die Spalten wie Variablen zu benennen, deshalb habe ich ihnen ein Präfix vorangestellt column. Soweit ich weiß, besteht jedoch keine Notwendigkeit, dies zu tun.
Werkzeug
Werkzeug besteht aus einer grundlegenden Definition:
<instrument>
<id>org.alamofire.networking.instrument</id>
<title>Alamofire</title>
<category>Behavior</category>
<purpose>Trace HTTP calls made via Alamofire, grouped by method, host, path, etc.</purpose>
<icon>Network</icon>
<create-table>
<id>alamofire-requests</id>
<schema-ref>org-alamofire-networking-schema</schema-ref>
</create-table>
<!-- -->
</instrument>
Es ist ziemlich einfach. Die meisten dieser Felder sind Freiformtext oder beziehen sich auf Materialien, die Sie zuvor definiert haben (
schema-ref). Es kann aber categoryauch iconnur ein kleiner Satz von Werten hier und hier definiert werden .
Diagramm innerhalb eines Werkzeugs
Ein Diagramm definiert den grafischen Teil der Benutzeroberfläche des Werkzeugs, die visuelle Darstellung, die Sie im Spurbereich sehen. Es sieht ungefähr so aus:
<instrument>
<!-- -->
<graph>
<title>HTTP Requests</title>
<lane>
<title>the Requests</title>
<table-ref>alamofire-requests</table-ref>
<plot-template>
<instance-by>column-host</instance-by>
<label-format>%s</label-format>
<value-from>column-url-path</value-from>
<color-from>column-response</color-from>
<label-from>column-url-path</label-from>
</plot-template>
</lane>
</graph>
<!-- -->
</instrument>
Sie können eine andere Spur haben und die Plot-Vorlage verwenden, um eine dynamische Anzahl von Plots pro Spur zu implementieren. Mein Beispiel enthält ein Beispiel für ein einfaches Diagramm . Ich bin mir nicht sicher warum
graphund lanehabe Titel. Darüber hinaus plot-templateerhält jedes Diagramm eine Beschriftung von label-format.
Liste, Aggregation oder was auch immer für eine detaillierte Ansicht
Mit nur einem Diagramm würden die Werkzeuge etwas unvollständig aussehen. Sie möchten auch etwas in der Detailansicht anzeigen. Sie können dies tun , mit
list, aggregationoder narrative. Möglicherweise gibt es noch mehr Optionen, die ich noch nicht getroffen habe. Die Aggregation sieht ungefähr so aus:
<instrument>
<!-- -->
<aggregation>
<title>Summary: Completed Requests</title>
<table-ref>alamofire-requests</table-ref>
<slice>
<column>column-completion-status</column>
<equals><string>Completed</string></equals>
</slice>
<hierarchy>
<level>
<column>column-host</column>
</level>
<level>
<column>column-url-path</column>
</level>
</hierarchy>
<column><count/></column>
<column><average>duration</average></column>
<column><max>duration</max></column>
<column><sum>column-size</sum></column>
<column><average>column-size</average></column>
</aggregation>
<!-- -->
</instrument>
Die Liste sieht folgendermaßen aus:
<instrument>
<!-- -->
<list>
<title>List: Requests</title>
<table-ref>alamofire-requests</table-ref>
<column>start</column>
<column>duration</column>
<column>column-host</column>
<!-- ->
</list>
<!-- -->
</instrument>
Bonusmaterial
Das ist in der Tat alles. Sie haben jedoch nicht viel mehr getan, als im WWDC-Video beschrieben, und ich habe versprochen, einige der Lücken zu schließen.
Mein Beispielwerkzeug enthält noch ein paar nette Dinge.
Ein kleiner CLIPS- Ausdruck zum Färben des Intervalls, je nachdem, ob die Anforderung erfolgreich war oder nicht . Farbwerte finden Sie in der Instruments Engineering Type Reference .
Mit einer Diagrammvorlage können Sie mehrere Diagramme auf einem Streifen anzeigen oder beispielsweise ein Diagramm pro Host erstellen, wie in meinem Beispiel. Sie können jedoch mehr als eine Hierarchieebene haben und dem Benutzer erlauben, Teile zu erweitern oder zu reduzieren. Dazu müssen Sie ein Element verwenden
<engineering-type-track>,Definieren Sie Ihre Hierarchie und fügen Sie dann (Erweiterung) für verschiedene Hierarchieebenen hinzu, um Diagramme und Detailansichten hinzuzufügen . Vergessen Sie auch nicht, Add-Ons im jeweiligen Tool zu aktivieren .
Weitere Maßnahmen
Wenn Sie von den vorherigen Links noch nicht darauf gestoßen sind, gibt es tatsächlich eine vollständige Hilfe für alles, was Sie in die
.instrpkgDatei einfügen können. Beispielsweise erfahren Sie, welche Elemente <instrument>oder welche Symbole Sie für Ihr Werkzeug auswählen können. Ein wichtiger Punkt: Ordnung ist wichtig. So zum Beispiel in <instrument>, <title>muss erscheint früher als <category>sonst die Beschreibung wird ungültig.
Überprüfen Sie die Erstellung benutzerdefinierter Tools (Sitzung 410 von WWDC 2018) erneut, um die Details zu notieren, die Sie möglicherweise benötigen. Es gibt auch Beispielcode aus der WWDC 2019-Sitzung, in der ich ein Anwendungsbeispiel gefunden habe
<engineering-type-track>.
CLIPS ist die Sprache, in der benutzerdefinierte Modellierer geschrieben werden (Modellierer - das wird hier nicht behandelt), sie kann jedoch auch für kurze Ausdrücke bei Spaltendeklarationen verwendet werden. Die Sprachdokumentation ist viel umfangreicher als Sie benötigen. Die Hauptsache, die Sie wahrscheinlich wissen müssen, um einen einfachen Ausdruck zu schreiben, ist, dass CLIPS die Präfixnotation verwendet, sodass
?a + ?bSie stattdessen schreiben müssen (+ ?a ?b).
Weitere Artikel zu benutzerdefinierten Tools
Igor über das Erstellen von benutzerdefinierten Toolboxen in Xcode
Debuggen
Es ist immer eine gute Idee, das Tool
XCodeos_signpostzu Ihrem Tracedokument hinzuzufügen . Auf diese Weise können Sie überprüfen, ob Ihre Daten korrekt protokolliert wurden und ob Ihr Tool sie richtig interpretiert hat, wenn etwas nicht wie erwartet funktioniert.
Was ich noch nicht herausgefunden habe
- Verwendung der Werte, die Instruments standardmäßig bereitstellt und in der Benutzeroberfläche (z. B. Dauer) in Ausdrücken für Spaltendefinitionen anzeigt (z. B. Erstellen einer Baudratenspalte durch Teilen der empfangenen Bytes durch die Dauer).
- So zeigen Sie alles im zusätzlichen Detailbereich an. Es fühlt sich an, als wäre es nur für den Call-Stack. Ich möchte zum Beispiel den JSON-Text der ausgewählten Anforderung anzeigen, habe jedoch kein Beispiel gefunden, das dies verdeutlichen würde.
Was dieses Tool kann
Arbeiten noch im Gange
Laden Sie es herunter und überzeugen Sie sich selbst.
Fußnoten
- Okay, es gab tatsächlich andere Gründe.
- Der vollständige Code für die Protokollierung in meinem Beispiel befindet sich in der Datei Logger.swift . Es wird für Alamofire 4.8 angenommen, da dies in der aktuellen Version der Wordpress iOS-App verwendet wird, obwohl Alamofire 5 zum Zeitpunkt des Schreibens bereits veröffentlicht ist. Aufgrund der Benachrichtigungen ist dieser Protokollierungscode einfach hinzuzufügen, ohne Alamofire selbst zu ändern. Wenn Sie jedoch über eine benutzerdefinierte Netzwerkbibliothek verfügen, ist es möglicherweise einfacher, der Bibliothek selbst einen Eintrag hinzuzufügen, um auf weitere Details zuzugreifen.
Ein schneller Einstieg in die iOS-Entwicklung (kostenloses Webinar)