Verwendung von Winston zum Protokollieren von Node.js-Anwendungen

Einführung

Eine effektive Protokollierungslösung ist entscheidend für den Erfolg jeder Anwendung. In diesem Handbuch konzentrieren wir uns auf ein Protokollierungspaket namensWinston, eine äußerst vielseitige Protokollierungsbibliothek und die beliebteste Protokollierungslösung, die fürNode.js-Anwendungen verfügbar ist, basierend auf NPM-Download-Statistiken. Zu den Funktionen von Winston gehören die Unterstützung mehrerer Speicheroptionen und Protokollebenen, Protokollabfragen und sogar ein integrierter Profiler. In diesem Tutorial erfahren Sie, wie Sie mit Winston eine Node / http: //expressjs.com/ [Express] -Anwendung protokollieren, die wir im Rahmen dieses Vorgangs erstellen. Wir werden uns auch ansehen, wie wir Winston mit einem anderen beliebten HTTP-Anforderungs-Middleware-Logger für Node.js namensMorgan kombinieren können, um HTTP-Anforderungsdatenprotokolle mit anderen Informationen zu konsolidieren.

Nach Abschluss dieses Tutorials wird auf einem Ubuntu-Server eine kleine Node / Express-Anwendung ausgeführt. Sie haben auch Winston implementiert, um Fehler und Meldungen in einer Datei und auf der Konsole zu protokollieren.

Voraussetzungen

Bevor Sie mit diesem Handbuch beginnen, benötigen Sie Folgendes:

Mit diesen Voraussetzungen können wir unsere Anwendung erstellen und Winston installieren.

[[Schritt-1 - Erstellen einer Basis-Knoten-Express-App]] == Schritt 1 - Erstellen einer Basis-Knoten- / Express-App

Eine häufige Verwendung von Winston ist das Protokollieren von Ereignissen aus Webanwendungen, die mit Node.js erstellt wurden. Um die vollständige Integration von Winston zu demonstrieren, erstellen wir eine einfache Node.js-Webanwendung unter Verwendung des Express-Frameworks. Um eine einfache Webanwendung zum Laufen zu bringen, verwenden wirexpress-generator, ein Befehlszeilentool, mit dem eine Node / Express-Webanwendung schnell ausgeführt werden kann. Da wirNode Package Manager als Teil unserer Voraussetzungen installiert haben, können wir den Befehlnpm verwenden, umexpress-generator zu installieren. Wir werden auch das Flag-g verwenden, mit dem das Paket global installiert wird, sodass es als Befehlszeilenprogramm außerhalb eines vorhandenen Knotenprojekts / -moduls verwendet werden kann. Installieren Sie das Paket mit dem folgenden Befehl:

sudo npm install express-generator -g

Wennexpress-generator installiert ist, können wir unsere App mit dem Befehlexpress erstellen, gefolgt vom Namen des Verzeichnisses, das wir für unser Projekt verwenden möchten. Dadurch wird unsere Anwendung mit allem erstellt, was wir für den Einstieg benötigen:

express myApp

Installieren Sie als NächstesNodemon, wodurch die Anwendung automatisch neu geladen wird, wenn Änderungen vorgenommen werden. Eine Node.js-Anwendung muss jedes Mal neu gestartet werden, wenn Änderungen am Quellcode vorgenommen werden, damit diese Änderungen wirksam werden. Nodemon sucht automatisch nach Änderungen und startet die Anwendung für uns neu. Und da wirnodemon als Befehlszeilentool verwenden möchten, installieren wir es mit dem Flag-g:

sudo npm install nodemon -g

Um die Einrichtung der Anwendung abzuschließen, wechseln Sie in das Anwendungsverzeichnis und installieren Sie die Abhängigkeiten wie folgt:

cd myApp
npm install

Standardmäßig werden mitexpress-generator erstellte Anwendungen auf Port 3000 ausgeführt. Daher müssen wir sicherstellen, dass der Port nicht von der Firewall blockiert wird. Führen Sie den folgenden Befehl aus, um Port 3000 zu öffnen:

sudo ufw allow 3000

Wir haben jetzt alles, was wir zum Starten Ihrer Webanwendung benötigen. Führen Sie dazu den folgenden Befehl aus:

nodemon bin/www

Dadurch wird die Anwendung gestartet, die auf Port 3000 ausgeführt wird. Wir können testen, ob es funktioniert, indem wir in einem Webbrowser aufhttp://your_server_ip:3000 gehen. Sie sollten so etwas sehen:

Default express-generator homepage

An diesem Punkt ist es eine gute Idee, eine zweite SSH-Sitzung auf Ihrem Server zu starten, die für den Rest dieses Lernprogramms verwendet wird, wobei die Webanwendung, die wir gerade in der ursprünglichen Sitzung ausgeführt haben, erhalten bleibt. In diesem Artikel wird auf die bisher verwendete SSH-Sitzung verwiesen, in der die Anwendung derzeit als Sitzung A ausgeführt wird. Wir werden die neue SSH-Sitzung zum Ausführen von Befehlen und Bearbeiten von Dateien verwenden. Diese Sitzung wird als Sitzung B bezeichnet. Sofern nicht anders angegeben, sollten alle verbleibenden Befehle in Sitzung B ausgeführt werden.

Schritt 2 - Anpassen der Node.js-Anwendung

Die vonexpress-generator erstellte Standardanwendung leistet hervorragende Arbeit beim Einstieg und enthält sogar die Morgan-HTTP-Protokollierungs-Middleware, mit der wir Daten zu allen HTTP-Anforderungen protokollieren. Und da Morgan Ausgabestreams unterstützt, ist es eine gute Kombination mit der in Winston integrierten Stream-Unterstützung, die es uns ermöglicht, HTTP-Anforderungsdatenprotokolle mit allen anderen Protokollen zu konsolidieren, die wir mit Winston erstellen.

Standardmäßig verwendet das Boilerplate vonexpress-generatordie Variablelogger, wenn auf das Paketmorgan verwiesen wird. Da wirmorgan undwinston verwenden, die beide Protokollierungspakete sind, kann es verwirrend sein, eines von ihnenlogger aufzurufen. Ändern Sie dies, indem Sie die Dateiapp.jsim Stammverzeichnis des Projekts bearbeiten und einige Änderungen vornehmen.

Verwenden Sie den Befehlnano, umapp.js zum Bearbeiten zu öffnen:

nano ~/myApp/app.js

Suchen Sie die folgende Zeile oben in der Datei:

~/myApp/app.js

...
var logger = require('morgan');
...

Ändern Sie es wie folgt:

~/myApp/app.js

...
var morgan = require('morgan');
...

Wir müssen auch herausfinden, wo auf die Variablelogger in der Datei verwiesen wurde, und sie inmorgan ändern. Während wir gerade dabei sind, ändern wir das vommorgan-Paket verwendete Protokollformat incombined. Dies ist das Standard-Apache-Protokollformat und enthält nützliche Informationen in den Protokollen, z. B. die Remote-IP-Adresse und den Benutzer -agent HTTP-Anforderungsheader.

Suchen Sie dazu die folgende Zeile:

~/myApp/app.js

...
app.use(logger('dev'));
...

Ändern Sie es wie folgt:

~/myApp/app.js

...
app.use(morgan('combined'));
...

Diese Änderungen helfen uns dabei, das Protokollierungspaket, auf das wir verweisen, zu einem bestimmten Zeitpunkt nach der Integration unserer Winston-Konfiguration besser zu verstehen.

Beenden und speichern Sie die Datei, indem SieCTRL-X, dannY und dannENTER eingeben.

Jetzt, da unsere App eingerichtet ist, können wir mit Winston arbeiten.

[[Schritt 3 - Installieren und Konfigurieren von Winston]] == Schritt 3 - Installieren und Konfigurieren von Winston

Jetzt können Sie Winston installieren und konfigurieren. In diesem Schritt werden einige der Konfigurationsoptionen untersucht, die als Teil des Paketswinstonverfügbar sind, und ein Logger erstellt, der Informationen in einer Datei und der Konsole protokolliert.

Führen Sie den folgenden Befehl aus, umwinston zu installieren:

cd ~/myApp
npm install winston

Es ist oft nützlich, alle Arten von Support- oder Dienstprogrammkonfigurationsdateien für unsere Anwendungen in einem speziellen Verzeichnis zu speichern. Erstellen Sie daher einen Ordnerconfig, der die Konfigurationwinstonenthält:

mkdir ~/myApp/config

Erstellen wir nun die Datei, die unserewinston-Konfiguration enthält, die wirwinston.js nennen:

touch ~/myApp/config/winston.js

Erstellen Sie als Nächstes einen Ordner, der Ihre Protokolldateien enthält:

mkdir ~/myApp/logs

Zum Schluss installieren wirapp-root-path, ein Paket, das beim Angeben von Pfaden in Node.js hilfreich ist. Dieses Paket steht nicht in direktem Zusammenhang mit Winston, hilft jedoch immens bei der Angabe von Pfaden zu Dateien in Node.js Code. Wir werden es verwenden, um den Speicherort der Winston-Protokolldateien im Stammverzeichnis des Projekts anzugeben und die hässliche relative Pfadsyntax zu vermeiden:

npm install app-root-path --save

Alles, was wir zur Konfiguration unserer Protokollierung benötigen, ist vorhanden, sodass wir mit der Definition unserer Konfigurationseinstellungen fortfahren können. Öffnen Sie zunächst~/myApp/config/winston.js zur Bearbeitung:

 nano ~/myApp/config/winston.js

Als nächstes benötigen Sie die Paketeapp-root-path undwinston:

~/myApp/config/winston.js

var appRoot = require('app-root-path');
var winston = require('winston');

Mit diesen Variablen können wir die Konfigurationseinstellungen für unseretransports definieren. Transporte sind ein von Winston eingeführtes Konzept, das sich auf die für die Protokolle verwendeten Speicher- / Ausgabemechanismen bezieht. Winston wird mit drei Kerntransporten geliefert:console,file undHTTP. In diesem Lernprogramm konzentrieren wir uns auf den Transport von Konsolen und Dateien: Der Transport von Konsolen protokolliert Informationen in der Konsole und der Transport von Dateien protokolliert Informationen in einer angegebenen Datei. Jede Transportdefinition kann eigene Konfigurationseinstellungen wie Dateigröße, Protokollebenen und Protokollformat enthalten. Hier ist eine kurze Zusammenfassung der Einstellungen, die wir für jeden Transport verwenden:

  • level - Ebene der zu protokollierenden Nachrichten.

  • filename - Die Datei, in die Protokolldaten geschrieben werden sollen.

  • handleExceptions - Nicht behandelte Ausnahmen abfangen und protokollieren.

  • json - Zeichnet Protokolldaten im JSON-Format auf.

  • maxsize - Maximale Größe der Protokolldatei in Byte, bevor eine neue Datei erstellt wird.

  • maxFiles - Begrenzen Sie die Anzahl der Dateien, die erstellt werden, wenn die Größe der Protokolldatei überschritten wird.

  • colorize - Färben Sie die Ausgabe ein. Dies kann hilfreich sein, wenn Sie sich Konsolenprotokolle ansehen.

Logging levels geben die Nachrichtenpriorität an und werden durch eine Ganzzahl gekennzeichnet. Winston verwendet die Protokollierungsstufen vonnpm, die von 0 bis 5 (am höchsten bis am niedrigsten) priorisiert sind:

  • 0: Fehler

  • 1: warnen

  • 2: info

  • 3: ausführlich

  • 4: Debug

  • 5: dumm

Wenn Sie eine Protokollierungsstufe für einen bestimmten Transport angeben, wird alles auf dieser oder einer höheren Stufe protokolliert. Wenn Sie beispielsweise eine Ebene voninfo angeben, wird alles auf Ebeneerror,warn oderinfo protokolliert. Beim Aufruf des Loggers werden Protokollebenen angegeben. Dies bedeutet, dass wir Folgendes tun können, um einen Fehler aufzuzeichnen:logger.error('test error message').

Wir können die Konfigurationseinstellungen für die Transportefile undconsole in der Konfigurationwinston wie folgt definieren:

~/myApp/config/winston.js

...
var options = {
  file: {
    level: 'info',
    filename: `${appRoot}/logs/app.log`,
    handleExceptions: true,
    json: true,
    maxsize: 5242880, // 5MB
    maxFiles: 5,
    colorize: false,
  },
  console: {
    level: 'debug',
    handleExceptions: true,
    json: false,
    colorize: true,
  },
};

Instanziieren Sie als Nächstes einen neuenwinston-Logger mit Datei- und Konsolentransporten mithilfe der in der Variablenoptions definierten Eigenschaften:

~/myApp/config/winston.js

...
var logger = new winston.Logger({
  transports: [
    new winston.transports.File(options.file),
    new winston.transports.Console(options.console)
  ],
  exitOnError: false, // do not exit on handled exceptions
});

Standardmäßig werdenmorgan nur an die Konsole ausgegeben. Definieren Sie daher eine Stream-Funktion, mit dermorgan-generierte Ausgaben in diewinston-Protokolldateien übertragen werden können. Wir werden deninfo-Pegel verwenden, damit die Ausgabe von beiden Transporten (Datei und Konsole) erfasst wird:

~/myApp/config/winston.js

...
logger.stream = {
  write: function(message, encoding) {
    logger.info(message);
  },
};

Exportieren Sie schließlich den Logger, damit er in anderen Teilen der Anwendung verwendet werden kann:

~/myApp/config/winston.js

...
module.exports = logger;

Die fertige Konfigurationsdatei vonwinstonollte folgendermaßen aussehen:

~/myApp/config/winston.js

var appRoot = require('app-root-path');
var winston = require('winston');

// define the custom settings for each transport (file, console)
var options = {
  file: {
    level: 'info',
    filename: `${appRoot}/logs/app.log`,
    handleExceptions: true,
    json: true,
    maxsize: 5242880, // 5MB
    maxFiles: 5,
    colorize: false,
  },
  console: {
    level: 'debug',
    handleExceptions: true,
    json: false,
    colorize: true,
  },
};

// instantiate a new Winston Logger with the settings defined above
var logger = new winston.Logger({
  transports: [
    new winston.transports.File(options.file),
    new winston.transports.Console(options.console)
  ],
  exitOnError: false, // do not exit on handled exceptions
});

// create a stream object with a 'write' function that will be used by `morgan`
logger.stream = {
  write: function(message, encoding) {
    // use the 'info' log level so the output will be picked up by both transports (file and console)
    logger.info(message);
  },
};

module.exports = logger;

Beenden und speichern Sie die Datei.

Wir haben jetzt unseren Logger konfiguriert, aber unsere Anwendung weiß immer noch nichts davon oder weiß nicht, wie sie es verwenden soll. Wir werden nun den Logger in die Anwendung integrieren.

[[Schritt 4 - Integration von Winston in unsere Anwendung] == Schritt 4 - Integration von Winston in unsere Anwendung

Damit unser Logger mit der Anwendung funktioniert, müssen wirexpressdarauf aufmerksam machen. Wir haben bereits in Schritt 2 gesehen, dass sich unsereexpress-Konfiguration inapp.js befindet. Importieren wir also unseren Logger in diese Datei. Öffnen Sie die Datei zum Bearbeiten, indem Sie Folgendes ausführen:

nano ~/myApp/app.js

Importieren Siewinston am Anfang der Datei mit den anderen erforderlichen Anweisungen:

~/myApp/app.js

...
var winston = require('./config/winston');
...

Der erste Ort, an dem wirwinston verwenden, istmorgan. Wir werden die Optionstream verwenden und sie auf die Stream-Schnittstelle setzen, die wir im Rahmen der Konfiguration vonwinstonerstellt haben. Suchen Sie dazu die folgende Zeile:

~/myApp/app.js

...
app.use(morgan('combined'));
...

Ändern Sie es zu diesem:

~/myApp/app.js

...
app.use(morgan('combined', { stream: winston.stream }));
...

Beenden und speichern Sie die Datei.

Wir sind bereit, einige Protokolldaten anzuzeigen! Wenn Sie die Seite im Webbrowser neu laden, sollte in der Konsole von SSH-Sitzung A Folgendes angezeigt werden:

Output[nodemon] restarting due to changes...
[nodemon] starting `node bin/www`
info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:29:36 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"

info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:29:37 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://167.99.4.120:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"

Hier gibt es zwei Protokolleinträge - den ersten für die Anforderung der HTML-Seite und den zweiten für das zugehörige Stylesheet. Da jeder Transport so konfiguriert ist, dass er Protokolldaten aufinfo-Ebene verarbeitet, sollten ähnliche Informationen auch im Dateitransport bei~/myApp/logs/app.log angezeigt werden. Die Ausgabe im Dateitransport sollte jedoch als JSON-Objekt geschrieben werden, da wirjson: true in der Dateitransportkonfiguration angegeben haben. Weitere Informationen zu JSON finden Sie in unserenintroduction to JSON tutorial. Führen Sie den folgenden Befehl aus, um den Inhalt der Protokolldatei anzuzeigen:

tail ~/myApp/logs/app.log

Sie sollten etwas Ähnliches wie das Folgende sehen:

{"level":"info","message":"::ffff:72.80.124.207 - - [07/Mar/2018:17:29:36 +0000] \"GET / HTTP/1.1\" 304 - \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\"\n","timestamp":"2018-03-07T17:29:36.962Z"}
{"level":"info","message":"::ffff:72.80.124.207 - - [07/Mar/2018:17:29:37 +0000] \"GET /stylesheets/style.css HTTP/1.1\" 304 - \"http://167.99.4.120:3000/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\"\n","timestamp":"2018-03-07T17:29:37.067Z"}

Bisher zeichnet unser Logger nur HTTP-Anfragen und zugehörige Daten auf. Dies sind sehr wichtige Informationen in unseren Protokollen, aber wie zeichnen wir benutzerdefinierte Protokollnachrichten auf? Es wird sicherlich Zeiten geben, in denen wir diese Funktion beispielsweise für das Aufzeichnen von Fehlern oder das Erstellen von Profilen für die Datenbank-Abfrageleistung benötigen. Um zu veranschaulichen, wie wir dies tun können, rufen wir den Logger über die Fehlerbehandlungsroutine auf.

Dasexpress-generator-Paket enthält standardmäßig eine 404- und eine 500-Fehlerbehandlungsroute. Wir werden also damit arbeiten. Öffnen Sie die Datei~/myApp/app.js:

nano ~/myApp/app.js

Suchen Sie den Codeblock am Ende der Datei, der folgendermaßen aussieht:

~/myApp/app.js

...
// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});
...

Dies ist die letzte Fehlerbehandlungsroute, die letztendlich eine Fehlerantwort an den Client zurücksendet. Da alle serverseitigen Fehler über diese Route ausgeführt werden, ist dies ein guter Ort, um denwinston-Logger einzuschließen.

Da es sich jetzt um Fehler handelt, möchten wir die Protokollstufeerrorverwenden. Wiederum sind beide Transporte so konfiguriert, dass Nachrichten auferror-Ebene protokolliert werden, sodass die Ausgabe in den Konsolen- und Dateiprotokollen angezeigt wird. Wir können alles, was wir wollen, in das Protokoll aufnehmen. Stellen Sie daher sicher, dass Sie einige nützliche Informationen wie die folgenden angeben:

  • err.status - Der HTTP-Fehlerstatuscode. Wenn noch keine vorhanden ist, wird standardmäßig 500 verwendet.

  • err.message - Details des Fehlers.

  • req.originalUrl - Die angeforderte URL.

  • req.path - Der Pfadteil der Anforderungs-URL.

  • req.method - HTTP-Methode der Anforderung (GET, POST, PUT usw.).

  • req.ip - Remote-IP-Adresse der Anforderung.

Aktualisieren Sie die Fehlerbehandlungsroutine so, dass sie den folgenden Bedingungen entspricht:

~/myApp/app.js

...
// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // add this line to include winston logging
  winston.error(`${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});
...

Beenden und speichern Sie die Datei.

Um dies zu testen, versuchen wir, auf eine Seite in unserem Projekt zuzugreifen, die nicht vorhanden ist und einen 404-Fehler auslöst. Versuchen Sie in Ihrem Webbrowser, die folgende URL zu laden:http://your_server_ip:3000/foo. Die Anwendung ist dank der vonexpress-generator erstellten Boilerplate bereits so eingerichtet, dass sie auf einen solchen Fehler reagiert. Ihr Browser sollte eine Fehlermeldung anzeigen, die so aussieht (Ihre Fehlermeldung ist möglicherweise detaillierter als die angezeigte):

Browser error message

Schauen Sie sich die Konsole in SSH-Sitzung A noch einmal an. Es sollte einen Protokolleintrag für den Fehler geben, und dank der Farbeinstellung sollte er leicht zu finden sein.

Output[nodemon] starting `node bin/www`
error: 404 - Not Found - /foo - GET - ::ffff:72.80.124.207
info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:40:11 +0000] "GET /foo HTTP/1.1" 404 985 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"

info: ::ffff:72.80.124.207 - - [07/Mar/2018:17:40:11 +0000] "GET /stylesheets/style.css HTTP/1.1" 304 - "http://167.99.4.120:3000/foo" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"

Beim erneuten Ausführen des Befehlstailfür den Dateiprotokollierer sollten die neuen Protokolldatensätze angezeigt werden:

tail ~/myApp/logs/app.log

Sie sehen eine Nachricht wie die folgende:

{"level":"error","message":"404 - Not Found - /foo - GET - ::ffff:72.80.124.207","timestamp":"2018-03-07T17:40:10.622Z"}

Die Fehlermeldung enthält alle Daten, die wirwinston ausdrücklich angewiesen haben, als Teil der Fehlerbehandlungsroutine zu protokollieren, einschließlich des Fehlerstatus (404 - Nicht gefunden), der angeforderten URL (localhost / foo) und der Anforderungsmethode (GET). , die IP-Adresse, die die Anfrage gestellt hat, und den Zeitstempel, zu dem die Anfrage gestellt wurde.

Fazit

In diesem Lernprogramm haben Sie eine einfache Node.js-Webanwendung erstellt und eine Winston-Protokollierungslösung integriert, die als effektives Tool dient, um einen Einblick in die Leistung der Anwendung zu erhalten. Sie können viel mehr tun, um robuste Protokollierungslösungen für Ihre Anwendungen zu erstellen, insbesondere wenn Ihre Anforderungen komplexer werden. Wir empfehlen Ihnen, sich einige der folgenden Dokumente anzusehen:

  • Weitere Informationen zu Winston-Transporten finden Sie unterWinston Transports Documentation.

  • Weitere Informationen zum Erstellen eigener Transporte finden Sie unterAdding Custom Transports

  • Informationen zum Erstellen eines HTTP-Endpunkts zur Verwendung mit dem HTTP-Kerntransport finden Sie unterwinstond.

  • Informationen zur Verwendung von Winston als Profilierungswerkzeug finden Sie unterProfiling