Wenn ein Benutzer ein Problem meldet: Wie kann ein Fehler bei der Nutzung einer bestimmten Online-Funktion schnell und genau lokalisiert werden? Wie kann die Optimierung effektiv verfolgt werden, wenn eine bestimmte Anforderungsschnittstelle Daten langsam zurückgibt?
Wie wir alle wissen, werden bei einer Anfrage wahrscheinlich die folgenden Protokolle generiert:
1. AceesLog: Benutzerzugriffsprotokoll
2. Ausnahme: Code-Ausnahmeprotokoll
3. SQL: SQL-Abfrageprotokoll
4. ThirdParty: Drittanbieter Party-Service
-Protokoll Wie kann ich alle durch eine Anfrage generierten Protokolle verfolgen?
Der allgemeine Ansatz besteht darin, eine Anforderungs-ID als eindeutige Kennung zu verwenden
und dann eine Middleware zu schreiben, um die Anforderungs-ID in den Kontext einzufügen. Wenn eine Protokollierung durchgeführt werden muss, nehmen Sie sie zum Drucken
in Drittanbieterdiensten und SQL-Protokollen
herausSie müssen auch die Anforderungs-ID in den Kontext einfügen. Die Anforderungs-ID wird zum Drucken an die entsprechende Funktion übergeben. Es ist zu mühsam, sie Schicht für Schicht zu übergeben, und der Code ist auch relativ aufdringlich.
Unser Ziel ist es, die Eindringlichkeit des Codes zu reduzieren, ihn einmal einzuschleusen und ihn automatisch zu verfolgen.
Nach der Recherche kann async_hooks den Lebenszyklus des asynchronen Verhaltens verfolgen. In jeder asynchronen Ressource (jede Anforderung ist eine asynchrone Ressource) gibt es zwei IDs,
nämlich asyncId (die aktuelle Lebenszyklus-ID der asynchronen Ressource) und trigerAsyncId (übergeordnete asynchrone Ressource). AUSWEIS).
async_hooks stellt die folgenden Lebenszyklus-Hooks zum Abhören asynchroner Ressourcen bereit:
asyncHook = async_hook.createHook({ //Überwachen Sie die Erstellung asynchroner Ressourcen init(asyncId,type,triggerAsyncId,resource){}, // Bevor die asynchrone Ressourcen-Callback-Funktion mit der Ausführung beginnt before(asyncId){}, //Nachdem die Ausführung der asynchronen Ressourcen-Callback-Funktion begonnen hat, after(asyncId){}, // Überwachen Sie die Zerstörung asynchroner Ressourcen destroy(asyncId){} })
Wenn wir dann eine Zuordnung erstellen, jede asyncId einem Speicher zuordnet und die entsprechende requestId im Speicher speichert, kann die requestId leicht abgerufen werden.
Es ist einfach so, dass die cls-hooked-Bibliothek basierend auf async_hooks gekapselt wurde, wodurch eine Kopie der Daten in derselben asynchronen Ressource verwaltet und in Form von Schlüssel-Wert-Paaren gespeichert wird. (Hinweis: async_hooked muss in einer höheren Version von node>=8.2.1 verwendet werden) Natürlich gibt es in der Community auch andere Implementierungen, wie cls-session, node-continuation-local-storage usw.
Lassen Sie uns über ein Beispiel für die Verwendung von cls-hooked in meinem Projekt sprechen:
/session.js Erstellen Sie einen benannten Speicherplatz
const createNamespace = require('cls-hooked').createNamespace const session = createNamespace('requestId-store') module.exports = session
/logger.js print log
const session = require('./session') module.exports = { info: (Nachricht) => { const requestId = session.get('requestId') console.log(`requestId:${requestId}`, Nachricht) }, Fehler: (Nachricht) => { const requestId = session.get('requestId') console.error(`requestId:${requestId}`, Nachricht) } }
/sequelize.js SQL ruft Logger auf, um Protokolle zu drucken
const logger = require("./logger") newSequelize( Protokollierung: Funktion (SQL, Kostenzeit) { logger.error( `sql exe : ${sql} | costtime ${costtime} ms` ); } )
/app.js Set requestId, set requestId, um den Antwortheader zurückzugeben, und drucke das Zugriffsprotokoll
const session = require('./session') const logger = require('./logger') asynchrone Funktion accessHandler(ctx, next) { const requestId = ctx.header['x-request-id'] ||. const params = ctx.request.body ? JSON.stringify(ctx.request.body) : JSON.stringify(ctx.request.query) //RequestId festlegen session.run(() => { session.set('requestId', requestId) logger.info(`url:${ctx.request.path}; params:${params}`) next() //Rückantwort-Header festlegen ctx.res.setHeader('X-Request-Id',requestId) }) }
Sehen wir uns das Protokoll an, wenn ein Anforderungspfad /home?a=1 lautet:
Zugriffsprotokoll: requestId:79f422a6-6151-4bfd-93ca-3c6f892fb9ac url:/home;params:{"a":"1"} SQL-Protokoll: requestId:79f422a6-6151-4bfd-93ca-3c6f892fb9ac SQL-Exe: Ausgeführt (Standard): SELECT `id` FROM t_user
Sie können sehen, dass die Protokollanforderungs-ID des gesamten Links für dieselbe Anforderung dieselbe ist. Wenn später ein Alarm an die Alarmplattform gesendet wird, können wir anhand der requestId den gesamten von dieser Anfrage ausgeführten Link finden.
Aufmerksame Schüler bemerken möglicherweise, dass ich die Anforderungs-ID auch im von der Schnittstelle zurückgegebenen Antwortheader festgelegt habe. Der Zweck besteht darin, dass die Anforderungs-ID direkt vom Browser erkannt und analysiert werden kann.
Ich habe lokal einen Stresstest durchgeführt.
Dies ist der Vergleich der Speichernutzung:
Ungefähr 10 % mehr als ohne Verwendung von async_hook.
Für unser QPS-System mit 100-Level-System ist das in Ordnung, aber wenn es sich um einen Dienst mit hoher Parallelität handelt, müssen wir dies möglicherweise sorgfältig prüfen.
ps: Wenn es Fehler gibt, weisen Sie sie bitte darauf hin. Wenn Ihnen diese nicht gefallen, kommentieren Sie sie bitte nicht.