Ajout de fonctions de journalisation dans une application
Cette rubrique décrit comment utiliser les fonctionnalités fournies par System.IO.Log pour ajouter des fonctions de journalisation à votre application.
Comparaison entre FileRecordSequence et LogRecordSequence
Pour choisir un journal simple basé sur des fichiers et un journal basé sur un système CLFS (Common Log File System), vous devez prendre en compte les quatre critères suivants : prise en charge des plateformes, richesse des fonctionnalités, fiabilité et performances.
Les journaux simples basés sur des fichiers sont disponibles sur tous les plateformes pour lesquelles System.IO.Log est pris en charge, alors que les journaux basés sur un système CLFS sont uniquement disponibles sur les plateformes Windows Server 2003 R2 et Windows Vista. De plus, certaines fonctionnalités CLFS sont désactivées sur Windows Server 2003 R2. Cela peut avoir un impact sur la manière dont votre application gère certaines conditions spécifiques, telles que la croissance automatique. Pour plus d'informations sur de telles limitations, consultez LogRecordSequence.
La stratégie et le multiplexage représentent deux fonctionnalités qui contribuent à la richesse des fonctionnalités de la classe LogRecordSequence basée sur un système CLFS, par opposition à la classe FileRecordSequence simple basée sur des fichiers. La définition des stratégies à l'aide de la structure LogPolicy permet le contrôle très fin sur des fonctionnalités de maintenance, telles que l'accroissement et la réduction automatiques de la taille du journal, les conteneurs d'étendue minimum et maximum et les seuils en attente d'une fin. Ces fonctionnalités permettent également la création de journaux circulaires à l'aide de la classe LogRecordSequence basée sur un système CLFS. Le multiplexage, ou capacité de manipuler des flux de fichiers NTFS, peut permettre d'améliorer les performances et le confort d'utilisation dans une application unique ou dans plusieurs applications travaillant ensemble en tant qu'unité. Par conséquent, les applications conçues pour des scénarios de longue durée ou des performances extrêmement exigeantes bénéficieront davantage de l'utilisation de la classe LogRecordSequence, par opposition à la classe FileRecordSequence.
De plus, le système CLFS présente des avantages en termes de fiabilité et de performance. Le système CLFS est conçu pour être utilisé par des applications hautes performances ou dans un environnement d'entreprise. Si les contraintes d'une application permettent une exécution sur une plateforme prise en charge sur un système CLFS, la classe LogRecordSequence offrira non seulement davantage d'options lors du contrôle de la maintenance du fichier journal via la classe LogPolicy, mais entraînera également une amélioration des performances d'E/S.
Principales classes dans System.IO.Log
Voici les trois classes les plus importantes dans System.IO.Log. Pour plus d'informations sur leur utilisation, consultez la documentation de référence associée.
LogRecordSequence
LogPolicy
Utilisation de System.IO.Log
Ouverture d'un journal et ajout d'étendues
L'ouverture d'un journal et l'ajout d'étendues est en général la première tâche lors de l'ajout de fonctions de journalisation à votre application. Notez que l'ajout d'étendues ne peut se faire que lors de l'utilisation de la classe LogRecordSequence basée sur un système CLFS.
Vous devez considérer l'emplacement du journal, ainsi que le nombre et la taille des étendues à ajouter initialement au journal. L'emplacement de stockage est uniquement limité par le compte d'utilisateur pour lequel l'application s'exécute. Un emplacement valide peut se trouver n'importe où sur le système local dès l'instant où cet utilisateur y a accès en écriture. Le nombre d'étendues et leur taille, toutefois, requièrent de prendre en compte d'autres facteurs plus spécifiques à l'application. Lors de l'ajout de l'étendue initiale au journal, une taille doit être fournie. Cette taille est utilisée pour toutes les étendues supplémentaires ajoutées au journal, soit manuellement soit par le biais du comportement de croissance automatique. En outre, pour tirer parti de des nombreuses fonctionnalités fournies par la classe LogPolicy, deux étendues au moins doivent être présentes à tout moment pour une séquence donnée.
L'exemple suivant montre comment créer un journal et lui ajouter deux conteneurs d'étendue de 1 Mo.
LogRecordSequence recordSequence = new LogRecordSequence("application.log", FileMode.CreateNew);
recordSequence.LogStore.Extents.Add("app_extent0", 1024 * 1024);
recordSequence.LogStore.Extents.Add("app_extent1");
Définition d'une stratégie
La définition de stratégies de journal à l'aide de la structure LogPolicy doit être la seconde tâche dans le développement de votre application de journalisation. Notez que cette tâche ne peut être effectuée que lors de l'utilisation de la classe LogRecordSequence basée sur un système CLFS et qu'elle doit être définie chaque fois qu'un journal est ouvert du fait que les stratégies ne sont pas conservées.
Une stratégie contient des options importantes telles que la croissance automatique et la valeur d'étendue maximale. Tout au long de la durée de vie d'une application, la variation des charges peut entraîner la diminution du jeu initial d'étendues, et éventuellement aboutir à une exception SequenceFullException pendant l'exécution. Pour éviter ce problème, il est souvent préférable de permettre au journal de croître automatiquement pour tenir compte de cette charge supplémentaire de façon transparente. Notez qu'un ajout manuel des étendues pendant une SequenceFullException est pris en charge et peut être utilisé à la place d'une croissance automatique transparente.
Lorsque vous travaillez avec un journal circulaire, vous devez également définir une valeur d'étendue maximale. De plus, la structure LogPolicy offre plusieurs paramètres auxiliaires communs, tels que les paramètres de réduction et de croissance automatiques. La manipulation de ces valeurs peut avoir des effets significatifs sur les performances de l'application et doit être ajustée selon le taux d'E/S à tout journal donné dans le système.
L'exemple suivant montre la définition d'une stratégie de journal pour une croissance automatique jusqu'à un maximum de 100 étendues, avec un agrandissement de 5 étendues à la fois.
recordSequence.LogStore.Policy.AutoGrow = true;
recordSequence.LogStore.Policy.MaximumExtentCount = 100;
recordSequence.LogStore.Policy.GrowthRate = new PolicyUnit(5, PolicyUnitType.Extents);
recordSequence.LogStore.Policy.Commit();
recordSequence.LogStore.Policy.Refresh();
Ajout d'un enregistrement
Vous devez considérer différentes techniques pour fournir des données dans un format reconnu par les méthodes Append.
Les méthodes Append actuelles ont été optimisées pour gérer des tableaux d'octets et des listes de tableaux d'octets. Toutefois, vous pouvez également les utiliser conjointement avec des classes de sérialisation de niveau supérieur. Le format d'enregistrement obtenu doit toujours être reconnu pour exploiter efficacement les fonctionnalités fournies par la classe IRecordSequence. Les éléments suivants montrent un exemple de code de sérialisation de niveau supérieur utilisant des fonctionnalités basées sur DataContractAttribute dans System.Runtime.Serialization.Formatters.
SomeDataContractClass someClassInstance = new SomeDataContractClass(…);
ArraySegment<byte>[] recordData = new ArraySegment<byte>[1];
using (MemoryStream formatStream = new MemoryStream())
{
IFormatter formatter = new NetDataContractSerializer();
formatter.Serialize(formatStream, someClassInstance);
formatStream.Flush();
recordData[0] = new ArraySegment<byte>(formatStream.GetBuffer());
}
recordSequence.Append(recordData, …);
Lorsque vous ajoutez des enregistrements à une séquence d'enregistrement, vous devez considérer avec soin les contraintes d'espace et de temps de votre application. Par exemple, votre application peut exiger qu'une opération d'ajout réussisse uniquement s'il reste suffisamment d'espace dans le journal pour écrire des enregistrements supplémentaires à l'avenir à l'aide de la classe ReservationCollection. Les systèmes de traitement transactionnel basés sur ARIES font généralement référence à de tels enregistrements comme des enregistrements de compensation ou d'annulation, utilisés lors d'une restauration du travail. De la même façon, certains systèmes présentent des règles strictes qui régissent l'heure à laquelle les enregistrements doivent être écrits. La sélection des enregistrements à vider sur disque et ceux pour lesquels un vidage paresseux est autorisé est basée exclusivement sur les spécifications de l'application et les contraintes du système. Ces considérations peuvent avoir un impact à la fois sur les performances et l'exactitude.
Les options d'espace et de temps sont exposées à l'utilisateur par le biais de différentes méthodes Append. L'exemple suivant montre la définition de telles options, en ajoutant trois enregistrements et en garantissant de l'espace pour les enregistrements ultérieurs. Il force uniquement un vidage lors de la dernière opération d'écriture de l'enregistrement.
// Assume recordData is smaller or equal to 1000bytes
// Reserve space in the log for three 1000byte records ReservationCollection reservations = recordSequence.CreateReservationCollection();
reservations.Add(1000);
reservations.Add(1000);
reservations.Add(1000);
// The following three appends are guaranteed to succeed if execution
// flows to here
recordSequence.Append(recordData1,
userSqn,
previousSqn,
RecordAppendOptions.None, // No flush
reservations);
recordSequence.Append(recordData2,
userSqn,
previousSqn,
RecordAppendOptions.None, // No flush
reservations);
recordSequence.Append(recordData3,
userSqn,
previousSqn,
RecordAppendOptions.ForceFlush, // Flush
reservations);
Si la propriété RetryAppend a la valeur true et qu'une opération d'ajout échoue parce qu'aucun espace n'est disponible dans la séquence, la séquence d'enregistrement essaie de libérer de l'espace et réitère l'opération. L'implémentation exacte de libération d'espace varie. Par exemple, la classe LogRecordSequence appelle le moteur de stratégie CLFS, comme décrit dans la section "Libération de l'espace avec TailPinnedEvent" ci-dessous.
Réservation
Les réservations peuvent être exécutées de deux manières, comme le montrent les exemples suivants. Vous pouvez adopter ces pratiques dans les exemples pour un traitement fiable. Notez que cette tâche ne peut être effectuée que lors de l'utilisation de la classe LogRecordSequence basée sur un système CLFS.
Utilisation de la méthode ReserveAndAppend
ReservationCollection reservations = recordSequence.CreateReservationCollection();
long[] lengthOfUndoRecords = new long[] { 1000 };
recordSequence.ReserveAndAppend(recordData,
userSqn,
previousSqn,
RecordSequenceAppendOptions.None,
reservations,
lengthOfUndoRecords);
recordSequence.Append(undoRecordData, // If necessary …
userSqn,
previousSqn,
RecordSequenceAppendOptions.ForceFlush,
reservations);
Utilisation de l'approche manuelle
ReservationCollection reservations = recordSequence.CreateReservationCollection();
reservations.Add(lengthOfUndoRecord);
try
{
recordSequence.Append(recordData, userSqn, previousSqn, RecordAppendOptions.None);
}
catch (Exception)
{
reservations.Remove(lengthOfUndoRecord);
throw;
}
recordSequence.Append(undoRecordData, userSqn, previousSqn, RecordAppendOptions.ForceFlush, reservations);
Lecture du journal
La lecture du journal peut avoir lieu n'importe quand pendant la durée de vie d'une application. Selon la situation, toutefois, il peut être nécessaire de répéter l'opération sur les enregistrements stockés dans le journal dans un ordre différent. En plus des directives standard spécifiées par Next et Previous pour le journal, vous pouvez également itérer l'opération dans un ordre défini par l'utilisateur spécifié lors de l'ajout de chaque enregistrement individuel à la séquence. Notez également que le Previous n'est pas toujours séquentiel en termes de journal physique, puisque l'utilisateur peut spécifier l'enregistrement précédent ajouté par le thread actuel d'exécution pendant une opération Append.
L'exemple suivant lit un journal dans un ordre de transmission séquentiel, en commençant par l'enregistrement ayant "startSqn" comme numéro de séquence.
foreach (LogRecord record in recordSequence.ReadLogRecords(startSqn, LogRecordEnumeratorType.Next))
{
Stream dataStream = record.Data;
// Process dataStream, which can be done using deserialization
}
Libération de l'espace avec TailPinnedEvent
Pour qu'une séquence d'enregistrement puisse libérer de l'espace, elle peut déclencher l'événement TailPinned. Cet événement indique que la fin de la séquence (c'est-à-dire le numéro de séquence de base) doit être avancée pour libérer de l'espace.
L'événement peut être déclenché à n'importe quel moment lorsque la séquence d'enregistrement décide qu'elle doit libérer de l'espace, pour une raison quelconque. Par exemple, le moteur de stratégie CLFS peut décider de déclencher l'événement lorsqu'il détermine que les fins de deux clients de journal qui partagent le même fichier journal sont trop éloignées. La libération de l'espace peut se faire par écriture de zones de reprise ou par troncation du journal et utilisation de la méthode AdvanceBaseSequenceNumber pour supprimer de l'espace. L'exemple suivant illustre la deuxième approche.
recordSequence.RetryAppend = true;
recordSequence.TailPinned += new EventHandler<TailPinnedEventArgs>(HandleTailPinned);
void HandleTailPinned(object sender, TailPinnedEventArgs tailPinnedEventArgs)
{
// tailPinnedEventArgs.TargetSequenceNumber is the target
// sequence number to free up space to.
// However, this sequence number is not necessarily valid. We have
// to use this sequence number as a starting point for finding a
// valid point within the log to advance toward. You need to
// identify a record with a sequence number equal to, or greater
// than TargetSequenceNumber; let's call this
// realTargetSequenceNumber. Once found, move the base
recordSequence.AdvanceBaseSequenceNumber(realTargetSequenceNumber);
}
Vous pouvez également appeler la méthode WriteRestartArea en dehors de l'événement TailPinned pour libérer de l'espace. Une zone de reprise est semblable à un point de contrôle dans d'autres systèmes de traitement de journal. L'appel de cette méthode indique que l'application considère tous les enregistrements antérieurs avant la zone de reprise comme entièrement terminés et utilisables pour de futurs ajouts d'enregistrements. Tout comme pour les autres enregistrements, l'enregistrement écrit par cette méthode requiert de l'espace libre réel dans le journal pour fonctionner.
Multiplexage
Lorsque vous possédez plusieurs applications qui travaillent ensemble en tant qu'unité, vous pouvez utiliser la classe LogRecordSequence basée sur un système CLFS pour manipuler un flux de fichiers NTFS unique. L'exemple suivant montre comment créer un journal multiplexé avec deux flux. L'entrelacement d'opérations de lecture et d'ajout sont effectuées sur les enregistrements du journal.
namespace MyMultiplexLog
{
class MyMultiplexLog
{
static void Main(string[] args)
{
try
{
string myLog = "MyMultiplexLog";
string logStream1 = "MyMultiplexLog::MyLogStream1";
string logStream2 = "MyMultiplexLog::MyLogStream2";
int containerSize = 32 * 1024;
LogRecordSequence sequence1 = null;
LogRecordSequence sequence2 = null;
Console.WriteLine("Creating Multiplexed log with two streams");
// Create log stream 1
sequence1 = new LogRecordSequence(logStream1, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
// Log Extents are shared between the two streams.
// Add two extents to sequence1.
sequence1.LogStore.Extents.Add("MyExtent0", containerSize);
sequence1.LogStore.Extents.Add("MyExtent1");
// Create log stream 2
sequence2 = new LogRecordSequence(logStream2, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
// Start Appending in two streams with interleaving appends.
SequenceNumber previous1 = SequenceNumber.Invalid;
SequenceNumber previous2 = SequenceNumber.Invalid;
Console.WriteLine("Appending interleaving records in stream1 and stream2...");
Console.WriteLine();
// Append two records in stream1
previous1 = sequence1.Append(CreateData("MyLogStream1: Hello World!"), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
previous1 = sequence1.Append(CreateData("MyLogStream1: This is my first Logging App"), previous1, previous1, RecordAppendOptions.ForceFlush);
// Append two records in stream2
previous2 = sequence2.Append(CreateData("MyLogStream2: Hello World!"), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.ForceFlush);
previous2 = sequence2.Append(CreateData("MyLogStream2: This is my first Logging App"), previous2, previous2, RecordAppendOptions.ForceFlush);
// Append the third record in stream1
previous1 = sequence1.Append(CreateData("MyLogStream1: Using LogRecordSequence..."), previous1, previous1, RecordAppendOptions.ForceFlush);
// Append the third record in stream2
previous2 = sequence2.Append(CreateData("MyLogStream2: Using LogRecordSequence..."), previous2, previous2, RecordAppendOptions.ForceFlush);
// Read log records from stream1 and stream2
Encoding enc = Encoding.Unicode;
Console.WriteLine();
Console.WriteLine("Reading Log Records from stream1...");
foreach (LogRecord record in sequence1.ReadLogRecords(sequence1.BaseSequenceNumber, LogRecordEnumeratorType.Next))
{
byte[] data = new byte[record.Data.Length];
record.Data.Read(data, 0, (int)record.Data.Length);
string mystr = enc.GetString(data);
Console.WriteLine(" {0}", mystr);
}
Console.WriteLine();
Console.WriteLine("Reading the log records from stream2...");
foreach (LogRecord record in sequence2.ReadLogRecords(sequence2.BaseSequenceNumber, LogRecordEnumeratorType.Next))
{
byte[] data = new byte[record.Data.Length];
record.Data.Read(data, 0, (int)record.Data.Length);
string mystr = enc.GetString(data);
Console.WriteLine(" {0}", mystr);
}
Console.WriteLine();
// Cleanup...
sequence1.Dispose();
sequence2.Dispose();
LogStore.Delete(myLog);
Console.WriteLine("Done...");
}
catch (Exception e)
{
Console.WriteLine("Exception thrown {0} {1}", e.GetType(), e.Message);
}
}
// Converts the given data to an Array of ArraySegment<byte>
public static IList<ArraySegment<byte>> CreateData(string str)
{
Encoding enc = Encoding.Unicode;
byte[] array = enc.GetBytes(str);
ArraySegment<byte>[] segments = new ArraySegment<byte>[1];
segments[0] = new ArraySegment<byte>(array);
return Array.AsReadOnly<ArraySegment<byte>>(segments);
}
}
}
Gestion des exceptions
Les applications qui utilisent System.IO.Log doivent être préparées à gérer les échecs rapides qui résultent de l'infrastructure. Dans certains scénarios, System.IO.Log n'utilise pas d'exceptions pour communiquer des erreurs à une application. À la place, les exceptions sont utilisées principalement pour les erreurs récupérables sur lesquelles un développeur d'applications peut agir. Toutefois, des échecs rapides se produisent si la poursuite de l'exécution risque d'endommager des fichiers journaux ou de les rendre potentiellement inutilisables. Lorsque des échecs rapides se produisent, aucune autre action d'application n'est disponible pour rectifier le problème et une application doit être prête à prendre fin.
Pour plus d'informations sur les échecs rapides, consultez FailFast.
La plupart des exceptions qui sont levées par System.IO.Log proviennent d'implémentations de journaux internes. Les listes suivantes répertorient quelques-unes des exceptions importantes que vous devez connaître.
SequenceFullException: Cette exception peut être irrécupérable ou non. Si AutoGrow a la valeur true et qu'il existe suffisant d'espace pour permettre une croissance, une exception SequenceFullException peut toujours être levée. Ceci s'explique par le fait que l'implémentation de croissance automatique est une opération fondamentalement asynchrone, non atomique et que le taux de croissance ne peut pas être garanti à chaque fois. Par exemple, si le taux de croissance a pour valeur 100 étendues, il faut parfois beaucoup de temps pour que les 100 étendues soient ajoutées au magasin de journaux. Cela peut provoquer la levée de cette exception par intervalles au cours de ce délai.
Gestionnaire TailPinned : une exception levée dans l'événement TailPinned est signalée à l'implémentation de journal interne. Cela indique l'incapacité d'une application à déplacer le numéro de séquence de base. Une SequenceFullException sera levée, du fait que l'événement TailPinned représente la dernière occasion pour une application d'empêcher des conditions de journal saturé.
ReservationNotFoundException: Cette exception se produit lorsque vous essayez d'ajouter des enregistrements à l'aide d'une collection de réservations et que toutes les réservations de la taille appropriée sont déjà prises.
Voir aussi
Référence
System.IO.Log
LogRecordSequence
FileRecordSequence
Autres ressources
Prise en charge de la journalisation dans System.IO.Log
Envoyer des commentaires sur cette rubrique à Microsoft.
Copyright ©2007 par Microsoft Corporation. Tous droits réservés.