mirror of
https://github.com/opensim/opensim.git
synced 2026-08-06 17:32:42 +08:00
Merge branch 'master' into careminster
Conflicts: OpenSim/Framework/Monitoring/BaseStatsCollector.cs OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
@@ -44,16 +44,16 @@ namespace OpenSim.Framework.Monitoring
|
||||
sb.Append("MEMORY STATISTICS");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.AppendFormat(
|
||||
"Allocated to OpenSim objects: {0} MB\n",
|
||||
Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
|
||||
"Heap allocated to OpenSim : {0} MB\n",
|
||||
Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
|
||||
|
||||
sb.AppendFormat(
|
||||
"OpenSim last object memory churn : {0} MB/s\n",
|
||||
Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3));
|
||||
"Last heap allocation rate : {0} MB/s\n",
|
||||
Math.Round((MemoryWatchdog.LastHeapAllocationRate * 1000) / 1024.0 / 1024, 3));
|
||||
|
||||
sb.AppendFormat(
|
||||
"OpenSim average object memory churn : {0} MB/s\n",
|
||||
Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3));
|
||||
"Average heap allocation rate: {0} MB/s\n",
|
||||
Math.Round((MemoryWatchdog.AverageHeapAllocationRate * 1000) / 1024.0 / 1024, 3));
|
||||
|
||||
Process myprocess = Process.GetCurrentProcess();
|
||||
if (!myprocess.HasExited)
|
||||
|
||||
@@ -60,17 +60,17 @@ namespace OpenSim.Framework.Monitoring
|
||||
private static bool m_enabled;
|
||||
|
||||
/// <summary>
|
||||
/// Last memory churn in bytes per millisecond.
|
||||
/// Average heap allocation rate in bytes per millisecond.
|
||||
/// </summary>
|
||||
public static double AverageMemoryChurn
|
||||
public static double AverageHeapAllocationRate
|
||||
{
|
||||
get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Average memory churn in bytes per millisecond.
|
||||
/// Last heap allocation in bytes
|
||||
/// </summary>
|
||||
public static double LastMemoryChurn
|
||||
public static double LastHeapAllocationRate
|
||||
{
|
||||
get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; }
|
||||
}
|
||||
|
||||
349
OpenSim/Framework/Monitoring/ServerStatsCollector.cs
Normal file
349
OpenSim/Framework/Monitoring/ServerStatsCollector.cs
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using Nini.Config;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenSim.Framework;
|
||||
|
||||
namespace OpenSim.Framework.Monitoring
|
||||
{
|
||||
public class ServerStatsCollector
|
||||
{
|
||||
private readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private readonly string LogHeader = "[SERVER STATS]";
|
||||
|
||||
public bool Enabled = false;
|
||||
private static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>();
|
||||
|
||||
public readonly string CategoryServer = "server";
|
||||
|
||||
public readonly string ContainerThreadpool = "threadpool";
|
||||
public readonly string ContainerProcessor = "processor";
|
||||
public readonly string ContainerMemory = "memory";
|
||||
public readonly string ContainerNetwork = "network";
|
||||
public readonly string ContainerProcess = "process";
|
||||
|
||||
public string NetworkInterfaceTypes = "Ethernet";
|
||||
|
||||
readonly int performanceCounterSampleInterval = 500;
|
||||
// int lastperformanceCounterSampleTime = 0;
|
||||
|
||||
private class PerfCounterControl
|
||||
{
|
||||
public PerformanceCounter perfCounter;
|
||||
public int lastFetch;
|
||||
public string name;
|
||||
public PerfCounterControl(PerformanceCounter pPc)
|
||||
: this(pPc, String.Empty)
|
||||
{
|
||||
}
|
||||
public PerfCounterControl(PerformanceCounter pPc, string pName)
|
||||
{
|
||||
perfCounter = pPc;
|
||||
lastFetch = 0;
|
||||
name = pName;
|
||||
}
|
||||
}
|
||||
|
||||
PerfCounterControl processorPercentPerfCounter = null;
|
||||
|
||||
// IRegionModuleBase.Initialize
|
||||
public void Initialise(IConfigSource source)
|
||||
{
|
||||
IConfig cfg = source.Configs["Monitoring"];
|
||||
|
||||
if (cfg != null)
|
||||
Enabled = cfg.GetBoolean("ServerStatsEnabled", true);
|
||||
|
||||
if (Enabled)
|
||||
{
|
||||
NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet");
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (RegisteredStats.Count == 0)
|
||||
RegisterServerStats();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (RegisteredStats.Count > 0)
|
||||
{
|
||||
foreach (Stat stat in RegisteredStats.Values)
|
||||
{
|
||||
StatsManager.DeregisterStat(stat);
|
||||
stat.Dispose();
|
||||
}
|
||||
RegisteredStats.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act)
|
||||
{
|
||||
MakeStat(pName, pDesc, pUnit, pContainer, act, MeasuresOfInterest.None);
|
||||
}
|
||||
|
||||
private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act, MeasuresOfInterest moi)
|
||||
{
|
||||
string desc = pDesc;
|
||||
if (desc == null)
|
||||
desc = pName;
|
||||
Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, moi, act, StatVerbosity.Debug);
|
||||
StatsManager.RegisterStat(stat);
|
||||
RegisteredStats.Add(pName, stat);
|
||||
}
|
||||
|
||||
public void RegisterServerStats()
|
||||
{
|
||||
// lastperformanceCounterSampleTime = Util.EnvironmentTickCount();
|
||||
PerformanceCounter tempPC;
|
||||
Stat tempStat;
|
||||
string tempName;
|
||||
|
||||
try
|
||||
{
|
||||
tempName = "CPUPercent";
|
||||
tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total");
|
||||
processorPercentPerfCounter = new PerfCounterControl(tempPC);
|
||||
// A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy.
|
||||
tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor,
|
||||
StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); },
|
||||
StatVerbosity.Info);
|
||||
StatsManager.RegisterStat(tempStat);
|
||||
RegisteredStats.Add(tempName, tempStat);
|
||||
|
||||
MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor,
|
||||
(s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; });
|
||||
|
||||
MakeStat("UserProcessorTime", null, "sec", ContainerProcessor,
|
||||
(s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; });
|
||||
|
||||
MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor,
|
||||
(s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; });
|
||||
|
||||
MakeStat("Threads", null, "threads", ContainerProcessor,
|
||||
(s) => { s.Value = Process.GetCurrentProcess().Threads.Count; });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e);
|
||||
}
|
||||
|
||||
MakeStat("BuiltinThreadpoolWorkerThreadsAvailable", null, "threads", ContainerThreadpool,
|
||||
s =>
|
||||
{
|
||||
int workerThreads, iocpThreads;
|
||||
ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
|
||||
s.Value = workerThreads;
|
||||
});
|
||||
|
||||
MakeStat("BuiltinThreadpoolIOCPThreadsAvailable", null, "threads", ContainerThreadpool,
|
||||
s =>
|
||||
{
|
||||
int workerThreads, iocpThreads;
|
||||
ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
|
||||
s.Value = iocpThreads;
|
||||
});
|
||||
|
||||
if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool && Util.GetSmartThreadPoolInfo() != null)
|
||||
{
|
||||
MakeStat("STPMaxThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxThreads);
|
||||
MakeStat("STPMinThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MinThreads);
|
||||
MakeStat("STPConcurrency", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxConcurrentWorkItems);
|
||||
MakeStat("STPActiveThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().ActiveThreads);
|
||||
MakeStat("STPInUseThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().InUseThreads);
|
||||
MakeStat("STPWorkItemsWaiting", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().WaitingCallbacks);
|
||||
}
|
||||
|
||||
MakeStat(
|
||||
"HTTPRequestsMade",
|
||||
"Number of outbound HTTP requests made",
|
||||
"requests",
|
||||
ContainerNetwork,
|
||||
s => s.Value = WebUtil.RequestNumber,
|
||||
MeasuresOfInterest.AverageChangeOverTime);
|
||||
|
||||
try
|
||||
{
|
||||
List<string> okInterfaceTypes = new List<string>(NetworkInterfaceTypes.Split(','));
|
||||
|
||||
IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces();
|
||||
foreach (NetworkInterface nic in nics)
|
||||
{
|
||||
if (nic.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
|
||||
string nicInterfaceType = nic.NetworkInterfaceType.ToString();
|
||||
if (!okInterfaceTypes.Contains(nicInterfaceType))
|
||||
{
|
||||
m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.",
|
||||
LogHeader, nic.Name, nicInterfaceType);
|
||||
m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}",
|
||||
LogHeader, NetworkInterfaceTypes);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nic.Supports(NetworkInterfaceComponent.IPv4))
|
||||
{
|
||||
IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics();
|
||||
if (nicStats != null)
|
||||
{
|
||||
MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork,
|
||||
(s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); });
|
||||
MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork,
|
||||
(s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); });
|
||||
MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork,
|
||||
(s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); });
|
||||
}
|
||||
}
|
||||
// TODO: add IPv6 (it may actually happen someday)
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e);
|
||||
}
|
||||
|
||||
MakeStat("ProcessMemory", null, "MB", ContainerMemory,
|
||||
(s) => { s.Value = Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d, 3); });
|
||||
MakeStat("HeapMemory", null, "MB", ContainerMemory,
|
||||
(s) => { s.Value = Math.Round(GC.GetTotalMemory(false) / 1024d / 1024d, 3); });
|
||||
MakeStat("LastHeapAllocationRate", null, "MB/sec", ContainerMemory,
|
||||
(s) => { s.Value = Math.Round(MemoryWatchdog.LastHeapAllocationRate * 1000d / 1024d / 1024d, 3); });
|
||||
MakeStat("AverageHeapAllocationRate", null, "MB/sec", ContainerMemory,
|
||||
(s) => { s.Value = Math.Round(MemoryWatchdog.AverageHeapAllocationRate * 1000d / 1024d / 1024d, 3); });
|
||||
}
|
||||
|
||||
// Notes on performance counters:
|
||||
// "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx
|
||||
// "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c
|
||||
// "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters
|
||||
private delegate double PerfCounterNextValue();
|
||||
private void GetNextValue(Stat stat, PerfCounterControl perfControl)
|
||||
{
|
||||
GetNextValue(stat, perfControl, 1.0);
|
||||
}
|
||||
private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor)
|
||||
{
|
||||
if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval)
|
||||
{
|
||||
if (perfControl != null && perfControl.perfCounter != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Kludge for factor to run double duty. If -1, subtract the value from one
|
||||
if (factor == -1)
|
||||
stat.Value = 1 - perfControl.perfCounter.NextValue();
|
||||
else
|
||||
stat.Value = perfControl.perfCounter.NextValue() / factor;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e);
|
||||
}
|
||||
perfControl.lastFetch = Util.EnvironmentTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup the nic that goes with this stat and set the value by using a fetch action.
|
||||
// Not sure about closure with delegates inside delegates.
|
||||
private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat);
|
||||
private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor)
|
||||
{
|
||||
// Get the one nic that has the name of this stat
|
||||
IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
|
||||
(network) => network.Name == stat.Description);
|
||||
try
|
||||
{
|
||||
foreach (NetworkInterface nic in nics)
|
||||
{
|
||||
IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics();
|
||||
if (intrStats != null)
|
||||
{
|
||||
double newVal = Math.Round(getter(intrStats) / factor, 3);
|
||||
stat.Value = newVal;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// There are times interfaces go away so we just won't update the stat for this
|
||||
m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ServerStatsAggregator : Stat
|
||||
{
|
||||
public ServerStatsAggregator(
|
||||
string shortName,
|
||||
string name,
|
||||
string description,
|
||||
string unitName,
|
||||
string category,
|
||||
string container
|
||||
)
|
||||
: base(
|
||||
shortName,
|
||||
name,
|
||||
description,
|
||||
unitName,
|
||||
category,
|
||||
container,
|
||||
StatType.Push,
|
||||
MeasuresOfInterest.None,
|
||||
null,
|
||||
StatVerbosity.Info)
|
||||
{
|
||||
}
|
||||
public override string ToConsoleString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override OSDMap ToOSDMap()
|
||||
{
|
||||
OSDMap ret = new OSDMap();
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
using log4net;
|
||||
using OpenMetaverse.StructuredData;
|
||||
|
||||
namespace OpenSim.Framework.Monitoring
|
||||
@@ -38,6 +40,10 @@ namespace OpenSim.Framework.Monitoring
|
||||
/// </summary>
|
||||
public class Stat : IDisposable
|
||||
{
|
||||
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public static readonly char[] DisallowedShortNameCharacters = { '.' };
|
||||
|
||||
/// <summary>
|
||||
/// Category of this stat (e.g. cache, scene, etc).
|
||||
/// </summary>
|
||||
@@ -95,7 +101,7 @@ namespace OpenSim.Framework.Monitoring
|
||||
/// <remarks>
|
||||
/// Will be null if no measures of interest require samples.
|
||||
/// </remarks>
|
||||
private static Queue<double> m_samples;
|
||||
private Queue<double> m_samples;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of statistical samples.
|
||||
@@ -162,6 +168,12 @@ namespace OpenSim.Framework.Monitoring
|
||||
throw new Exception(
|
||||
string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category));
|
||||
|
||||
foreach (char c in DisallowedShortNameCharacters)
|
||||
{
|
||||
if (shortName.IndexOf(c) != -1)
|
||||
throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c));
|
||||
}
|
||||
|
||||
ShortName = shortName;
|
||||
Name = name;
|
||||
Description = description;
|
||||
@@ -204,6 +216,8 @@ namespace OpenSim.Framework.Monitoring
|
||||
if (m_samples.Count >= m_maxSamples)
|
||||
m_samples.Dequeue();
|
||||
|
||||
// m_log.DebugFormat("[STAT]: Recording value {0} for {1}", newValue, Name);
|
||||
|
||||
m_samples.Enqueue(newValue);
|
||||
}
|
||||
}
|
||||
@@ -242,6 +256,10 @@ namespace OpenSim.Framework.Monitoring
|
||||
|
||||
lock (m_samples)
|
||||
{
|
||||
// m_log.DebugFormat(
|
||||
// "[STAT]: Samples for {0} are {1}",
|
||||
// Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray()));
|
||||
|
||||
foreach (double s in m_samples)
|
||||
{
|
||||
if (lastSample != null)
|
||||
@@ -253,7 +271,7 @@ namespace OpenSim.Framework.Monitoring
|
||||
|
||||
int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1;
|
||||
|
||||
sb.AppendFormat(", {0:0.##}{1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName);
|
||||
sb.AppendFormat(", {0:0.##} {1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenSim.Framework.Monitoring
|
||||
@@ -54,13 +55,13 @@ namespace OpenSim.Framework.Monitoring
|
||||
public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats
|
||||
= new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>();
|
||||
|
||||
private static AssetStatsCollector assetStats;
|
||||
private static UserStatsCollector userStats;
|
||||
private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
|
||||
// private static AssetStatsCollector assetStats;
|
||||
// private static UserStatsCollector userStats;
|
||||
// private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
|
||||
|
||||
public static AssetStatsCollector AssetStats { get { return assetStats; } }
|
||||
public static UserStatsCollector UserStats { get { return userStats; } }
|
||||
public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
|
||||
// public static AssetStatsCollector AssetStats { get { return assetStats; } }
|
||||
// public static UserStatsCollector UserStats { get { return userStats; } }
|
||||
public static SimExtraStatsCollector SimExtraStats { get; set; }
|
||||
|
||||
public static void RegisterConsoleCommands(ICommandConsole console)
|
||||
{
|
||||
@@ -68,12 +69,14 @@ namespace OpenSim.Framework.Monitoring
|
||||
"General",
|
||||
false,
|
||||
"show stats",
|
||||
"show stats [list|all|<category>]",
|
||||
"show stats [list|all|(<category>[.<container>])+",
|
||||
"Show statistical information for this server",
|
||||
"If no final argument is specified then legacy statistics information is currently shown.\n"
|
||||
+ "If list is specified then statistic categories are shown.\n"
|
||||
+ "If all is specified then all registered statistics are shown.\n"
|
||||
+ "If a category name is specified then only statistics from that category are shown.\n"
|
||||
+ "'list' argument will show statistic categories.\n"
|
||||
+ "'all' will show all statistics.\n"
|
||||
+ "A <category> name will show statistics from that category.\n"
|
||||
+ "A <category>.<container> name will show statistics from that category in that container.\n"
|
||||
+ "More than one name can be given separated by spaces.\n"
|
||||
+ "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
|
||||
HandleShowStatsCommand);
|
||||
}
|
||||
@@ -84,43 +87,47 @@ namespace OpenSim.Framework.Monitoring
|
||||
|
||||
if (cmd.Length > 2)
|
||||
{
|
||||
var categoryName = cmd[2];
|
||||
var containerName = cmd.Length > 3 ? cmd[3] : String.Empty;
|
||||
foreach (string name in cmd.Skip(2))
|
||||
{
|
||||
string[] components = name.Split('.');
|
||||
|
||||
if (categoryName == AllSubCommand)
|
||||
{
|
||||
foreach (var category in RegisteredStats.Values)
|
||||
string categoryName = components[0];
|
||||
string containerName = components.Length > 1 ? components[1] : null;
|
||||
|
||||
if (categoryName == AllSubCommand)
|
||||
{
|
||||
OutputCategoryStatsToConsole(con, category);
|
||||
OutputAllStatsToConsole(con);
|
||||
}
|
||||
}
|
||||
else if (categoryName == ListSubCommand)
|
||||
{
|
||||
con.Output("Statistic categories available are:");
|
||||
foreach (string category in RegisteredStats.Keys)
|
||||
con.OutputFormat(" {0}", category);
|
||||
}
|
||||
else
|
||||
{
|
||||
SortedDictionary<string, SortedDictionary<string, Stat>> category;
|
||||
if (!RegisteredStats.TryGetValue(categoryName, out category))
|
||||
else if (categoryName == ListSubCommand)
|
||||
{
|
||||
con.OutputFormat("No such category as {0}", categoryName);
|
||||
con.Output("Statistic categories available are:");
|
||||
foreach (string category in RegisteredStats.Keys)
|
||||
con.OutputFormat(" {0}", category);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (String.IsNullOrEmpty(containerName))
|
||||
OutputCategoryStatsToConsole(con, category);
|
||||
SortedDictionary<string, SortedDictionary<string, Stat>> category;
|
||||
if (!RegisteredStats.TryGetValue(categoryName, out category))
|
||||
{
|
||||
con.OutputFormat("No such category as {0}", categoryName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SortedDictionary<string, Stat> container;
|
||||
if (category.TryGetValue(containerName, out container))
|
||||
if (String.IsNullOrEmpty(containerName))
|
||||
{
|
||||
OutputContainerStatsToConsole(con, container);
|
||||
OutputCategoryStatsToConsole(con, category);
|
||||
}
|
||||
else
|
||||
{
|
||||
con.OutputFormat("No such container {0} in category {1}", containerName, categoryName);
|
||||
SortedDictionary<string, Stat> container;
|
||||
if (category.TryGetValue(containerName, out container))
|
||||
{
|
||||
OutputContainerStatsToConsole(con, container);
|
||||
}
|
||||
else
|
||||
{
|
||||
con.OutputFormat("No such container {0} in category {1}", containerName, categoryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +136,18 @@ namespace OpenSim.Framework.Monitoring
|
||||
else
|
||||
{
|
||||
// Legacy
|
||||
con.Output(SimExtraStats.Report());
|
||||
if (SimExtraStats != null)
|
||||
con.Output(SimExtraStats.Report());
|
||||
else
|
||||
OutputAllStatsToConsole(con);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OutputAllStatsToConsole(ICommandConsole con)
|
||||
{
|
||||
foreach (var category in RegisteredStats.Values)
|
||||
{
|
||||
OutputCategoryStatsToConsole(con, category);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,27 +168,27 @@ namespace OpenSim.Framework.Monitoring
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start collecting statistics related to assets.
|
||||
/// Should only be called once.
|
||||
/// </summary>
|
||||
public static AssetStatsCollector StartCollectingAssetStats()
|
||||
{
|
||||
assetStats = new AssetStatsCollector();
|
||||
|
||||
return assetStats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start collecting statistics related to users.
|
||||
/// Should only be called once.
|
||||
/// </summary>
|
||||
public static UserStatsCollector StartCollectingUserStats()
|
||||
{
|
||||
userStats = new UserStatsCollector();
|
||||
|
||||
return userStats;
|
||||
}
|
||||
// /// <summary>
|
||||
// /// Start collecting statistics related to assets.
|
||||
// /// Should only be called once.
|
||||
// /// </summary>
|
||||
// public static AssetStatsCollector StartCollectingAssetStats()
|
||||
// {
|
||||
// assetStats = new AssetStatsCollector();
|
||||
//
|
||||
// return assetStats;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Start collecting statistics related to users.
|
||||
// /// Should only be called once.
|
||||
// /// </summary>
|
||||
// public static UserStatsCollector StartCollectingUserStats()
|
||||
// {
|
||||
// userStats = new UserStatsCollector();
|
||||
//
|
||||
// return userStats;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Registers a statistic.
|
||||
|
||||
Reference in New Issue
Block a user