Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Unter diesem Thema wird beschrieben, wie Sie mit den Funktionen von System.IO.Log Protokollierfunktionen für Ihre Anwendung hinzufügen können.
FileRecordSequence im Gegensatz zu LogRecordSequence
Für die Auswahl eines einfachen dateibasierten Protokolls und eines Common Log File System (CLFS)-basierten Protokolls müssen Sie die folgenden vier Kriterien berücksichtigen: Plattformunterstützung, Funktionsauswahl, Stabilität und Leistung.
Einfache dateibasierte Protokolle stehen auf allen Plattformen zur Verfügung, für die System.IO.Log unterstützt wird, wobei CLFS-basierte Protokolle nur für Windows Server 2003 R2- und Windows Vista-Plattformen verfügbar sind. Darüber hinaus sind einige CLFS-Funktionen für Windows Server 2003 R2 deaktiviert. Dies kann Auswirkungen auf die Behandlung bestimmter Bedingungen durch Ihre Anwendung haben, wie zum Beispiel die automatische Wachstumsbehandlung. Weitere Informationen zu solchen Beschränkungen finden Sie unter LogRecordSequence.
Richtlinie und Multiplexing sind zwei Funktionen, die den Funktionsreichtum der CLFS-basierten LogRecordSequence-Klasse gegenüber der einfachen dateibasierten FileRecordSequence-Klasse ausmachen. Die Festlegung von Richtlinien mit der LogPolicy-Struktur ermöglicht eine äußerst detaillierte Steuerung der Wartungsfunktionen, wie zum Beispiel automatische Vergrößerung und automatische Verkleinerung der Protokollgröße, Container mit Mindest- und Höchstausdehnung sowie tail-pinned Schwellenwerte. Mit diesen Funktionen können auch zirkuläre Protokolle mithilfe der CLFS-basierten LogRecordSequence-Klasse erstellt werden. Multiplexing oder die Möglichkeit, NTFS-Dateistreams zu bearbeiten, kann für eine Anwendung oder mehrere Anwendungen, die als Einheit zusammenarbeiten, eine höhere Leistung und größere Benutzerfreundlichkeit bedeuten. Daher können Anwendungen, die für langfristige Szenarios oder außerordentlich hohe Leistung entworfen werden, am meisten von der Verwendung der LogRecordSequence-Klasse im Gegensatz zur FileRecordSequence-Klasse profitieren.
Darüber hinaus bietet CLFS Vorteile in puncto Stabilität und Leistung. CLFS wurde für die Verwendung mit Hochleistungsanwendungen oder in einer Unternehmensumgebung entworfen. Gestatten Anwendungseinschränkungen die Ausführung auf einer CLFS-unterstützten Plattform, unterstützt die LogRecordSequence-Klasse nicht nur mehr Optionen bei der Steuerung der Protokolldateiwartung durch die LogPolicy-Klasse, sondern hat auch eine höhere E/A-Leistung zur Folge.
Wichtige Klassen in System.IO.Log
Im Folgenden werden die drei wichtigsten Klassen von System.IO.Log aufgeführt. Weitere Informationen zu ihrer Verwendung finden Sie in der dazugehörigen Referenzdokumentation.
LogRecordSequence
LogPolicy
Verwenden von System.IO.Log
Öffnen eines Protokolls und Hinzufügen von Wertebereichen
Das Öffnen eines Protokolls und das Hinzufügen von Wertebereichen ist üblicherweise die erste Aufgabe für das Hinzufügen von Protokollierfunktionen zu Ihrer Anwendung. Beachten Sie, dass Wertebereiche nur bei Verwendung der CLFS-basierten LogRecordSequence-Klasse hinzugefügt werden können.
Sie sollten zunächst den Speicherort des Protokolls sowie die Anzahl und die Größe der zum Protokoll hinzuzufügenden Wertebereiche berücksichtigen. Der Speicherort ist nur durch das Benutzerkonto beschränkt, für das die Anwendung ausgeführt wird. Jeder beliebige Speicherort, für den dieser Benutzer Schreibzugriff hat, ist ein gültiger Speicherort. Die Anzahl der Wertebereiche und ihre Größe erfordert jedoch mehr anwendungsspezifische Überlegungen. Beim Hinzufügen des ersten Wertebereichs zum Protokoll muss eine Größe angegeben werden. Diese Größe wird für alle manuell oder mit automatischem Wachstumsverhalten zum Protokoll hinzugefügten Wertebereiche verwendet. Darüber hinaus müssen zur Nutzung der zahlreichen von der LogPolicy-Klasse bereitgestellten Funktionen jederzeit mindestens zwei Wertebereiche für eine bestimmte Sequenz vorhanden sein.
Im folgenden Beispiel werden das Erstellen eines Protokolls und das Hinzufügen von zwei 1-MB-Wertebereichscontainern zu diesem Protokoll veranschaulicht.
LogRecordSequence recordSequence = new LogRecordSequence("application.log", FileMode.CreateNew);
recordSequence.LogStore.Extents.Add("app_extent0", 1024 * 1024);
recordSequence.LogStore.Extents.Add("app_extent1");
Festlegen einer Richtlinie
Die Festlegung von Protokollrichtlinien mit der LogPolicy-Struktur sollte die zweite Aufgabe in der Entwicklung Ihrer Protokollanwendung sein. Beachten Sie, dass diese Aufgabe nur bei Verwendung der CLFS-basierten LogRecordSequence-Klasse ausgeführt werden kann und bei jedem Öffnen eines Protokolls neu festgelegt werden muss, da Richtlinien nicht beibehalten werden.
Eine Richtlinie enthält wichtige Optionen, wie zum Beispiel automatische Vergrößerung und maximale Anzahl der Wertebereiche. Während der Lebensdauer einer Anwendung können schwankende Auslastungen dazu führen, dass der ursprüngliche Satz Wertebereiche vermindert wird, was möglicherweise während der Ausführung eine SequenceFullException zur Folge haben kann. Um dies zu verhindern, empfiehlt es sich häufig, das Protokoll automatisch wachsen zu lassen, um diese zusätzliche Auslastung transparent aufzunehmen. Beachten Sie, dass das manuelle Hinzufügen von Wertebereichen während SequenceFullException unterstützt wird und anstelle des transparenten automatischen Wachstums verwendet werden kann.
Für zirkuläre Protokolle sollte außerdem eine maximale Wertebereichsanzahl festgelegt werden. Darüber hinaus verfügt die LogPolicy-Struktur über verschiedene gemeinsame Hilfseinstellungen, wie zum Beispiel automatische Verkleinerung und Wachstumsrateneinstellungen. Die Bearbeitung dieser Werte kann die Anwendungsleistung erheblich beeinträchtigen, und diese sollte basierend auf der E/A-Rate für jedes vorhandene Protokoll im System angepasst werden.
Mit dem nachstehenden Beispiel wird das Festlegen einer Protokollrichtlinie für das automatische Wachstum auf bis zu 100 Wertebereiche mit einer Wachstumsrate von jeweils 5 Wertebereichen auf einmal dargestellt.
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();
Anhängen eines Datensatzes
Sie sollten verschiedene Techniken zur Bereitstellung von Daten in einem den Append-Methoden bekannten Format kennen.
Die aktuellen Append-Methoden wurden zur Behandlung von Byte-Arrays und Byte-Array-Listen optimiert. Sie können sie jedoch auch in Verbindung mit höheren Serialisierungsklassen verwenden. Das daraus resultierende Datensatzformat muss trotzdem bekannt sein, um von der IRecordSequence-Klasse bereitgestellte Funktionen effektiv übernehmen zu können. Nachstehend ist ein hohes Serialisierungscodebeispiel mit den auf DataContractAttribute in System.Runtime.Serialization.Formatters basierenden Funktionen dargestellt.
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, …);
Beim Anhängen von Datensätzen an eine Datensatzsequenz sollten Platz- und Zeitbeschränkungen Ihrer Anwendung sorgfältig abgewogen werden. So kann Ihre Anwendung beispielsweise erzwingen, dass ein Anhängevorgang nur dann erfolgreich ist, wenn im Protokoll ausreichend Platz verbleibt, um zu einem späteren Zeitpunkt mit der ReservationCollection-Klasse zusätzliche Datensätze zu schreiben. ARIES-basierte Transaktionsverarbeitungssysteme können auf solche Datensätze üblicherweise als Kompensierungs- oder Rückgängigdatensätze verweisen, die verwendet werden, wenn für Arbeit ein Rollback ausgeführt werden muss. Ähnlich verfügen einige Systeme über strenge Richtlinien bezüglich der Zeit, zu der Datensätze zu schreiben sind. Die Auswahl der auf den Datenträger wegzuschreibenden Datensätze und der Datensätze, für die verzögerte Speicherung unterstützt wird, basiert ausschließlich auf Anwendungsanforderungen und Systembeschränkungen. Diese Aspekte können sich sowohl auf Leistung als auch auf Richtigkeit auswirken.
Sowohl Platz- als auch Zeitoptionen werden dem Benutzer mit den verschiedenen Append-Methoden angezeigt. Mit dem nachstehenden Beispiel wird das Festlegen dieser Optionen durch das Anhängen von drei Datensätzen und die Reservierung von Platz für spätere Datensätze dargestellt. Ein Wegschreiben wird nur beim Schreiben des allerletzten Datensatzes erzwungen.
// 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);
Wenn für die RetryAppend-Eigenschaft true festgelegt ist und ein Anhängen-Vorgang fehlschlägt, da die Sequenz nicht genügend Speicherplatz aufweist, versucht die Datensatzsequenz, Speicherplatz freizugeben und den Anhängevorgang zu wiederholen. Die genaue Durchführung der Speicherplatzfreigabe ist unterschiedlich. So ruft die LogRecordSequence-Klasse beispielsweise das CLFS-Richtlinienmodul auf, wie im nachstehenden Abschnitt "Freigeben von Speicherplatz mit dem TailPinnedEvent" beschrieben.
Reservierung
Reservierungen können auf zwei Arten vorgenommen werden, wie in den nachstehenden Beispielen beschrieben. Sie können die Vorgehensweise aus den Beispielen für stabile Verarbeitung übernehmen. Beachten Sie, dass diese Aufgabe nur bei Verwendung der CLFS-basierten LogRecordSequence-Klasse ausgeführt werden kann.
Verwenden der ReserveAndAppend-Methode
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);
Verwenden der manuellen Vorgehensweise
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);
Lesen des Protokolls
Während der Lebensdauer einer Anwendung kann jederzeit aus dem Protokoll gelesen werden. Je nach Situation kann es jedoch notwendig sein, die im Protokoll in verschiedenen Sortierungen gespeicherten Datensätze zu durchlaufen. Zusätzlich zu den von Next und Previous für das Protokoll festgelegten Standardanweisungen können Sie sie auch in einer benutzerdefinierten Reihenfolge durchlaufen, die beim Anhängen einzelner Datensätze an die Sequenz angegeben wird. Beachten Sie auch, dass Previous hinsichtlich des physischen Protokolls nicht immer sequenziell ist, da der Benutzer den mit dem aktuellen Thread einer Ausführung während eines Append-Vorgangs angehängten vorherigen Datensatz angeben kann.
Mit dem nachstehenden Beispiel wird ein Protokoll in sequenzieller Vorwärtsrichtung gelesen, beginnend mit dem Datensatz mit der Sequenznummer 'startSqn'.
foreach (LogRecord record in recordSequence.ReadLogRecords(startSqn, LogRecordEnumeratorType.Next))
{
Stream dataStream = record.Data;
// Process dataStream, which can be done using deserialization
}
Freigeben von Speicherplatz mit dem TailPinnedEvent
Mit einer der Vorgehensweisen kann eine Datensatzsequenz die Freigabe von Speicherplatz zur Auslösung des TailPinned-Ereignisses verwenden. Dieses Ereignis gibt an, dass das Ende der Sequenz (die Basissequenznummer) nach vorne verschoben werden muss, um Speicherplatz freizugeben.
Das Ereignis kann jederzeit ausgelöst werden, wenn die Datensatzsequenz aus einem beliebigen Grund Speicherplatz freigeben muss. So kann das CLFS-Richtlinienmodul beispielsweise entscheiden, das Ereignis auszulösen, wenn es feststellt, dass die Enden von zwei Anmeldeclients, die sich eine Protokolldatei teilen, zu weit auseinander liegen. Die Freigabe von Speicherplatz kann durch Schreiben von Neustartbereichen oder durch Abschneiden des Protokolls und Freigeben von Speicherplatz mit der AdvanceBaseSequenceNumber-Methode erfolgen. Im folgenden Beispiel wird die zweite Vorgehensweise veranschaulicht.
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);
}
Sie können auch die WriteRestartArea-Methode außerhalb des TailPinned-Ereignisses aufrufen, um Speicherplatz freizugeben. Ein Neustartbereich ist mit einem Prüfpunkt anderer Protokollverarbeitungssysteme vergleichbar. Beim Aufruf dieser Methode betrachtet die Anwendung alle vorherigen Datensätze vor dem Neustartbereich als abgeschlossen und für spätere Datensatzanhängevorgänge verfügbar. Ähnlich wie andere Datensätze erfordert der mit dieser Methode geschriebene Datensatz tatsächlich freien Speicherplatz im Protokoll, um funktionieren zu können.
Multiplexing
Wenn mehrere Anwendungen als Einheit zusammenarbeiten, können Sie mit der CLFS-basierten LogRecordSequence-Klasse einen einzelnen NTFS-Dateistream verwenden. Im folgenden Codebeispiel wird die Erstellung eines Multiplex-Protokolls mit zwei Streams veranschaulicht. Es werden verschachtelnde Anhänge- und Lesevorgänge für die Protokolldatensätze durchgeführt.
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);
}
}
}
Behandeln von Ausnahmen
Anwendungen, die System.IO.Log verwenden, müssen auf die Handhabung von aus der Infrastruktur resultierenden Failfasts vorbereitet sein. In einigen Szenarios verwendet System.IO.Log keine Ausnahmen für die Kommunikation von Fehlern an eine Anwendung. Stattdessen werden Ausnahmen vorwiegend für wiederherstellbare Fehler verwendet, anhand derer ein Anwendungsentwickler aktiv werden kann. Failfasts treten jedoch auf, wenn eine weitere Ausführung beschädigte oder potenziell nicht mehr verwendbare Protokolldateien zur Folge hätte. Treten Failfasts auf, ist keine andere Anwendungsaktion zur Beseitigung des Problems verfügbar, und die Anwendung muss auf die Beendigung vorbereitet sein.
Weitere Informationen zu Failfasts finden Sie unter FailFast.
Viele der von System.IO.Log ausgelösten Ausnahmen beruhen auf internen Protokollimplementierungen. Nachstehend sind einige wichtige Ausnahmen aufgeführt, die Sie kennen sollten.
SequenceFullException: Diese Ausnahme kann, muss aber nicht schwerwiegend sein. Ist für AutoGrowtrue festgelegt und ist ausreichend Platz zum Wachsen vorhanden, kann trotzdem eine SequenceFullException ausgelöst werden. Dies beruht darauf, dass die automatische Wachstumsimplementierung grundsätzlich ein asynchroner, nicht atomischer Vorgang ist und nicht garantiert werden kann, dass die Wachstumsrate auf einmal abgeschlossen werden kann. Sind für die Wachstumsrate beispielsweise 100 Wertebereiche angegeben, kann das Hinzufügen aller 100 Wertebereiche zum Protokollspeicher erhebliche Zeit in Anspruch nehmen. Dies kann dazu führen, dass in diesem Zeitrahmen regelmäßig diese Ausnahme ausgelöst wird.
TailPinned-Handler: Eine innerhalb des TailPinned-Ereignisses ausgelöste Ausnahme wird der internen Protokollimplementierung gemeldet. Diese gibt an, dass eine Anwendung die Basissequenznummer nicht verschieben kann. Es wird eine SequenceFullException ausgelöst, da das TailPinned-Ereignis für eine Anwendung die letzte Möglichkeit darstellt, zu verhindern, dass ein Protokoll voll wird.
ReservationNotFoundException: Diese Ausnahme tritt ein, wenn Sie versuchen, Datensätze mit einer Reservierungssammlung anzuhängen und alle Reservierungen der passenden Größe bereits vergeben sind.
Siehe auch
Referenz
System.IO.Log
LogRecordSequence
FileRecordSequence
Weitere Ressourcen
Protokollierunterstützung in System.IO.Log
.gif)
Senden Sie Kommentare zu diesem Thema an Microsoft.
Copyright © 2007 by Microsoft Corporation. Alle Rechte vorbehalten.