diff options
Diffstat (limited to 'OpenSim/Framework/Monitoring/StatsManager.cs')
-rw-r--r-- | OpenSim/Framework/Monitoring/StatsManager.cs | 334 |
1 files changed, 269 insertions, 65 deletions
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index 24db6d4..05ee4c5 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs | |||
@@ -26,15 +26,20 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | ||
29 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
31 | using System.Linq; | ||
30 | using System.Text; | 32 | using System.Text; |
31 | 33 | ||
34 | using OpenSim.Framework; | ||
35 | using OpenMetaverse.StructuredData; | ||
36 | |||
32 | namespace OpenSim.Framework.Monitoring | 37 | namespace OpenSim.Framework.Monitoring |
33 | { | 38 | { |
34 | /// <summary> | 39 | /// <summary> |
35 | /// Singleton used to provide access to statistics reporters | 40 | /// Static class used to register/deregister/fetch statistics |
36 | /// </summary> | 41 | /// </summary> |
37 | public class StatsManager | 42 | public static class StatsManager |
38 | { | 43 | { |
39 | // Subcommand used to list other stats. | 44 | // Subcommand used to list other stats. |
40 | public const string AllSubCommand = "all"; | 45 | public const string AllSubCommand = "all"; |
@@ -54,13 +59,13 @@ namespace OpenSim.Framework.Monitoring | |||
54 | public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats | 59 | public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats |
55 | = new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>(); | 60 | = new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>(); |
56 | 61 | ||
57 | private static AssetStatsCollector assetStats; | 62 | // private static AssetStatsCollector assetStats; |
58 | private static UserStatsCollector userStats; | 63 | // private static UserStatsCollector userStats; |
59 | private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); | 64 | // private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); |
60 | 65 | ||
61 | public static AssetStatsCollector AssetStats { get { return assetStats; } } | 66 | // public static AssetStatsCollector AssetStats { get { return assetStats; } } |
62 | public static UserStatsCollector UserStats { get { return userStats; } } | 67 | // public static UserStatsCollector UserStats { get { return userStats; } } |
63 | public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } | 68 | public static SimExtraStatsCollector SimExtraStats { get; set; } |
64 | 69 | ||
65 | public static void RegisterConsoleCommands(ICommandConsole console) | 70 | public static void RegisterConsoleCommands(ICommandConsole console) |
66 | { | 71 | { |
@@ -68,14 +73,18 @@ namespace OpenSim.Framework.Monitoring | |||
68 | "General", | 73 | "General", |
69 | false, | 74 | false, |
70 | "show stats", | 75 | "show stats", |
71 | "show stats [list|all|<category>]", | 76 | "show stats [list|all|(<category>[.<container>])+", |
72 | "Show statistical information for this server", | 77 | "Show statistical information for this server", |
73 | "If no final argument is specified then legacy statistics information is currently shown.\n" | 78 | "If no final argument is specified then legacy statistics information is currently shown.\n" |
74 | + "If list is specified then statistic categories are shown.\n" | 79 | + "'list' argument will show statistic categories.\n" |
75 | + "If all is specified then all registered statistics are shown.\n" | 80 | + "'all' will show all statistics.\n" |
76 | + "If a category name is specified then only statistics from that category are shown.\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" | ||
77 | + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS", | 84 | + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS", |
78 | HandleShowStatsCommand); | 85 | HandleShowStatsCommand); |
86 | |||
87 | StatsLogger.RegisterConsoleCommands(console); | ||
79 | } | 88 | } |
80 | 89 | ||
81 | public static void HandleShowStatsCommand(string module, string[] cmd) | 90 | public static void HandleShowStatsCommand(string module, string[] cmd) |
@@ -84,43 +93,47 @@ namespace OpenSim.Framework.Monitoring | |||
84 | 93 | ||
85 | if (cmd.Length > 2) | 94 | if (cmd.Length > 2) |
86 | { | 95 | { |
87 | var categoryName = cmd[2]; | 96 | foreach (string name in cmd.Skip(2)) |
88 | var containerName = cmd.Length > 3 ? cmd[3] : String.Empty; | ||
89 | |||
90 | if (categoryName == AllSubCommand) | ||
91 | { | 97 | { |
92 | foreach (var category in RegisteredStats.Values) | 98 | string[] components = name.Split('.'); |
99 | |||
100 | string categoryName = components[0]; | ||
101 | string containerName = components.Length > 1 ? components[1] : null; | ||
102 | |||
103 | if (categoryName == AllSubCommand) | ||
93 | { | 104 | { |
94 | OutputCategoryStatsToConsole(con, category); | 105 | OutputAllStatsToConsole(con); |
95 | } | 106 | } |
96 | } | 107 | else if (categoryName == ListSubCommand) |
97 | else if (categoryName == ListSubCommand) | ||
98 | { | ||
99 | con.Output("Statistic categories available are:"); | ||
100 | foreach (string category in RegisteredStats.Keys) | ||
101 | con.OutputFormat(" {0}", category); | ||
102 | } | ||
103 | else | ||
104 | { | ||
105 | SortedDictionary<string, SortedDictionary<string, Stat>> category; | ||
106 | if (!RegisteredStats.TryGetValue(categoryName, out category)) | ||
107 | { | 108 | { |
108 | con.OutputFormat("No such category as {0}", categoryName); | 109 | con.Output("Statistic categories available are:"); |
110 | foreach (string category in RegisteredStats.Keys) | ||
111 | con.OutputFormat(" {0}", category); | ||
109 | } | 112 | } |
110 | else | 113 | else |
111 | { | 114 | { |
112 | if (String.IsNullOrEmpty(containerName)) | 115 | SortedDictionary<string, SortedDictionary<string, Stat>> category; |
113 | OutputCategoryStatsToConsole(con, category); | 116 | if (!RegisteredStats.TryGetValue(categoryName, out category)) |
117 | { | ||
118 | con.OutputFormat("No such category as {0}", categoryName); | ||
119 | } | ||
114 | else | 120 | else |
115 | { | 121 | { |
116 | SortedDictionary<string, Stat> container; | 122 | if (String.IsNullOrEmpty(containerName)) |
117 | if (category.TryGetValue(containerName, out container)) | ||
118 | { | 123 | { |
119 | OutputContainerStatsToConsole(con, container); | 124 | OutputCategoryStatsToConsole(con, category); |
120 | } | 125 | } |
121 | else | 126 | else |
122 | { | 127 | { |
123 | con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); | 128 | SortedDictionary<string, Stat> container; |
129 | if (category.TryGetValue(containerName, out container)) | ||
130 | { | ||
131 | OutputContainerStatsToConsole(con, container); | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); | ||
136 | } | ||
124 | } | 137 | } |
125 | } | 138 | } |
126 | } | 139 | } |
@@ -129,51 +142,187 @@ namespace OpenSim.Framework.Monitoring | |||
129 | else | 142 | else |
130 | { | 143 | { |
131 | // Legacy | 144 | // Legacy |
132 | con.Output(SimExtraStats.Report()); | 145 | if (SimExtraStats != null) |
146 | con.Output(SimExtraStats.Report()); | ||
147 | else | ||
148 | OutputAllStatsToConsole(con); | ||
133 | } | 149 | } |
134 | } | 150 | } |
135 | 151 | ||
152 | public static List<string> GetAllStatsReports() | ||
153 | { | ||
154 | List<string> reports = new List<string>(); | ||
155 | |||
156 | foreach (var category in RegisteredStats.Values) | ||
157 | reports.AddRange(GetCategoryStatsReports(category)); | ||
158 | |||
159 | return reports; | ||
160 | } | ||
161 | |||
162 | private static void OutputAllStatsToConsole(ICommandConsole con) | ||
163 | { | ||
164 | foreach (string report in GetAllStatsReports()) | ||
165 | con.Output(report); | ||
166 | } | ||
167 | |||
168 | private static List<string> GetCategoryStatsReports( | ||
169 | SortedDictionary<string, SortedDictionary<string, Stat>> category) | ||
170 | { | ||
171 | List<string> reports = new List<string>(); | ||
172 | |||
173 | foreach (var container in category.Values) | ||
174 | reports.AddRange(GetContainerStatsReports(container)); | ||
175 | |||
176 | return reports; | ||
177 | } | ||
178 | |||
136 | private static void OutputCategoryStatsToConsole( | 179 | private static void OutputCategoryStatsToConsole( |
137 | ICommandConsole con, SortedDictionary<string, SortedDictionary<string, Stat>> category) | 180 | ICommandConsole con, SortedDictionary<string, SortedDictionary<string, Stat>> category) |
138 | { | 181 | { |
139 | foreach (var container in category.Values) | 182 | foreach (string report in GetCategoryStatsReports(category)) |
140 | { | 183 | con.Output(report); |
141 | OutputContainerStatsToConsole(con, container); | ||
142 | } | ||
143 | } | 184 | } |
144 | 185 | ||
145 | private static void OutputContainerStatsToConsole( ICommandConsole con, SortedDictionary<string, Stat> container) | 186 | private static List<string> GetContainerStatsReports(SortedDictionary<string, Stat> container) |
146 | { | 187 | { |
188 | List<string> reports = new List<string>(); | ||
189 | |||
147 | foreach (Stat stat in container.Values) | 190 | foreach (Stat stat in container.Values) |
148 | { | 191 | reports.Add(stat.ToConsoleString()); |
149 | con.Output(stat.ToConsoleString()); | 192 | |
150 | } | 193 | return reports; |
151 | } | 194 | } |
152 | 195 | ||
153 | /// <summary> | 196 | private static void OutputContainerStatsToConsole( |
154 | /// Start collecting statistics related to assets. | 197 | ICommandConsole con, SortedDictionary<string, Stat> container) |
155 | /// Should only be called once. | 198 | { |
156 | /// </summary> | 199 | foreach (string report in GetContainerStatsReports(container)) |
157 | public static AssetStatsCollector StartCollectingAssetStats() | 200 | con.Output(report); |
201 | } | ||
202 | |||
203 | // Creates an OSDMap of the format: | ||
204 | // { categoryName: { | ||
205 | // containerName: { | ||
206 | // statName: { | ||
207 | // "Name": name, | ||
208 | // "ShortName": shortName, | ||
209 | // ... | ||
210 | // }, | ||
211 | // statName: { | ||
212 | // "Name": name, | ||
213 | // "ShortName": shortName, | ||
214 | // ... | ||
215 | // }, | ||
216 | // ... | ||
217 | // }, | ||
218 | // containerName: { | ||
219 | // ... | ||
220 | // }, | ||
221 | // ... | ||
222 | // }, | ||
223 | // categoryName: { | ||
224 | // ... | ||
225 | // }, | ||
226 | // ... | ||
227 | // } | ||
228 | // The passed in parameters will filter the categories, containers and stats returned. If any of the | ||
229 | // parameters are either EmptyOrNull or the AllSubCommand value, all of that type will be returned. | ||
230 | // Case matters. | ||
231 | public static OSDMap GetStatsAsOSDMap(string pCategoryName, string pContainerName, string pStatName) | ||
158 | { | 232 | { |
159 | assetStats = new AssetStatsCollector(); | 233 | OSDMap map = new OSDMap(); |
234 | |||
235 | foreach (string catName in RegisteredStats.Keys) | ||
236 | { | ||
237 | // Do this category if null spec, "all" subcommand or category name matches passed parameter. | ||
238 | // Skip category if none of the above. | ||
239 | if (!(String.IsNullOrEmpty(pCategoryName) || pCategoryName == AllSubCommand || pCategoryName == catName)) | ||
240 | continue; | ||
160 | 241 | ||
161 | return assetStats; | 242 | OSDMap contMap = new OSDMap(); |
243 | foreach (string contName in RegisteredStats[catName].Keys) | ||
244 | { | ||
245 | if (!(string.IsNullOrEmpty(pContainerName) || pContainerName == AllSubCommand || pContainerName == contName)) | ||
246 | continue; | ||
247 | |||
248 | OSDMap statMap = new OSDMap(); | ||
249 | |||
250 | SortedDictionary<string, Stat> theStats = RegisteredStats[catName][contName]; | ||
251 | foreach (string statName in theStats.Keys) | ||
252 | { | ||
253 | if (!(String.IsNullOrEmpty(pStatName) || pStatName == AllSubCommand || pStatName == statName)) | ||
254 | continue; | ||
255 | |||
256 | statMap.Add(statName, theStats[statName].ToOSDMap()); | ||
257 | } | ||
258 | |||
259 | contMap.Add(contName, statMap); | ||
260 | } | ||
261 | map.Add(catName, contMap); | ||
262 | } | ||
263 | |||
264 | return map; | ||
162 | } | 265 | } |
163 | 266 | ||
164 | /// <summary> | 267 | public static Hashtable HandleStatsRequest(Hashtable request) |
165 | /// Start collecting statistics related to users. | ||
166 | /// Should only be called once. | ||
167 | /// </summary> | ||
168 | public static UserStatsCollector StartCollectingUserStats() | ||
169 | { | 268 | { |
170 | userStats = new UserStatsCollector(); | 269 | Hashtable responsedata = new Hashtable(); |
270 | // string regpath = request["uri"].ToString(); | ||
271 | int response_code = 200; | ||
272 | string contenttype = "text/json"; | ||
273 | |||
274 | string pCategoryName = StatsManager.AllSubCommand; | ||
275 | string pContainerName = StatsManager.AllSubCommand; | ||
276 | string pStatName = StatsManager.AllSubCommand; | ||
277 | |||
278 | if (request.ContainsKey("cat")) pCategoryName = request["cat"].ToString(); | ||
279 | if (request.ContainsKey("cont")) pContainerName = request["cat"].ToString(); | ||
280 | if (request.ContainsKey("stat")) pStatName = request["cat"].ToString(); | ||
281 | |||
282 | string strOut = StatsManager.GetStatsAsOSDMap(pCategoryName, pContainerName, pStatName).ToString(); | ||
283 | |||
284 | // If requestor wants it as a callback function, build response as a function rather than just the JSON string. | ||
285 | if (request.ContainsKey("callback")) | ||
286 | { | ||
287 | strOut = request["callback"].ToString() + "(" + strOut + ");"; | ||
288 | } | ||
171 | 289 | ||
172 | return userStats; | 290 | // m_log.DebugFormat("{0} StatFetch: uri={1}, cat={2}, cont={3}, stat={4}, resp={5}", |
291 | // LogHeader, regpath, pCategoryName, pContainerName, pStatName, strOut); | ||
292 | |||
293 | responsedata["int_response_code"] = response_code; | ||
294 | responsedata["content_type"] = contenttype; | ||
295 | responsedata["keepalive"] = false; | ||
296 | responsedata["str_response_string"] = strOut; | ||
297 | responsedata["access_control_allow_origin"] = "*"; | ||
298 | |||
299 | return responsedata; | ||
173 | } | 300 | } |
174 | 301 | ||
302 | // /// <summary> | ||
303 | // /// Start collecting statistics related to assets. | ||
304 | // /// Should only be called once. | ||
305 | // /// </summary> | ||
306 | // public static AssetStatsCollector StartCollectingAssetStats() | ||
307 | // { | ||
308 | // assetStats = new AssetStatsCollector(); | ||
309 | // | ||
310 | // return assetStats; | ||
311 | // } | ||
312 | // | ||
313 | // /// <summary> | ||
314 | // /// Start collecting statistics related to users. | ||
315 | // /// Should only be called once. | ||
316 | // /// </summary> | ||
317 | // public static UserStatsCollector StartCollectingUserStats() | ||
318 | // { | ||
319 | // userStats = new UserStatsCollector(); | ||
320 | // | ||
321 | // return userStats; | ||
322 | // } | ||
323 | |||
175 | /// <summary> | 324 | /// <summary> |
176 | /// Registers a statistic. | 325 | /// Register a statistic. |
177 | /// </summary> | 326 | /// </summary> |
178 | /// <param name='stat'></param> | 327 | /// <param name='stat'></param> |
179 | /// <returns></returns> | 328 | /// <returns></returns> |
@@ -187,7 +336,7 @@ namespace OpenSim.Framework.Monitoring | |||
187 | // Stat name is not unique across category/container/shortname key. | 336 | // Stat name is not unique across category/container/shortname key. |
188 | // XXX: For now just return false. This is to avoid problems in regression tests where all tests | 337 | // XXX: For now just return false. This is to avoid problems in regression tests where all tests |
189 | // in a class are run in the same instance of the VM. | 338 | // in a class are run in the same instance of the VM. |
190 | if (TryGetStat(stat, out category, out container)) | 339 | if (TryGetStatParents(stat, out category, out container)) |
191 | return false; | 340 | return false; |
192 | 341 | ||
193 | // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. | 342 | // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. |
@@ -223,7 +372,7 @@ namespace OpenSim.Framework.Monitoring | |||
223 | 372 | ||
224 | lock (RegisteredStats) | 373 | lock (RegisteredStats) |
225 | { | 374 | { |
226 | if (!TryGetStat(stat, out category, out container)) | 375 | if (!TryGetStatParents(stat, out category, out container)) |
227 | return false; | 376 | return false; |
228 | 377 | ||
229 | newContainer = new SortedDictionary<string, Stat>(container); | 378 | newContainer = new SortedDictionary<string, Stat>(container); |
@@ -239,12 +388,67 @@ namespace OpenSim.Framework.Monitoring | |||
239 | } | 388 | } |
240 | } | 389 | } |
241 | 390 | ||
242 | public static bool TryGetStats(string category, out SortedDictionary<string, SortedDictionary<string, Stat>> stats) | 391 | public static bool TryGetStat(string category, string container, string statShortName, out Stat stat) |
243 | { | 392 | { |
244 | return RegisteredStats.TryGetValue(category, out stats); | 393 | stat = null; |
394 | SortedDictionary<string, SortedDictionary<string, Stat>> categoryStats; | ||
395 | |||
396 | lock (RegisteredStats) | ||
397 | { | ||
398 | if (!TryGetStatsForCategory(category, out categoryStats)) | ||
399 | return false; | ||
400 | |||
401 | SortedDictionary<string, Stat> containerStats; | ||
402 | |||
403 | if (!categoryStats.TryGetValue(container, out containerStats)) | ||
404 | return false; | ||
405 | |||
406 | return containerStats.TryGetValue(statShortName, out stat); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | public static bool TryGetStatsForCategory( | ||
411 | string category, out SortedDictionary<string, SortedDictionary<string, Stat>> stats) | ||
412 | { | ||
413 | lock (RegisteredStats) | ||
414 | return RegisteredStats.TryGetValue(category, out stats); | ||
415 | } | ||
416 | |||
417 | /// <summary> | ||
418 | /// Get the same stat for each container in a given category. | ||
419 | /// </summary> | ||
420 | /// <returns> | ||
421 | /// The stats if there were any to fetch. Otherwise null. | ||
422 | /// </returns> | ||
423 | /// <param name='category'></param> | ||
424 | /// <param name='statShortName'></param> | ||
425 | public static List<Stat> GetStatsFromEachContainer(string category, string statShortName) | ||
426 | { | ||
427 | SortedDictionary<string, SortedDictionary<string, Stat>> categoryStats; | ||
428 | |||
429 | lock (RegisteredStats) | ||
430 | { | ||
431 | if (!RegisteredStats.TryGetValue(category, out categoryStats)) | ||
432 | return null; | ||
433 | |||
434 | List<Stat> stats = null; | ||
435 | |||
436 | foreach (SortedDictionary<string, Stat> containerStats in categoryStats.Values) | ||
437 | { | ||
438 | if (containerStats.ContainsKey(statShortName)) | ||
439 | { | ||
440 | if (stats == null) | ||
441 | stats = new List<Stat>(); | ||
442 | |||
443 | stats.Add(containerStats[statShortName]); | ||
444 | } | ||
445 | } | ||
446 | |||
447 | return stats; | ||
448 | } | ||
245 | } | 449 | } |
246 | 450 | ||
247 | public static bool TryGetStat( | 451 | public static bool TryGetStatParents( |
248 | Stat stat, | 452 | Stat stat, |
249 | out SortedDictionary<string, SortedDictionary<string, Stat>> category, | 453 | out SortedDictionary<string, SortedDictionary<string, Stat>> category, |
250 | out SortedDictionary<string, Stat> container) | 454 | out SortedDictionary<string, Stat> container) |