aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2013-06-17 22:39:00 +0100
committerJustin Clark-Casey (justincc)2013-06-17 22:39:00 +0100
commit0d2fd0d914581f755661455b8db2b9e399154632 (patch)
treeb3c8f5634d946d8e623f761eb7001f254d2af9be /OpenSim/Framework/Monitoring/ServerStatsCollector.cs
parentcorrect method doc for llRot2Axis() (diff)
downloadopensim-SC-0d2fd0d914581f755661455b8db2b9e399154632.zip
opensim-SC-0d2fd0d914581f755661455b8db2b9e399154632.tar.gz
opensim-SC-0d2fd0d914581f755661455b8db2b9e399154632.tar.bz2
opensim-SC-0d2fd0d914581f755661455b8db2b9e399154632.tar.xz
Make general server stats available on the robust console as well as the simulator console
This means the "show stats" command is now active on the robust console.
Diffstat (limited to 'OpenSim/Framework/Monitoring/ServerStatsCollector.cs')
-rw-r--r--OpenSim/Framework/Monitoring/ServerStatsCollector.cs309
1 files changed, 309 insertions, 0 deletions
diff --git a/OpenSim/Framework/Monitoring/ServerStatsCollector.cs b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
new file mode 100644
index 0000000..80d0a89
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
@@ -0,0 +1,309 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.Linq;
32using System.Net.NetworkInformation;
33using System.Text;
34using System.Threading;
35using log4net;
36using Nini.Config;
37using OpenMetaverse.StructuredData;
38using OpenSim.Framework;
39
40namespace OpenSim.Framework.Monitoring
41{
42 public class ServerStatsCollector
43 {
44 private readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
45 private readonly string LogHeader = "[SERVER STATS]";
46
47 public bool Enabled = false;
48 private static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>();
49
50 public readonly string CategoryServer = "server";
51
52 public readonly string ContainerProcessor = "processor";
53 public readonly string ContainerMemory = "memory";
54 public readonly string ContainerNetwork = "network";
55 public readonly string ContainerProcess = "process";
56
57 public string NetworkInterfaceTypes = "Ethernet";
58
59 readonly int performanceCounterSampleInterval = 500;
60// int lastperformanceCounterSampleTime = 0;
61
62 private class PerfCounterControl
63 {
64 public PerformanceCounter perfCounter;
65 public int lastFetch;
66 public string name;
67 public PerfCounterControl(PerformanceCounter pPc)
68 : this(pPc, String.Empty)
69 {
70 }
71 public PerfCounterControl(PerformanceCounter pPc, string pName)
72 {
73 perfCounter = pPc;
74 lastFetch = 0;
75 name = pName;
76 }
77 }
78
79 PerfCounterControl processorPercentPerfCounter = null;
80
81 // IRegionModuleBase.Initialize
82 public void Initialise(IConfigSource source)
83 {
84 IConfig cfg = source.Configs["Monitoring"];
85
86 if (cfg != null)
87 Enabled = cfg.GetBoolean("ServerStatsEnabled", true);
88
89 if (Enabled)
90 {
91 NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet");
92 }
93 }
94
95 public void Start()
96 {
97 if (RegisteredStats.Count == 0)
98 RegisterServerStats();
99 }
100
101 public void Close()
102 {
103 if (RegisteredStats.Count > 0)
104 {
105 foreach (Stat stat in RegisteredStats.Values)
106 {
107 StatsManager.DeregisterStat(stat);
108 stat.Dispose();
109 }
110 RegisteredStats.Clear();
111 }
112 }
113
114 private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act)
115 {
116 string desc = pDesc;
117 if (desc == null)
118 desc = pName;
119 Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, act, StatVerbosity.Info);
120 StatsManager.RegisterStat(stat);
121 RegisteredStats.Add(pName, stat);
122 }
123
124 public void RegisterServerStats()
125 {
126// lastperformanceCounterSampleTime = Util.EnvironmentTickCount();
127 PerformanceCounter tempPC;
128 Stat tempStat;
129 string tempName;
130
131 try
132 {
133 tempName = "CPUPercent";
134 tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total");
135 processorPercentPerfCounter = new PerfCounterControl(tempPC);
136 // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy.
137 tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor,
138 StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); },
139 StatVerbosity.Info);
140 StatsManager.RegisterStat(tempStat);
141 RegisteredStats.Add(tempName, tempStat);
142
143 MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor,
144 (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; });
145
146 MakeStat("UserProcessorTime", null, "sec", ContainerProcessor,
147 (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; });
148
149 MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor,
150 (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; });
151
152 MakeStat("Threads", null, "threads", ContainerProcessor,
153 (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; });
154 }
155 catch (Exception e)
156 {
157 m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e);
158 }
159
160 try
161 {
162 List<string> okInterfaceTypes = new List<string>(NetworkInterfaceTypes.Split(','));
163
164 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces();
165 foreach (NetworkInterface nic in nics)
166 {
167 if (nic.OperationalStatus != OperationalStatus.Up)
168 continue;
169
170 string nicInterfaceType = nic.NetworkInterfaceType.ToString();
171 if (!okInterfaceTypes.Contains(nicInterfaceType))
172 {
173 m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.",
174 LogHeader, nic.Name, nicInterfaceType);
175 m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}",
176 LogHeader, NetworkInterfaceTypes);
177 continue;
178 }
179
180 if (nic.Supports(NetworkInterfaceComponent.IPv4))
181 {
182 IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics();
183 if (nicStats != null)
184 {
185 MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork,
186 (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); });
187 MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork,
188 (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); });
189 MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork,
190 (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); });
191 }
192 }
193 // TODO: add IPv6 (it may actually happen someday)
194 }
195 }
196 catch (Exception e)
197 {
198 m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e);
199 }
200
201 MakeStat("ProcessMemory", null, "MB", ContainerMemory,
202 (s) => { s.Value = Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d; });
203 MakeStat("ObjectMemory", null, "MB", ContainerMemory,
204 (s) => { s.Value = GC.GetTotalMemory(false) / 1024d / 1024d; });
205 MakeStat("LastMemoryChurn", null, "MB/sec", ContainerMemory,
206 (s) => { s.Value = Math.Round(MemoryWatchdog.LastMemoryChurn * 1000d / 1024d / 1024d, 3); });
207 MakeStat("AverageMemoryChurn", null, "MB/sec", ContainerMemory,
208 (s) => { s.Value = Math.Round(MemoryWatchdog.AverageMemoryChurn * 1000d / 1024d / 1024d, 3); });
209 }
210
211 // Notes on performance counters:
212 // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx
213 // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c
214 // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters
215 private delegate double PerfCounterNextValue();
216 private void GetNextValue(Stat stat, PerfCounterControl perfControl)
217 {
218 GetNextValue(stat, perfControl, 1.0);
219 }
220 private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor)
221 {
222 if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval)
223 {
224 if (perfControl != null && perfControl.perfCounter != null)
225 {
226 try
227 {
228 // Kludge for factor to run double duty. If -1, subtract the value from one
229 if (factor == -1)
230 stat.Value = 1 - perfControl.perfCounter.NextValue();
231 else
232 stat.Value = perfControl.perfCounter.NextValue() / factor;
233 }
234 catch (Exception e)
235 {
236 m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e);
237 }
238 perfControl.lastFetch = Util.EnvironmentTickCount();
239 }
240 }
241 }
242
243 // Lookup the nic that goes with this stat and set the value by using a fetch action.
244 // Not sure about closure with delegates inside delegates.
245 private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat);
246 private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor)
247 {
248 // Get the one nic that has the name of this stat
249 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
250 (network) => network.Name == stat.Description);
251 try
252 {
253 foreach (NetworkInterface nic in nics)
254 {
255 IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics();
256 if (intrStats != null)
257 {
258 double newVal = Math.Round(getter(intrStats) / factor, 3);
259 stat.Value = newVal;
260 }
261 break;
262 }
263 }
264 catch
265 {
266 // There are times interfaces go away so we just won't update the stat for this
267 m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description);
268 }
269 }
270 }
271
272 public class ServerStatsAggregator : Stat
273 {
274 public ServerStatsAggregator(
275 string shortName,
276 string name,
277 string description,
278 string unitName,
279 string category,
280 string container
281 )
282 : base(
283 shortName,
284 name,
285 description,
286 unitName,
287 category,
288 container,
289 StatType.Push,
290 MeasuresOfInterest.None,
291 null,
292 StatVerbosity.Info)
293 {
294 }
295 public override string ToConsoleString()
296 {
297 StringBuilder sb = new StringBuilder();
298
299 return sb.ToString();
300 }
301
302 public override OSDMap ToOSDMap()
303 {
304 OSDMap ret = new OSDMap();
305
306 return ret;
307 }
308 }
309}