Pokud v aplikaci zapisujeme nějakou událost do aplikačního EventLogu měli by jsme mít na paměti, že velikost zapisované zprávy je omezená (*). Pokud zapisujeme jen nějakou kratičkou zprávu není toto problém, pokud se ale jedná o složitěji generovanou zprávu nebo report po dokončení nějakého zpracování je potřeba toto nějakým způsobem ošetřit. V tomto příspěvku uvedu jeden z možných způsobů.
Toto řešení předpokládá, že zapisovaná zpráva má strukturu souhrnného řádkového výpisu logu tj. skládá se z předem neznámého počtu řádků, které sami o sobě ale nejsou příliš dlouhé. Dále se vychází z faktu, že nejdůležitější informace jsou na začátku zprávy a pak až úplně na konci. V případě, kdy by tedy byla zpráva pro zápis do EventLogu příliš dlouhá, provede se vynechání potřebného počtu řádků někde “kus” před koncem zprávy.
Pro náš kód budeme dále předpokládat existenci nějaké velice jednoduché logovací infrastruktury, zde konkrétně třídy EventLogLogger, kterou o výše uvedený princip rozšíříme.
Nejprve výchozí verze této třídy:
/// <summary>
/// Typ logované zprávy
/// </summary>
public enum EntryType
{
/// <summary>
/// Informace
/// </summary>
Information = 1,
/// <summary>
/// Varování
/// </summary>
Warning,
/// <summary>
/// Chyba
/// </summary>
Error
}
/// <summary>
/// Interface třídy implementující zápis do logu
/// </summary>
public interface ILogger
{
#region action methods
/// <summary>
/// Zápis zprávy do logu
/// </summary>
/// <param name="message">Text zprávy</param>
/// <param name="type">Typ zprávy</param>
void WriteLine(string message, EntryType type = EntryType.Information);
#endregion
}
/// <summary>
/// Zápis zpráv do event logu
/// </summary>
public class EventLogLogger : ILogger
{
#region member varible and default property initialization
/// <summary>
/// Zdroj zpráv zapisovaných do event logu
/// </summary>
public string Source { get; set; }
/// <summary>
/// Hlavička zpráv zapisovaných do event logu
/// </summary>
public string EntryHeader { get; set; }
#endregion
#region constructors and destructors
/// <summary>
/// Konstruktor třídy <see cref="EventLogLogger"/>
/// </summary>
public EventLogLogger()
{
this.Source = GetDefaultSourceName();
}
#endregion
#region action methods
/// <summary>
/// Zápis zprávy do logu
/// </summary>
/// <param name="message">Text zprávy</param>
/// <param name="type">Typ zprávy</param>
public void WriteLine(string message, EntryType type)
{
EventLogEntryType entryType;
switch (type)
{
case EntryType.Error:
entryType = EventLogEntryType.Error;
break;
case EntryType.Warning:
entryType = EventLogEntryType.Warning;
break;
default:
entryType = EventLogEntryType.Information;
break;
}
EventLog.WriteEntry(this.Source, (string.IsNullOrEmpty(this.EntryHeader) ? "" : this.EntryHeader + Environment.NewLine) + message, entryType);
}
#endregion
#region private member functions
private static string GetDefaultSourceName()
{
var assembly = System.Reflection.Assembly.GetEntryAssembly();
if (assembly == null)
{
assembly = System.Reflection.Assembly.GetExecutingAssembly();
}
return assembly.GetName().Name;
}
#endregion
}
Pro zpracování zapisované zprávy budeme dále potřebovat rozložit jí na jednotlivé řádky, k tomu bude sloužit pomocná metoda ReadLines.
private static IEnumerable<string> ReadLines(string s)
{
using (var reader = new StringReader(s))
{
while (true)
{
string line = reader.ReadLine();
if (line == null)
{
break;
}
yield return line;
}
}
}
Vlastní logiku zkrácení zprávy nyní můžeme umístit do metody TrimMessage.
private static string TrimMessage(string message, int sourceLength)
{
int maxLength = 31894 - sourceLength;
const int endLines = 20;
if (message.Length <= maxLength)
{
return message;
}
var lines = ReadLines(message).ToList();
var sb = new System.Text.StringBuilder();
sb.AppendLine();
sb.AppendLine("----Část této zprávy je na tomto místě vynechána----");
for (int i = lines.Count - endLines - 1; i < lines.Count; i++)
{
sb.AppendLine();
sb.Append(lines[i]);
}
int index = 0;
foreach (var line in lines)
{
if (sb.Length + line.Length > maxLength)
{
break;
}
sb.Insert(index, line);
index += line.Length;
sb.Insert(index, Environment.NewLine);
index += Environment.NewLine.Length;
}
return sb.ToString();
}
Metoda musí kromě vlastní zprávy jako parametr dostat i délku názvu zdroje (vlastnost Source), protože i ten se započítává do celkové délky zprávy. Její skutečná maximální délka je pak 31894 znaků (POZOR, hodnota je jiná než ta uváděná v chybové zprávě) – hodnota platná pro operační systémy Windows 7/2008/2008 R2. Metoda dále obsahuje druhou konstantu určující počet řádků od konce zprávy, které se mají zachovat, zde konkrétně 20 řádků.
Vlastní výkonný kód metody je poměrně jednoduchý, nejprve se zpráva rozloží na jednotlivé řádky, do výstupu se zapíše text informující o vynechání části zprávy a oněch posledních 20 řádku a nakonec se před tento výstup postupně zapisují jednotlivé řádky ze začátku zprávy až do doby, kdy ještě není překročená maximální délka výstupu.
Posledním krokem je doplnit volání metody TrimMessage v metodě WriteLine:
EventLog.WriteEntry(this.Source, TrimMessage((string.IsNullOrEmpty(this.EntryHeader) ? "" : this.EntryHeader + Environment.NewLine) + message, this.Source.Length), entryType);
Výsledná třída EventLogLogger, pouze rozšířena ještě o úroveň důležitosti logovaných zpráv (Verbosity) je k dispozici zde.
(*) Konkrétně při překročení délky zapisované zprávy dostaneme chybu:
Log entry string is too long. A string written to the event log cannot exceed 32766 characters.