* 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
parent
3b0eb958a8
commit
e4a8cc192d
|
@ -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, "");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue