* 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.
0.6.2-post-fixes
Teravus Ovares 2009-01-05 04:09:04 +00:00
parent 3b0eb958a8
commit e4a8cc192d
5 changed files with 185 additions and 11 deletions

View File

@ -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("<br />Position: ?");
}
else
{
output.Append(string.Format("<br /><NOBR>Position: <{0},{1},{2}></NOBR>", (int)av.AbsolutePosition.X,
(int) av.AbsolutePosition.Y,
(int) av.AbsolutePosition.Z));
}
Dictionary<string, int> throttles = DecodeClientThrottles(av.ControllingClient.GetThrottlesPacked(1));
HTMLUtil.UL_O(ref output, "");
foreach (string throttlename in throttles.Keys)
{
HTMLUtil.LI_O(ref output, "");

View File

@ -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("<DIV id=\"activeconnections\">loading...</DIV>");
output.Append("<DIV id=\"activeconnections\">Active Connections loading...</DIV>");
HTMLUtil.TD_C(ref output);
HTMLUtil.TD_O(ref output, "align_top");
output.Append("<DIV id=\"activesimstats\">loading...</DIV>");
output.Append("<DIV id=\"activesimstats\">SimStats loading...</DIV>");
output.Append("<DIV id=\"activelog\">ActiveLog loading...</DIV>");
HTMLUtil.TD_C(ref output);
HTMLUtil.TR_C(ref output);
HTMLUtil.TABLE_C(ref output);

View File

@ -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("<H3>ActiveLog</H3>\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 = "<font color=\"#7D7C00\">";
formatclose = "</font>";
break;
case "ERROR ":
formatopen = "<font color=\"#FF0000\">";
formatclose = "</font>";
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 - [<font color=\"#");
replaceStr.Append(formatresult);
replaceStr.Append("\">$3</font>] $4<br />");
string repstr = replaceStr.ToString();
output.Append(formatopen);
output.Append(webFormat.Replace(result[i], repstr));
output.Append(formatclose);
}
return output.ToString();
}
#endregion
}
}

View File

@ -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();
}

View File

@ -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<UUID, UserSessionID> m_sessions = new Dictionary<UUID, UserSessionID>();
private List<Scene> m_scene = new List<Scene>();
private Dictionary<string, IStatsController> reports = new Dictionary<string, IStatsController>();
private Dictionary<UUID, USimStatsData> m_simstatsCounters = new Dictionary<UUID, USimStatsData>();
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)