// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; using System.Data.Common; using System.Drawing; using System.Web.UI.WebControls; namespace Spludlow { public static class Log { [Serializable] public class LogInfo { public string LogType; public DateTime LogTime; public string Subject; public string OrganizationId; public string NetworkId; public string HostId; public string WebSite; public string Application; public int PId; public string RequestUrl; public string CommandLine; public string Thread; public string MethodAssembly; public string MethodType; public string Method; public string UserDomain; public string UserName; public string IdentityName; public string IPAddress; public string Version; public string OSVersion; public LogBodyInfo[] Bodies; } [Serializable] public class LogBodyInfo { public string LogBodyType; //public string LogBodyFormat; public string[] Body; public LogBodyInfo() { } public LogBodyInfo(string bodyType, object body, bool largeParamter) { this.LogBodyType = bodyType; //this.LogBodyFormat = ""; this.Body = Spludlow.Parameters.EncodeParameter(body, largeParamter, false); } } public static List Types = new List(new string[] { "I", "R", "F", "W", "E" }); public static string[] TypeNames = new string[] { "Info", "Report", "Finish", "Warning", "Error" }; public static string Host = null; public static string Address = null; static Log() { SetAddress(); } public static void SetAddress() { Host = Spludlow.Config.Host("Log", true); if (Host != null) { Address = Host + ":Log"; // Check if route queue or final log queue are available ??? not that simple need to work with Route() return; } Address = null; Console.WriteLine("Spludlow Logging disbaled. If you are runnng initial setup then this is normal."); } public static string LogTypeName(string logTypeLetter) { int index = Types.IndexOf(logTypeLetter); if (index < 0 || index >= TypeNames.Length) return null; return TypeNames[index]; } public static void Mark() { LogInfo info = Create("I", "SPLMARK", null); Spludlow.Call.Now(Host, "Spludlow", "Spludlow.Log", "Receive", new object[] { info }); } public static void Info(string subject) { Submit("I", subject, null); } public static void Info(string subject, params object[] bodies) { Submit("I", subject, bodies); } public static void Report(string subject) { Submit("R", subject, null); } public static void Report(string subject, params object[] bodies) { Submit("R", subject, bodies); } public static void Finish(string subject) { Submit("F", subject, null); } public static void Finish(string subject, params object[] bodies) { Submit("F", subject, bodies); } public static void Warning(string subject) { Submit("W", subject, null); } public static void Warning(string subject, params object[] bodies) { Submit("W", subject, bodies); } public static void Error(string subject) { Submit("E", subject, null); } public static void Error(string subject, params object[] bodies) { Submit("E", subject, bodies); } public static void Submit(string type, string subject) { Submit(type, subject, null); } public static void Submit(string type, string subject, object[] bodies) { if (Address == null) { Console.WriteLine("Log " + LogTypeName(type) + ": " + subject); return; } if (bodies != null) { if (bodies.GetType() != typeof(object[])) bodies = new object[] { bodies }; // Flatern out passed object[] //List bodyItems = new List(); don't need params keywork handles this //WalkObjects(bodyItems, bodies); //bodies = bodyItems.ToArray(); // Add first Exception message to subject foreach (object item in bodies) { if (item is Exception) { subject += " " + ((Exception)item).Message; break; } } } LogInfo logInfo = Create(type, subject, bodies); Spludlow.Call.Queue(Address, "Spludlow", "Spludlow.Log", "Receive", new object[] { logInfo }); } private static void WalkObjects(List bodyItems, object[] bodies) { foreach (object item in bodies) { if (item == null) continue; if (item.GetType() == typeof(object[])) WalkObjects(bodyItems, (object[])item); else bodyItems.Add(item); } } public static LogInfo Create(string type, string subject, object[] bodies) { List bodyList = MakeBodies(bodies); LogInfo logInfo = new LogInfo(); logInfo.LogType = type; logInfo.LogTime = DateTime.Now; logInfo.Subject = subject; logInfo.OrganizationId = Spludlow.Config.Get("Spludlow.OrganizationId"); logInfo.NetworkId = Spludlow.Config.Get("Spludlow.NetworkId"); logInfo.HostId = Environment.MachineName; if (System.Web.HttpContext.Current != null) { int statusCode = 0; if (System.Web.HttpContext.Current.Response != null) statusCode = System.Web.HttpContext.Current.Response.StatusCode; // Not much use, differnt to caught in Global.asax logInfo.WebSite = System.Web.Hosting.HostingEnvironment.ApplicationHost.GetSiteName(); logInfo.Application = System.Web.Hosting.HostingEnvironment.ApplicationHost.GetVirtualPath(); logInfo.RequestUrl = System.Web.HttpContext.Current.Request.HttpMethod + " " + statusCode + " " + System.Web.HttpContext.Current.Request.Url.AbsoluteUri; //.RawUrl; logInfo.IdentityName = ""; if (System.Web.HttpContext.Current.User != null) logInfo.IdentityName = System.Web.HttpContext.Current.User.Identity.Name; logInfo.IPAddress = System.Web.HttpContext.Current.Request.UserHostAddress; } else { logInfo.WebSite = ""; logInfo.Application = System.AppDomain.CurrentDomain.FriendlyName; logInfo.RequestUrl = ""; logInfo.IdentityName = ""; logInfo.IPAddress = ""; } System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess(); logInfo.PId = process.Id; logInfo.CommandLine = Environment.CommandLine; logInfo.Thread = System.Threading.Thread.CurrentThread.Name; logInfo.UserDomain = Environment.UserDomainName; logInfo.UserName = Environment.UserName; logInfo.Version = Environment.Version.ToString(); logInfo.OSVersion = Environment.OSVersion.ToString(); bool useStackFrame = false; if (type == "E") useStackFrame = true; string stackFrame = FindMethod(logInfo, useStackFrame); if (stackFrame != null) bodyList.Add(new LogBodyInfo("F", stackFrame, false)); if (bodyList.Count > 0) logInfo.Bodies = bodyList.ToArray(); return logInfo; } private static List MakeBodies(object[] bodies) { List infos = new List(); if (bodies != null) { for (int index = 0; index < bodies.Length; ++index) { object body = bodies[index]; if (body == null) continue; Type type = body.GetType(); DataSet dataBody = Spludlow.Data.ADO.WireDataSetObject(body); if (dataBody != null) body = dataBody; if (typeof(Exception).IsAssignableFrom(type) == true) { body = ((Exception)body).ToString(); } // Don't upload paramter if short string or exception bool large = true; if ((type.Name == "String" && ((string)body).Length < Spludlow.Parameters.MinUploadLength)) large = false; infos.Add(new LogBodyInfo("", body, large)); } } return infos; } public static void Receive(LogInfo info) { Receive(info, false); } public static void Receive(LogInfo info, bool dontReport) { Spludlow.Schema.LogsDataTable logsTable = new Schema.LogsDataTable(); Spludlow.Schema.LogBodiesDataTable logBodiesTable = new Schema.LogBodiesDataTable(); logBodiesTable.LogIdColumn.AllowDBNull = true; Spludlow.Schema.LogsRow logsRow = logsTable.NewLogsRow(); Spludlow.Schema.LogBodiesRow bodyRow; logsRow.LogType = info.LogType; logsRow.LogTime = info.LogTime; string subject = info.Subject; if (subject.Length > logsTable.SubjectColumn.MaxLength) { //bodyRow = bodyTable.NewLogBodiesRow(); //bodyRow.LogBodyType = "L"; //bodyRow.LogBodyFormat = " "; //bodyRow.Length = subject.Length; //bodyRow.Body = subject; //bodyTable.Rows.Add(bodyRow); subject = subject.Substring(0, logsTable.SubjectColumn.MaxLength); } logsRow.Subject = subject; logsRow.OrganizationId = info.OrganizationId; logsRow.NetworkId = info.NetworkId; logsRow.HostId = info.HostId; logsRow.WebSite = info.WebSite; logsRow.Application = info.Application; logsRow.PId = info.PId; logsRow.RequestUrl = info.RequestUrl; logsRow.CommandLine = info.CommandLine; logsRow.Thread = ""; if (info.Thread != null) logsRow.Thread = info.Thread; logsRow.MethodAssembly = info.MethodAssembly; logsRow.MethodType = info.MethodType; logsRow.Method = info.Method; logsRow.UserDomain = info.UserDomain; logsRow.UserName = info.UserName; logsRow.IdentityName = info.IdentityName; logsRow.IPAddress = info.IPAddress; logsRow.Version = info.Version; string version = info.OSVersion; version = version.Replace("Microsoft Windows ", ""); version = version.Replace("Service Pack", "SP"); logsRow.OSVersion = version; if (info.Bodies != null) { foreach (LogBodyInfo bodyInfo in info.Bodies) { bodyRow = logBodiesTable.NewLogBodiesRow(); bodyRow.LogBodyType = bodyInfo.LogBodyType; bodyRow.LogBodyFormat = bodyInfo.Body[0]; bodyRow.Length = bodyInfo.Body[1].Length; bodyRow.Body = bodyInfo.Body[1]; logBodiesTable.Rows.Add(bodyRow); } } logsRow.BodyCount = logBodiesTable.Rows.Count; string truncates = Spludlow.Data.ADO.TruncateMaxStrings(logsRow); if (truncates != null) Spludlow.Log.Error("Log Columns Truncated", truncates); Spludlow.Data.IDAL database = Spludlow.Data.DAL.Create("@Spludlow"); database.Begin(); try { long logId = database.Insert(logsRow); Spludlow.Data.DAL.CommandInfo commandInfo = database.MakeInserter("LogBodies"); foreach (Spludlow.Schema.LogBodiesRow row in logBodiesTable.Rows) { row.LogId = logId; database.Insert(commandInfo, row); } database.Commit(); } catch { database.Rollback(); throw; } } private static string FindMethod(LogInfo logInfo, bool stackFrame) { StringBuilder text = null; if (stackFrame == true) text = new StringBuilder(); System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(false); if (trace == null) throw new ApplicationException("trace"); foreach (System.Diagnostics.StackFrame frame in trace.GetFrames()) { System.Reflection.MethodBase method = frame.GetMethod(); if (method == null) throw new ApplicationException("method"); string assembly = ""; string type = ""; if (method.DeclaringType != null) { type = method.DeclaringType.FullName; if (type == "Spludlow.Log") continue; if (method.DeclaringType.Assembly == null) throw new ApplicationException("method.DeclaringType.Assembly"); if (method.DeclaringType.Assembly.GetName() == null) throw new ApplicationException("method.DeclaringType.Assembly.GetName()"); assembly = method.DeclaringType.Assembly.GetName().Name; } if (logInfo.Method == null) { logInfo.MethodAssembly = assembly; logInfo.MethodType = type; logInfo.Method = method.Name; if (text != null) text.Append("*"); if (stackFrame == false) break; } if (text != null) { text.Append(assembly); text.Append("\t"); text.Append(type); text.Append("."); text.Append(method.Name); text.Append("()"); text.AppendLine(); } } if (text == null) return null; return text.ToString(); } public static DataSet Query(int pageIndex, int pageSize, string searchText, string searchColumn) { Spludlow.Data.IDAL database = Spludlow.Data.DAL.Create("@Spludlow"); string commandText = database.PageCommandText("SELECT * FROM Logs @Where ORDER BY LogTime DESC", pageIndex, pageSize); DataSet dataSet = new DataSet(); if (searchText.Length == 0) { commandText = commandText.Replace("@Where", ""); database.Fill(dataSet, commandText); } else { commandText = commandText.Replace("@Where", "WHERE ((@SearchColumn LIKE @SearchText) OR (Subject = 'SPLMARK'))"); commandText = commandText.Replace("@SearchColumn", searchColumn); //Spludlow.Log.Warning(commandText); searchText = "%" + searchText + "%"; // Improve search !!!!!!!!!!!!!!!!!!!!!!!!! database.Fill(dataSet, commandText, new object[] { searchText }); } return dataSet; } public static DataSet Query(DateTime startTime) { Spludlow.Schema.LogsDataTable logsTable = new Schema.LogsDataTable(); Spludlow.Data.IDAL database = Spludlow.Data.DAL.Create("@Spludlow"); DbCommand command = database.MakeCommand("SELECT * FROM Logs WHERE (LogTime >= @StartTime) ORDER BY LogTime DESC, LogId DESC", new string[] { "@StartTime" }, new object[] { startTime }); database.Fill(logsTable, command); DataSet dataSet = new DataSet(); dataSet.Tables.Add(logsTable); return dataSet; } public static DataSet QueryErrors() { DateTime startTime = DateTime.Now.Date.AddDays(-1); Spludlow.Schema.LogsDataTable logsTable = new Schema.LogsDataTable(); Spludlow.Data.IDAL database = Spludlow.Data.DAL.Create("@Spludlow"); DbCommand command = database.MakeCommand("SELECT * FROM Logs WHERE (LogTime >= @StartTime AND (LogType = 'E')) ORDER BY LogTime DESC, LogId DESC", new string[] { "@StartTime" }, new object[] { startTime }); database.Fill(logsTable, command); DataSet dataSet = new DataSet(); dataSet.Tables.Add(logsTable); return dataSet; } public static DataSet Query(long logId) { Spludlow.Schema.LogsDataTable logsTable = new Schema.LogsDataTable(); Spludlow.Schema.LogBodiesDataTable logBodiesTable = new Schema.LogBodiesDataTable(); Spludlow.Data.IDAL database = Spludlow.Data.DAL.Create("@Spludlow"); database.Fill(logsTable, "SELECT * FROM Logs WHERE LogId = " + logId); database.Fill(logBodiesTable, "SELECT * FROM LogBodies WHERE LogId = " + logId); DataSet dataSet = new DataSet(); dataSet.Tables.Add(logsTable); dataSet.Tables.Add(logBodiesTable); return dataSet; } public static DataSet QueryBody(int logBodyId) { Spludlow.Schema.LogBodiesDataTable logBodiesTable = new Schema.LogBodiesDataTable(); Spludlow.Data.IDAL database = Spludlow.Data.DAL.Create("@Spludlow"); database.Fill(logBodiesTable, "SELECT * FROM LogBodies WHERE LogBodyId = " + logBodyId); DataSet dataSet = new DataSet(); dataSet.Tables.Add(logBodiesTable); return dataSet; } public static void StreamBodyTextTable(int logBodyId, int tableIndex) { System.Web.HttpResponse response = System.Web.HttpContext.Current.Response; Spludlow.Data.IDAL database = Spludlow.Data.DAL.Create("@Spludlow"); DataTable logBodiesTable = database.Select("SELECT LogBodyFormat, Body FROM LogBodies WHERE LogBodyId = " + logBodyId); DataRow row = logBodiesTable.Rows[0]; object data = Spludlow.Parameters.DecodeParameter(new string[] { (string)row["LogBodyFormat"], (string)row["Body"] }); if (data is DataSet) { DataTable table = ((DataSet)data).Tables[tableIndex]; response.BufferOutput = false; Spludlow.Data.TextTable.Write(response.OutputStream, table, System.Text.Encoding.Default); //.UTF8); } else { response.Write(data.GetType().FullName); } } public static void Clear() { Clear(DateTime.Now); } public static void Clear(DateTime beforeDate) { Spludlow.Data.IDAL database = Spludlow.Data.DAL.Create("@Spludlow"); string commandTextLogBodies = "DELETE LogBodies FROM LogBodies INNER JOIN Logs ON Logs.LogId = LogBodies.LogId WHERE Logs.LogTime < @LogTime"; string commandTextLogs = "DELETE FROM Logs WHERE LogTime < @LogTime"; database.CommandTimeout(10); database.Begin(); int logBodyCount; int logCount; try { logBodyCount = database.ExecuteNonQuery(commandTextLogBodies, new object[] { beforeDate }); logCount = database.ExecuteNonQuery(commandTextLogs, new object[] { beforeDate }); database.Commit(); } catch { database.Rollback(); throw; } Spludlow.Log.Report("Log Clear; before date:" + beforeDate.ToString() + ", logs deleted:" + logCount + " bodies deleted:" + logBodyCount); } public static void ColourGridRow(GridViewRowEventArgs e, DataRow row) { if (e.Row.RowType != DataControlRowType.DataRow) return; string logType = (string)row["LogType"]; string subject = (string)row["Subject"]; if (subject == "SPLMARK") { e.Row.BackColor = Color.Black; e.Row.ForeColor = Color.White; return; } Color colour = Color.Empty; if (logType == "E") { if ((e.Row.RowIndex % 2) == 0) colour = Color.FromArgb(0xff, 0xd0, 0xd0); else colour = Color.FromArgb(0xff, 0xc0, 0xc0); } if (logType == "R") { if ((e.Row.RowIndex % 2) == 0) colour = Color.FromArgb(0xd0, 0xff, 0xd0); else colour = Color.FromArgb(0xc0, 0xff, 0xc0); } if (logType == "F") { if ((e.Row.RowIndex % 2) == 0) colour = Color.FromArgb(0x90, 0xd0, 0xff); else colour = Color.FromArgb(0x80, 0xc0, 0xff); } if (logType == "W") { if ((e.Row.RowIndex % 2) == 0) colour = Color.FromArgb(0xff, 0xff, 0xd0); else colour = Color.FromArgb(0xff, 0xff, 0xc0); } if (colour != Color.Empty) e.Row.BackColor = colour; } public static DataSet BuildFilters(int historyCount) // Not in use { string[] targetColumns = new string[] { "LogType", "OrganizationId", "NetworkId", "HostId", "WebSite", "Application", "MethodAssembly", //"MethodType", //"Method", }; Dictionary> filterCounts = new Dictionary>(); foreach (string targetColumn in targetColumns) filterCounts.Add(targetColumn, new Dictionary()); DataTable logsTable = Query(0, historyCount, "", "").Tables[0]; foreach (DataRow row in logsTable.Rows) { foreach (string targetColumn in targetColumns) { if (row.IsNull(targetColumn) == true) continue; string value = (string)row[targetColumn]; Dictionary columnCount = filterCounts[targetColumn]; if (columnCount.ContainsKey(value) == false) columnCount.Add(value, 0); columnCount[value] = columnCount[value] + 1; } } int mostRowCount = 0; foreach (string targetColumn in targetColumns) { mostRowCount = Math.Max(mostRowCount, filterCounts[targetColumn].Count); } DataTable table = new DataTable(); foreach (string targetColumn in targetColumns) { table.Columns.Add(targetColumn, typeof(string)); } for (int index = 0; index < mostRowCount; ++index) { DataRow row = table.NewRow(); foreach (string targetColumn in targetColumns) row[targetColumn] = ""; table.Rows.Add(row); } foreach (string targetColumn in targetColumns) { int rowIndex = 0; foreach (string dataValue in filterCounts[targetColumn].Keys) { table.Rows[rowIndex][targetColumn] = dataValue + " (" + filterCounts[targetColumn][dataValue] + ")"; ++rowIndex; } } DataSet filterDataSet = new DataSet(); filterDataSet.Tables.Add(table); //foreach (string targetColumn in targetColumns) //{ // DataTable table = new DataTable(targetColumn); // table.Columns.Add("Value", typeof(string)); // table.Columns.Add("Count", typeof(int)); // Dictionary columnCount = filterCounts[targetColumn]; // foreach (string value in columnCount.Keys) // { // table.Rows.Add(new object[] { value, columnCount[value] }); // } // filterDataSet.Tables.Add(table); //} Spludlow.Log.Report("Logs Filter", new object[] { filterDataSet }); return filterDataSet; } } }