In den meisten Programmen gibt es eigene Logger. So auch z.B. in dem Konfigurator PrimeFact. Sobald man aber viele Instanzen aufgesetzt hat, hat man kaum noch einen Überblick über alle auftretenden Probleme. Es gibt wie immer mehrere Möglichkeiten. Klar, wenn alles auf einer Datenbank läuft: Einfach eine View machen mit mehreren Tabellen. Problem hier, es fehlen einfach Informationen und falls nicht alles auf einer Datenbank läuft wird es schon schwerer. Falls dann noch eine Updateautomatik hinzu kommt, hat man fast verloren. Warum nicht einfach mal umdenken?
Schön wäre ein Zentrale DB mit einer coolen Oberfläche, wo man alles an Fehler hin pushen könnte. Letzte Woche erst installiert und schon läuft es Produktiv. Kibana.

Hiermit hab ich endlich einen Überblick über alle Fehlermeldungen im Konfigurator. Sogar das Implementieren war super einfach. Dafür musste ich einfach nur im PrimeFact die LoggerItem Klasse ableiten.


public class MyLoggerItem : LoggerItem
{
public override void InitializeObject()
{
base.InitializeObject();
SBObject.nonDB = true;
}

/// <summary>
/// Bevor der Beleg gespeichert wird
/// </summary>
public override void BeforeCommit()
{
string message = _Message.StringValue;
string level = _Level.StringValue;
string stackTrace = _StackTrace.StringValue;
try
{
SendMessage(message, this, level, stackTrace);
}
catch
{
SBObject.nonDB = false;
}
}

/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="level"></param>
/// <param name="stackTrace"></param>
public static void SendMessage(string message, PFObject obj, string level, string stackTrace)
{
string area = "primefact";
string subarea = "message";

var content = new PrimeFactKibanaMessage(message, level, stackTrace, GetEFAVersion(), GetPFVersion(obj?.Session));

PrimeFact5.EFAAPI.Kibana.SendMessage(area, subarea, content);
}
}

Dann noch eine kleine Klasse, die man gefüllt dann ins Kibana schreibt…

/// <summary>
/// Klasse die gefüllt werden muss, die dann zum Kibana per json gesendet wird
/// </summary>
public class PrimeFactKibanaMessage
{
/// <summary>
/// Konstruktor
/// </summary>
/// <param name="message"></param>
/// <param name="stackTrace"></param>
public PrimeFactKibanaMessage(string message, string level, string stackTrace, string efaVersion, string pfVersion)
{
Message = message;
Level = level;
StackTrace = stackTrace;
TimeStamp = DateTime.Now.ToUniversalTime();
try
{
SystemName = EFA.Core.EfaProtokoller.GetSystemName();
}
catch { }
try
{
SystemVersion = EFA.Core.EfaProtokoller.GetSystemVersion();
}
catch { }
PFVersion = pfVersion;
EFAVersion = efaVersion;
User = System.Environment.UserName;
Domain = System.Environment.UserDomainName;
HostName = System.Environment.MachineName;
}

public DateTime TimeStamp { get; set; }
public string Message { get; set; }
public string Level { get; set; }
public string StackTrace { get; set; }
public string SystemName { get; set; }
public string SystemVersion { get; set; }
public string PFVersion { get; set; }
public string EFAVersion { get; set; }
public string User { get; set; }
public string Domain { get; set; }
public string HostName { get; set; }
}

Ach ja und das Herzstück. Das schreiben ins Kibana.

public static class Kibana
{
/// <summary>
///
/// </summary>
/// <param name="area"></param>
/// <param name="subarea"></param>
/// <param name="content"></param>
public static void POSTKibanaMessageAsync(string area, string subarea, object content)
{
if (String.IsNullOrWhiteSpace(area))
{
throw new ArgumentNullException("area");
}
if (String.IsNullOrWhiteSpace(subarea))
{
throw new ArgumentNullException("subarea");
}
if (content == null)
{
throw new ArgumentNullException("content");
}

area = area.ToLower();
subarea = subarea.ToLower();

var values = new Dictionary<string, object>();

var type = content.GetType();

if (type.GetInterfaces().Contains(typeof(IEnumerable)))
{
POSTKibanaMessageListAsync(area, subarea, content);
return;
}

var acceptableTypeList = new List<Type>
{
typeof(string),
typeof(DateTime),
typeof(int),
typeof(float),
typeof(double),
typeof(decimal),
typeof(char)
};

foreach (var propertyInfo in type.GetProperties())
{
if (acceptableTypeList.Contains(propertyInfo.PropertyType) && propertyInfo.CanRead)
{
var key = propertyInfo.Name;
var value = propertyInfo.GetValue(content, null);

// Kibana braucht ein ganz bestimmtes DateTime Format, sonst erkennt es das nicht
if (propertyInfo.PropertyType == typeof(DateTime) && value != null)
value = ((DateTime)value).ToString("yyyy-MM-dd'T'HH:mm:ss");
values.Add(key, value);
}
}

var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://Server:port/" + area + "/" + subarea + "/");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";

String username = "";
String password = "";
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
httpWebRequest.Headers.Add("Authorization", "Basic " + encoded);

using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(values);

streamWriter.Write(json);
streamWriter.Flush();
streamWriter.Close();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}

}

/// <summary>
///
/// </summary>
/// <param name="area"></param>
/// <param name="subarea"></param>
/// <param name="content"></param>
private static void POSTKibanaMessageListAsync(string area, string subarea, object content)
{
var type = content.GetType();

if (!type.GetInterfaces().Contains(typeof(IEnumerable)))
{
POSTKibanaMessageAsync(area, subarea, content);
}
else
{
var list = content as IEnumerable;
if (list != content)
{
foreach (var item in list)
{
POSTKibanaMessageAsync(area, subarea, content);
}
}
}
}
}

Das Ergebnis lässt sich hier schon nach 2 Tagen sehen.