From e4a8cc192dd16930718ff18838aa82e6187741bf Mon Sep 17 00:00:00 2001 From: Teravus Ovares Date: Mon, 5 Jan 2009 04:09:04 +0000 Subject: * Adds an active log to the WebStats console. for an example of it in use as it is right now see http://wmcv.com:9000/SStats/ * It still isn't quite ready to be used mainstream. * A couple of things to note, it doesn't keep track of the logs if nobody is looking at the stats. * It doesn't read the whole log file. Just the last 10 lines of the stream. Tested to 1GB+ logfiles with no noticeable performance issues. --- .../Region/UserStatistics/ActiveConnectionsAJAX.cs | 13 ++- OpenSim/Region/UserStatistics/Default_Report.cs | 16 ++-- OpenSim/Region/UserStatistics/LogLinesAJAX.cs | 98 ++++++++++++++++++++++ OpenSim/Region/UserStatistics/SimStatsAJAX.cs | 3 +- OpenSim/Region/UserStatistics/WebStatsModule.cs | 66 ++++++++++++++- 5 files changed, 185 insertions(+), 11 deletions(-) create mode 100644 OpenSim/Region/UserStatistics/LogLinesAJAX.cs diff --git a/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs b/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs index 2dcd2b8..ed8fc40 100644 --- a/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs +++ b/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs @@ -14,6 +14,7 @@ namespace OpenSim.Region.UserStatistics { #region IStatsController Members + private Vector3 DefaultNeighborPosition = new Vector3(128, 128, 70); public Hashtable ProcessModel(Hashtable pParams) { @@ -50,10 +51,20 @@ namespace OpenSim.Region.UserStatistics output.Append(av.Name); output.Append("      "); output.Append((av.IsChildAgent ? "Child" : "Root")); - + if (av.AbsolutePosition == DefaultNeighborPosition) + { + output.Append("
Position: ?"); + } + else + { + output.Append(string.Format("
Position: <{0},{1},{2}>", (int)av.AbsolutePosition.X, + (int) av.AbsolutePosition.Y, + (int) av.AbsolutePosition.Z)); + } Dictionary throttles = DecodeClientThrottles(av.ControllingClient.GetThrottlesPacked(1)); HTMLUtil.UL_O(ref output, ""); + foreach (string throttlename in throttles.Keys) { HTMLUtil.LI_O(ref output, ""); diff --git a/OpenSim/Region/UserStatistics/Default_Report.cs b/OpenSim/Region/UserStatistics/Default_Report.cs index 02b15ad..eac4a7e 100644 --- a/OpenSim/Region/UserStatistics/Default_Report.cs +++ b/OpenSim/Region/UserStatistics/Default_Report.cs @@ -76,9 +76,9 @@ TD.align_top { vertical-align: top; } HTMLUtil.HtmlHeaders_O(ref output); HTMLUtil.InsertProtoTypeAJAX(ref output); - string[] ajaxUpdaterDivs = new string[2]; - int[] ajaxUpdaterSeconds = new int[2]; - string[] ajaxUpdaterReportFragments = new string[2]; + string[] ajaxUpdaterDivs = new string[3]; + int[] ajaxUpdaterSeconds = new int[3]; + string[] ajaxUpdaterReportFragments = new string[3]; ajaxUpdaterDivs[0] = "activeconnections"; ajaxUpdaterSeconds[0] = 10; @@ -88,6 +88,10 @@ TD.align_top { vertical-align: top; } ajaxUpdaterSeconds[1] = 20; ajaxUpdaterReportFragments[1] = "simstatsajax.ajax"; + ajaxUpdaterDivs[2] = "activelog"; + ajaxUpdaterSeconds[2] = 5; + ajaxUpdaterReportFragments[2] = "activelogajax.ajax"; + HTMLUtil.InsertPeriodicUpdaters(ref output, ajaxUpdaterDivs, ajaxUpdaterSeconds, ajaxUpdaterReportFragments); output.Append(STYLESHEET); @@ -152,11 +156,11 @@ TD.align_top { vertical-align: top; } HTMLUtil.TABLE_O(ref output, ""); HTMLUtil.TR_O(ref output, ""); HTMLUtil.TD_O(ref output, "align_top"); - output.Append("
loading...
"); + output.Append("
Active Connections loading...
"); HTMLUtil.TD_C(ref output); HTMLUtil.TD_O(ref output, "align_top"); - output.Append("
loading...
"); - + output.Append("
SimStats loading...
"); + output.Append("
ActiveLog loading...
"); HTMLUtil.TD_C(ref output); HTMLUtil.TR_C(ref output); HTMLUtil.TABLE_C(ref output); diff --git a/OpenSim/Region/UserStatistics/LogLinesAJAX.cs b/OpenSim/Region/UserStatistics/LogLinesAJAX.cs new file mode 100644 index 0000000..9626c12 --- /dev/null +++ b/OpenSim/Region/UserStatistics/LogLinesAJAX.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenSim.Region.Environment.Scenes; +using OpenSim.Framework.Statistics; + +namespace OpenSim.Region.UserStatistics +{ + public class LogLinesAJAX : IStatsController + { + private Regex normalizeEndLines = new Regex(@"\r\n", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.Multiline); + + private Regex webFormat = new Regex(@"[^\s]*\s([^,]*),[^\s]*\s([A-Z]*)[^\s-][^\[]*\[([^\]]*)\]([^\n]*)", + RegexOptions.Singleline | RegexOptions.Compiled); + private Regex TitleColor = new Regex(@"[^\s]*\s(?:[^,]*),[^\s]*\s(?:[A-Z]*)[^\s-][^\[]*\[([^\]]*)\](?:[^\n]*)", + RegexOptions.Singleline | RegexOptions.Compiled); + + + #region IStatsController Members + + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable nh = new Hashtable(); + nh.Add("loglines", pParams["LogLines"]); + return nh; + } + + public string RenderView(Hashtable pModelResult) + { + StringBuilder output = new StringBuilder(); + + HTMLUtil.HR(ref output, ""); + output.Append("

ActiveLog

\n"); + + string tmp = normalizeEndLines.Replace(pModelResult["loglines"].ToString(), "\n"); + + string[] result = Regex.Split(tmp, "\n"); + + string formatopen = ""; + string formatclose = ""; + + for (int i = 0; i < result.Length;i++ ) + { + if (result[i].Length >= 30) + { + string logtype = result[i].Substring(24, 6); + switch (logtype) + { + case "WARN ": + formatopen = ""; + formatclose = ""; + break; + + case "ERROR ": + formatopen = ""; + formatclose = ""; + break; + + default: + formatopen = ""; + formatclose = ""; + break; + + } + } + StringBuilder replaceStr = new StringBuilder(); + //string titlecolorresults = + + string formatresult = Regex.Replace(TitleColor.Replace(result[i], "$1"), "[^ABCDEFabcdef0-9]", ""); + if (formatresult.Length > 6) + { + formatresult = formatresult.Substring(0, 6); + + } + for (int j = formatresult.Length; j <= 5; j++) + formatresult += "0"; + replaceStr.Append("$1 - [$3] $4
"); + string repstr = replaceStr.ToString(); + + output.Append(formatopen); + output.Append(webFormat.Replace(result[i], repstr)); + output.Append(formatclose); + } + + + return output.ToString(); + } + + #endregion + } +} diff --git a/OpenSim/Region/UserStatistics/SimStatsAJAX.cs b/OpenSim/Region/UserStatistics/SimStatsAJAX.cs index f6dd1d6..26fe3d4 100644 --- a/OpenSim/Region/UserStatistics/SimStatsAJAX.cs +++ b/OpenSim/Region/UserStatistics/SimStatsAJAX.cs @@ -21,7 +21,6 @@ namespace OpenSim.Region.UserStatistics Hashtable nh = new Hashtable(); nh.Add("hdata", m_scene); nh.Add("simstats", pParams["SimStats"]); - return nh; } @@ -181,7 +180,9 @@ namespace OpenSim.Region.UserStatistics HTMLUtil.TD_C(ref output); HTMLUtil.TR_C(ref output); HTMLUtil.TABLE_C(ref output); + } + return output.ToString(); } diff --git a/OpenSim/Region/UserStatistics/WebStatsModule.cs b/OpenSim/Region/UserStatistics/WebStatsModule.cs index 4eb8cb7..832c763 100644 --- a/OpenSim/Region/UserStatistics/WebStatsModule.cs +++ b/OpenSim/Region/UserStatistics/WebStatsModule.cs @@ -1,8 +1,11 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Net; // to be used for REST-->Grid shortly using System.Reflection; +using System.Text; +using System.Threading; using log4net; using Nini.Config; using OpenMetaverse; @@ -25,14 +28,19 @@ namespace OpenSim.Region.UserStatistics { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static SqliteConnection dbConn; private Dictionary m_sessions = new Dictionary(); private List m_scene = new List(); private Dictionary reports = new Dictionary(); private Dictionary m_simstatsCounters = new Dictionary(); private const int updateStatsMod = 6; + private int updateLogMod = 1; + private volatile int updateLogCounter = 0; private volatile int concurrencyCounter = 0; private bool enabled = false; + private string m_loglines = String.Empty; + private volatile int lastHit = 12000; public virtual void Initialise(Scene scene, IConfigSource config) @@ -42,7 +50,8 @@ namespace OpenSim.Region.UserStatistics { cnfg = config.Configs["WebStats"]; enabled = cnfg.GetBoolean("enabled", false); - + + } catch (Exception) { @@ -68,12 +77,14 @@ namespace OpenSim.Region.UserStatistics Updater_distributor updatedep = new Updater_distributor(); ActiveConnectionsAJAX ajConnections = new ActiveConnectionsAJAX(); SimStatsAJAX ajSimStats = new SimStatsAJAX(); + LogLinesAJAX ajLogLines = new LogLinesAJAX(); reports.Add("", rep); reports.Add("index.aspx", rep); reports.Add("prototype.js", protodep); reports.Add("updater.js", updatedep); reports.Add("activeconnectionsajax.ajax", ajConnections); reports.Add("simstatsajax.ajax", ajSimStats); + reports.Add("activelogajax.ajax", ajLogLines); scene.CommsManager.HttpServer.AddHTTPHandler("/SStats/", HandleStatsRequest); @@ -95,10 +106,17 @@ namespace OpenSim.Region.UserStatistics try { - - if (concurrencyCounter > 0) + // Ignore the update if there's a report running right now + // ignore the update if there hasn't been a hit in 30 seconds. + if (concurrencyCounter > 0 && System.Environment.TickCount - lastHit < 30000) return; + if ((updateLogCounter++ % updateLogMod) == 0) + { + m_loglines = readLogLines(10); + if (updateLogCounter > 10000) updateLogCounter = 1; + } + USimStatsData ss = m_simstatsCounters[stats.RegionUUID]; if ((++ss.StatsCounter % updateStatsMod) == 0) @@ -114,6 +132,7 @@ namespace OpenSim.Region.UserStatistics public Hashtable HandleStatsRequest(Hashtable request) { + lastHit = System.Environment.TickCount; Hashtable responsedata = new Hashtable(); string regpath = request["uri"].ToString(); int response_code = 404; @@ -130,6 +149,7 @@ namespace OpenSim.Region.UserStatistics repParams["DatabaseConnection"] = dbConn; repParams["Scenes"] = m_scene; repParams["SimStats"] = m_simstatsCounters; + repParams["LogLines"] = m_loglines; concurrencyCounter++; @@ -245,6 +265,7 @@ namespace OpenSim.Region.UserStatistics { lock (m_scene) { + updateLogMod = m_scene.Count * 2; foreach (Scene scene in m_scene) { scene.EventManager.OnRegisterCaps += OnRegisterCaps; @@ -315,6 +336,45 @@ namespace OpenSim.Region.UserStatistics } + public string readLogLines( int amount) + { + Encoding encoding = Encoding.ASCII; + int sizeOfChar = encoding.GetByteCount("\n"); + byte[] buffer = encoding.GetBytes("\n"); + string logfile = Util.logDir() + "/" + "OpenSim.log"; + FileStream fs = new FileStream(logfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + Int64 tokenCount = 0; + Int64 endPosition = fs.Length / sizeOfChar; + + for (Int64 position = sizeOfChar; position < endPosition; position += sizeOfChar) + { + fs.Seek(-position, SeekOrigin.End); + fs.Read(buffer, 0, buffer.Length); + + if (encoding.GetString(buffer) == "\n") + { + tokenCount++; + if (tokenCount == amount) + { + byte[] returnBuffer = new byte[fs.Length - fs.Position]; + fs.Read(returnBuffer, 0, returnBuffer.Length); + fs.Close(); + fs.Dispose(); + return encoding.GetString(returnBuffer); + } + } + } + + // handle case where number of tokens in file is less than numberOfTokens + fs.Seek(0, SeekOrigin.Begin); + buffer = new byte[fs.Length]; + fs.Read(buffer, 0, buffer.Length); + fs.Close(); + fs.Dispose(); + return encoding.GetString(buffer); + + } + public UUID GetRegionUUIDFromHandle(ulong regionhandle) { lock (m_scene) -- cgit v1.1