OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
StatsManager.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Linq;
32 using System.Text;
33 
34 using OpenSim.Framework;
35 using OpenMetaverse.StructuredData;
36 
37 namespace OpenSim.Framework.Monitoring
38 {
42  public static class StatsManager
43  {
44  // Subcommand used to list other stats.
45  public const string AllSubCommand = "all";
46 
47  // Subcommand used to list other stats.
48  public const string ListSubCommand = "list";
49 
50  // All subcommands
51  public static HashSet<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand };
52 
59  public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats
60  = new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>();
61 
62 // private static AssetStatsCollector assetStats;
63 // private static UserStatsCollector userStats;
64 // private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
65 
66 // public static AssetStatsCollector AssetStats { get { return assetStats; } }
67 // public static UserStatsCollector UserStats { get { return userStats; } }
68  public static SimExtraStatsCollector SimExtraStats { get; set; }
69 
70  public static void RegisterConsoleCommands(ICommandConsole console)
71  {
72  console.Commands.AddCommand(
73  "General",
74  false,
75  "stats show",
76  "stats show [list|all|(<category>[.<container>])+",
77  "Show statistical information for this server",
78  "If no final argument is specified then legacy statistics information is currently shown.\n"
79  + "'list' argument will show statistic categories.\n"
80  + "'all' will show all statistics.\n"
81  + "A <category> name will show statistics from that category.\n"
82  + "A <category>.<container> name will show statistics from that category in that container.\n"
83  + "More than one name can be given separated by spaces.\n"
84  + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
85  HandleShowStatsCommand);
86 
87  console.Commands.AddCommand(
88  "General",
89  false,
90  "show stats",
91  "show stats [list|all|(<category>[.<container>])+",
92  "Alias for 'stats show' command",
93  HandleShowStatsCommand);
94 
95  StatsLogger.RegisterConsoleCommands(console);
96  }
97 
98  public static void HandleShowStatsCommand(string module, string[] cmd)
99  {
100  ICommandConsole con = MainConsole.Instance;
101 
102  if (cmd.Length > 2)
103  {
104  foreach (string name in cmd.Skip(2))
105  {
106  string[] components = name.Split('.');
107 
108  string categoryName = components[0];
109  string containerName = components.Length > 1 ? components[1] : null;
110  string statName = components.Length > 2 ? components[2] : null;
111 
112  if (categoryName == AllSubCommand)
113  {
114  OutputAllStatsToConsole(con);
115  }
116  else if (categoryName == ListSubCommand)
117  {
118  con.Output("Statistic categories available are:");
119  foreach (string category in RegisteredStats.Keys)
120  con.OutputFormat(" {0}", category);
121  }
122  else
123  {
124  SortedDictionary<string, SortedDictionary<string, Stat>> category;
125  if (!RegisteredStats.TryGetValue(categoryName, out category))
126  {
127  con.OutputFormat("No such category as {0}", categoryName);
128  }
129  else
130  {
131  if (String.IsNullOrEmpty(containerName))
132  {
133  OutputCategoryStatsToConsole(con, category);
134  }
135  else
136  {
137  SortedDictionary<string, Stat> container;
138  if (category.TryGetValue(containerName, out container))
139  {
140  if (String.IsNullOrEmpty(statName))
141  {
142  OutputContainerStatsToConsole(con, container);
143  }
144  else
145  {
146  Stat stat;
147  if (container.TryGetValue(statName, out stat))
148  {
149  OutputStatToConsole(con, stat);
150  }
151  else
152  {
153  con.OutputFormat(
154  "No such stat {0} in {1}.{2}", statName, categoryName, containerName);
155  }
156  }
157  }
158  else
159  {
160  con.OutputFormat("No such container {0} in category {1}", containerName, categoryName);
161  }
162  }
163  }
164  }
165  }
166  }
167  else
168  {
169  // Legacy
170  if (SimExtraStats != null)
171  con.Output(SimExtraStats.Report());
172  else
173  OutputAllStatsToConsole(con);
174  }
175  }
176 
177  public static List<string> GetAllStatsReports()
178  {
179  List<string> reports = new List<string>();
180 
181  foreach (var category in RegisteredStats.Values)
182  reports.AddRange(GetCategoryStatsReports(category));
183 
184  return reports;
185  }
186 
187  private static void OutputAllStatsToConsole(ICommandConsole con)
188  {
189  foreach (string report in GetAllStatsReports())
190  con.Output(report);
191  }
192 
193  private static List<string> GetCategoryStatsReports(
194  SortedDictionary<string, SortedDictionary<string, Stat>> category)
195  {
196  List<string> reports = new List<string>();
197 
198  foreach (var container in category.Values)
199  reports.AddRange(GetContainerStatsReports(container));
200 
201  return reports;
202  }
203 
204  private static void OutputCategoryStatsToConsole(
205  ICommandConsole con, SortedDictionary<string, SortedDictionary<string, Stat>> category)
206  {
207  foreach (string report in GetCategoryStatsReports(category))
208  con.Output(report);
209  }
210 
211  private static List<string> GetContainerStatsReports(SortedDictionary<string, Stat> container)
212  {
213  List<string> reports = new List<string>();
214 
215  foreach (Stat stat in container.Values)
216  reports.Add(stat.ToConsoleString());
217 
218  return reports;
219  }
220 
221  private static void OutputContainerStatsToConsole(
222  ICommandConsole con, SortedDictionary<string, Stat> container)
223  {
224  foreach (string report in GetContainerStatsReports(container))
225  con.Output(report);
226  }
227 
228  private static void OutputStatToConsole(ICommandConsole con, Stat stat)
229  {
230  con.Output(stat.ToConsoleString());
231  }
232 
233  // Creates an OSDMap of the format:
234  // { categoryName: {
235  // containerName: {
236  // statName: {
237  // "Name": name,
238  // "ShortName": shortName,
239  // ...
240  // },
241  // statName: {
242  // "Name": name,
243  // "ShortName": shortName,
244  // ...
245  // },
246  // ...
247  // },
248  // containerName: {
249  // ...
250  // },
251  // ...
252  // },
253  // categoryName: {
254  // ...
255  // },
256  // ...
257  // }
258  // The passed in parameters will filter the categories, containers and stats returned. If any of the
259  // parameters are either EmptyOrNull or the AllSubCommand value, all of that type will be returned.
260  // Case matters.
261  public static OSDMap GetStatsAsOSDMap(string pCategoryName, string pContainerName, string pStatName)
262  {
263  OSDMap map = new OSDMap();
264 
265  lock (RegisteredStats)
266  {
267  foreach (string catName in RegisteredStats.Keys)
268  {
269  // Do this category if null spec, "all" subcommand or category name matches passed parameter.
270  // Skip category if none of the above.
271  if (!(String.IsNullOrEmpty(pCategoryName) || pCategoryName == AllSubCommand || pCategoryName == catName))
272  continue;
273 
274  OSDMap contMap = new OSDMap();
275  foreach (string contName in RegisteredStats[catName].Keys)
276  {
277  if (!(string.IsNullOrEmpty(pContainerName) || pContainerName == AllSubCommand || pContainerName == contName))
278  continue;
279 
280  OSDMap statMap = new OSDMap();
281 
282  SortedDictionary<string, Stat> theStats = RegisteredStats[catName][contName];
283  foreach (string statName in theStats.Keys)
284  {
285  if (!(String.IsNullOrEmpty(pStatName) || pStatName == AllSubCommand || pStatName == statName))
286  continue;
287 
288  statMap.Add(statName, theStats[statName].ToBriefOSDMap());
289  }
290 
291  contMap.Add(contName, statMap);
292  }
293  map.Add(catName, contMap);
294  }
295  }
296 
297  return map;
298  }
299 
300  public static Hashtable HandleStatsRequest(Hashtable request)
301  {
302  Hashtable responsedata = new Hashtable();
303 // string regpath = request["uri"].ToString();
304  int response_code = 200;
305  string contenttype = "text/json";
306 
307  string pCategoryName = StatsManager.AllSubCommand;
308  string pContainerName = StatsManager.AllSubCommand;
309  string pStatName = StatsManager.AllSubCommand;
310 
311  if (request.ContainsKey("cat")) pCategoryName = request["cat"].ToString();
312  if (request.ContainsKey("cont")) pContainerName = request["cat"].ToString();
313  if (request.ContainsKey("stat")) pStatName = request["stat"].ToString();
314 
315  string strOut = StatsManager.GetStatsAsOSDMap(pCategoryName, pContainerName, pStatName).ToString();
316 
317  // If requestor wants it as a callback function, build response as a function rather than just the JSON string.
318  if (request.ContainsKey("callback"))
319  {
320  strOut = request["callback"].ToString() + "(" + strOut + ");";
321  }
322 
323  // m_log.DebugFormat("{0} StatFetch: uri={1}, cat={2}, cont={3}, stat={4}, resp={5}",
324  // LogHeader, regpath, pCategoryName, pContainerName, pStatName, strOut);
325 
326  responsedata["int_response_code"] = response_code;
327  responsedata["content_type"] = contenttype;
328  responsedata["keepalive"] = false;
329  responsedata["str_response_string"] = strOut;
330  responsedata["access_control_allow_origin"] = "*";
331 
332  return responsedata;
333  }
334 
335 // /// <summary>
336 // /// Start collecting statistics related to assets.
337 // /// Should only be called once.
338 // /// </summary>
339 // public static AssetStatsCollector StartCollectingAssetStats()
340 // {
341 // assetStats = new AssetStatsCollector();
342 //
343 // return assetStats;
344 // }
345 //
346 // /// <summary>
347 // /// Start collecting statistics related to users.
348 // /// Should only be called once.
349 // /// </summary>
350 // public static UserStatsCollector StartCollectingUserStats()
351 // {
352 // userStats = new UserStatsCollector();
353 //
354 // return userStats;
355 // }
356 
362  public static bool RegisterStat(Stat stat)
363  {
364  SortedDictionary<string, SortedDictionary<string, Stat>> category = null, newCategory;
365  SortedDictionary<string, Stat> container = null, newContainer;
366 
367  lock (RegisteredStats)
368  {
369  // Stat name is not unique across category/container/shortname key.
370  // XXX: For now just return false. This is to avoid problems in regression tests where all tests
371  // in a class are run in the same instance of the VM.
372  if (TryGetStatParents(stat, out category, out container))
373  return false;
374 
375  // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed.
376  // This means that we don't need to lock or copy them on iteration, which will be a much more
377  // common operation after startup.
378  if (container != null)
379  newContainer = new SortedDictionary<string, Stat>(container);
380  else
381  newContainer = new SortedDictionary<string, Stat>();
382 
383  if (category != null)
384  newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>(category);
385  else
386  newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>();
387 
388  newContainer[stat.ShortName] = stat;
389  newCategory[stat.Container] = newContainer;
390  RegisteredStats[stat.Category] = newCategory;
391  }
392 
393  return true;
394  }
395 
401  public static bool DeregisterStat(Stat stat)
402  {
403  SortedDictionary<string, SortedDictionary<string, Stat>> category = null, newCategory;
404  SortedDictionary<string, Stat> container = null, newContainer;
405 
406  lock (RegisteredStats)
407  {
408  if (!TryGetStatParents(stat, out category, out container))
409  return false;
410 
411  newContainer = new SortedDictionary<string, Stat>(container);
412  newContainer.Remove(stat.ShortName);
413 
414  newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>(category);
415  newCategory.Remove(stat.Container);
416 
417  newCategory[stat.Container] = newContainer;
418  RegisteredStats[stat.Category] = newCategory;
419 
420  return true;
421  }
422  }
423 
424  public static bool TryGetStat(string category, string container, string statShortName, out Stat stat)
425  {
426  stat = null;
427  SortedDictionary<string, SortedDictionary<string, Stat>> categoryStats;
428 
429  lock (RegisteredStats)
430  {
431  if (!TryGetStatsForCategory(category, out categoryStats))
432  return false;
433 
434  SortedDictionary<string, Stat> containerStats;
435 
436  if (!categoryStats.TryGetValue(container, out containerStats))
437  return false;
438 
439  return containerStats.TryGetValue(statShortName, out stat);
440  }
441  }
442 
443  public static bool TryGetStatsForCategory(
444  string category, out SortedDictionary<string, SortedDictionary<string, Stat>> stats)
445  {
446  lock (RegisteredStats)
447  return RegisteredStats.TryGetValue(category, out stats);
448  }
449 
458  public static List<Stat> GetStatsFromEachContainer(string category, string statShortName)
459  {
460  SortedDictionary<string, SortedDictionary<string, Stat>> categoryStats;
461 
462  lock (RegisteredStats)
463  {
464  if (!RegisteredStats.TryGetValue(category, out categoryStats))
465  return null;
466 
467  List<Stat> stats = null;
468 
469  foreach (SortedDictionary<string, Stat> containerStats in categoryStats.Values)
470  {
471  if (containerStats.ContainsKey(statShortName))
472  {
473  if (stats == null)
474  stats = new List<Stat>();
475 
476  stats.Add(containerStats[statShortName]);
477  }
478  }
479 
480  return stats;
481  }
482  }
483 
484  public static bool TryGetStatParents(
485  Stat stat,
486  out SortedDictionary<string, SortedDictionary<string, Stat>> category,
487  out SortedDictionary<string, Stat> container)
488  {
489  category = null;
490  container = null;
491 
492  lock (RegisteredStats)
493  {
494  if (RegisteredStats.TryGetValue(stat.Category, out category))
495  {
496  if (category.TryGetValue(stat.Container, out container))
497  {
498  if (container.ContainsKey(stat.ShortName))
499  return true;
500  }
501  }
502  }
503 
504  return false;
505  }
506 
507  public static void RecordStats()
508  {
509  lock (RegisteredStats)
510  {
511  foreach (SortedDictionary<string, SortedDictionary<string, Stat>> category in RegisteredStats.Values)
512  {
513  foreach (SortedDictionary<string, Stat> container in category.Values)
514  {
515  foreach (Stat stat in container.Values)
516  {
517  if (stat.MeasuresOfInterest != MeasuresOfInterest.None)
518  stat.RecordValue();
519  }
520  }
521  }
522  }
523  }
524  }
525 
533  public enum StatType
534  {
535  Push,
536  Pull
537  }
538 
542  [Flags]
543  public enum MeasuresOfInterest
544  {
545  None,
547  }
548 
555  public enum StatVerbosity
556  {
557  Debug,
558  Info
559  }
560 }
OpenMetaverse.StructuredData.OSDMap OSDMap
StatVerbosity
Verbosity of stat.
MeasuresOfInterest
Measures of interest for this stat.