1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4
5 #include <Ice/MetricsAdminI.h>
6
7 #include <Ice/InstrumentationI.h>
8 #include <Ice/Properties.h>
9 #include <Ice/Logger.h>
10 #include <Ice/Communicator.h>
11 #include <Ice/Instance.h>
12 #include <Ice/LoggerUtil.h>
13
14 #include <IceUtil/StringUtil.h>
15
16 using namespace std;
17 using namespace Ice;
18 using namespace IceInternal;
19 using namespace IceMX;
20
21 namespace
22 {
23
24 const string suffixes[] =
25 {
26 "Disabled",
27 "GroupBy",
28 "Accept.*",
29 "Reject.*",
30 "RetainDetached",
31 "Map.*",
32 };
33
34 void
validateProperties(const string & prefix,const PropertiesPtr & properties)35 validateProperties(const string& prefix, const PropertiesPtr& properties)
36 {
37 vector<string> unknownProps;
38 PropertyDict props = properties->getPropertiesForPrefix(prefix);
39 for(PropertyDict::const_iterator p = props.begin(); p != props.end(); ++p)
40 {
41 bool valid = false;
42 for(size_t i = 0; i < sizeof(suffixes) / sizeof(*suffixes); ++i)
43 {
44 string prop = prefix + suffixes[i];
45 if(IceUtilInternal::match(p->first, prop))
46 {
47 valid = true;
48 break;
49 }
50 }
51 if(!valid)
52 {
53 unknownProps.push_back(p->first);
54 }
55 }
56
57 if(!unknownProps.empty() && properties->getPropertyAsIntWithDefault("Ice.Warn.UnknownProperties", 1) > 0)
58 {
59 Warning out(getProcessLogger());
60 out << "found unknown IceMX properties for '" << prefix.substr(0, prefix.size() - 1) << "':";
61 for(vector<string>::const_iterator p = unknownProps.begin(); p != unknownProps.end(); ++p)
62 {
63 out << "\n " << *p;
64 properties->setProperty(*p, ""); // Clear the known property to prevent further warnings.
65 }
66 }
67 }
68
69 vector<MetricsMapI::RegExpPtr>
parseRule(const PropertiesPtr & properties,const string & name)70 parseRule(const PropertiesPtr& properties, const string& name)
71 {
72 vector<MetricsMapI::RegExpPtr> regexps;
73 PropertyDict rules = properties->getPropertiesForPrefix(name + '.');
74 for(PropertyDict::const_iterator p = rules.begin(); p != rules.end(); ++p)
75 {
76 try
77 {
78 regexps.push_back(ICE_MAKE_SHARED(MetricsMapI::RegExp, p->first.substr(name.length() + 1), p->second));
79 }
80 catch(const std::exception&)
81 {
82 throw invalid_argument("invalid regular expression `" + p->second + "' for `" + p->first + "'");
83 }
84 }
85 return regexps;
86 }
87
88 }
89
RegExp(const string & attribute,const string & regexp)90 MetricsMapI::RegExp::RegExp(const string& attribute, const string& regexp) : _attribute(attribute)
91 {
92 #ifdef __MINGW32__
93 //
94 // No regexp support with MinGW, when MinGW C++11 mode is not experimental
95 // we can use std::regex.
96 //
97 #elif !defined(ICE_CPP11_COMPILER_REGEXP)
98 if(regcomp(&_preg, regexp.c_str(), REG_EXTENDED | REG_NOSUB) != 0)
99 {
100 throw SyscallException(__FILE__, __LINE__);
101 }
102 #else
103 # if _MSC_VER < 1600
104 _regex = std::tr1::regex(regexp, std::tr1::regex_constants::extended | std::tr1::regex_constants::nosubs);
105 # else
106 _regex = regex(regexp, std::regex_constants::extended | std::regex_constants::nosubs);
107 # endif
108 #endif
109 }
110
~RegExp()111 MetricsMapI::RegExp::~RegExp()
112 {
113 #ifdef __MINGW32__
114 //
115 // No regexp support with MinGW, when MinGW C++11 mode is not experimental
116 // we can use std::regex.
117 //
118 #elif !defined(ICE_CPP11_COMPILER_REGEXP)
119 regfree(&_preg);
120 #endif
121 }
122
123 bool
match(const string & value)124 MetricsMapI::RegExp::match(const string& value)
125 {
126 #ifdef __MINGW32__
127 //
128 // No regexp support with MinGW, when MinGW C++11 mode is not experimental
129 // we can use std::regex.
130 //
131 return false;
132 #elif !defined(ICE_CPP11_COMPILER_REGEXP)
133 return regexec(&_preg, value.c_str(), 0, 0, 0) == 0;
134 #else
135 # if _MSC_VER < 1600
136 return std::tr1::regex_match(value, _regex);
137 # else
138 return regex_match(value, _regex);
139 # endif
140 #endif
141 }
142
~MetricsMapI()143 MetricsMapI::~MetricsMapI()
144 {
145 // Out of line to avoid weak vtable
146 }
147
MetricsMapI(const std::string & mapPrefix,const PropertiesPtr & properties)148 MetricsMapI::MetricsMapI(const std::string& mapPrefix, const PropertiesPtr& properties) :
149 _properties(properties->getPropertiesForPrefix(mapPrefix)),
150 _retain(properties->getPropertyAsIntWithDefault(mapPrefix + "RetainDetached", 10)),
151 _accept(parseRule(properties, mapPrefix + "Accept")),
152 _reject(parseRule(properties, mapPrefix + "Reject"))
153 {
154 validateProperties(mapPrefix, properties);
155
156 string groupBy = properties->getPropertyWithDefault(mapPrefix + "GroupBy", "id");
157 vector<string>& groupByAttributes = const_cast<vector<string>&>(_groupByAttributes);
158 vector<string>& groupBySeparators = const_cast<vector<string>&>(_groupBySeparators);
159 if(!groupBy.empty())
160 {
161 string v;
162 bool attribute = IceUtilInternal::isAlpha(groupBy[0]) || IceUtilInternal::isDigit(groupBy[0]);
163 if(!attribute)
164 {
165 groupByAttributes.push_back("");
166 }
167
168 for(string::const_iterator p = groupBy.begin(); p != groupBy.end(); ++p)
169 {
170 bool isAlphaNum = IceUtilInternal::isAlpha(*p) || IceUtilInternal::isDigit(*p) || *p == '.';
171 if(attribute && !isAlphaNum)
172 {
173 groupByAttributes.push_back(v);
174 v = *p;
175 attribute = false;
176 }
177 else if(!attribute && isAlphaNum)
178 {
179 groupBySeparators.push_back(v);
180 v = *p;
181 attribute = true;
182 }
183 else
184 {
185 v += *p;
186 }
187 }
188
189 if(attribute)
190 {
191 groupByAttributes.push_back(v);
192 }
193 else
194 {
195 groupBySeparators.push_back(v);
196 }
197 }
198 }
199
MetricsMapI(const MetricsMapI & map)200 MetricsMapI::MetricsMapI(const MetricsMapI& map) :
201 #if defined(ICE_CPP11_MAPPING)
202 std::enable_shared_from_this<MetricsMapI>(),
203 #elif defined(__GNUC__)
204 IceUtil::Shared(),
205 #endif
206 _properties(map._properties),
207 _groupByAttributes(map._groupByAttributes),
208 _groupBySeparators(map._groupBySeparators),
209 _retain(map._retain),
210 _accept(map._accept),
211 _reject(map._reject)
212 {
213 }
214
215 const ::Ice::PropertyDict&
getProperties() const216 MetricsMapI::getProperties() const
217 {
218 return _properties;
219 }
220
~MetricsMapFactory()221 MetricsMapFactory::~MetricsMapFactory()
222 {
223 // Out of line to avoid weak vtable
224 }
225
MetricsMapFactory(Updater * updater)226 MetricsMapFactory::MetricsMapFactory(Updater* updater) : _updater(updater)
227 {
228 }
229
230 void
update()231 MetricsMapFactory::update()
232 {
233 assert(_updater);
234 _updater->update();
235 }
236
MetricsViewI(const string & name)237 MetricsViewI::MetricsViewI(const string& name) : _name(name)
238 {
239 }
240
241 void
destroy()242 MetricsViewI::destroy()
243 {
244 for(map<string, MetricsMapIPtr>::const_iterator p = _maps.begin(); p != _maps.end(); ++p)
245 {
246 p->second->destroy();
247 }
248 }
249
250 bool
addOrUpdateMap(const PropertiesPtr & properties,const string & mapName,const MetricsMapFactoryPtr & factory,const::Ice::LoggerPtr & logger)251 MetricsViewI::addOrUpdateMap(const PropertiesPtr& properties, const string& mapName,
252 const MetricsMapFactoryPtr& factory, const ::Ice::LoggerPtr& logger)
253 {
254 const string viewPrefix = "IceMX.Metrics." + _name + ".";
255 const string mapsPrefix = viewPrefix + "Map.";
256 PropertyDict mapsProps = properties->getPropertiesForPrefix(mapsPrefix);
257
258 string mapPrefix;
259 PropertyDict mapProps;
260 if(!mapsProps.empty())
261 {
262 mapPrefix = mapsPrefix + mapName + ".";
263 mapProps = properties->getPropertiesForPrefix(mapPrefix);
264 if(mapProps.empty())
265 {
266 // This map isn't configured for this view.
267 map<string, MetricsMapIPtr>::iterator q = _maps.find(mapName);
268 if(q != _maps.end())
269 {
270 q->second->destroy();
271 _maps.erase(q);
272 return true;
273 }
274 return false;
275 }
276 }
277 else
278 {
279 mapPrefix = viewPrefix;
280 mapProps = properties->getPropertiesForPrefix(mapPrefix);
281 }
282
283 if(properties->getPropertyAsInt(mapPrefix + "Disabled") > 0)
284 {
285 // This map is disabled for this view.
286 map<string, MetricsMapIPtr>::iterator q = _maps.find(mapName);
287 if(q != _maps.end())
288 {
289 q->second->destroy();
290 _maps.erase(q);
291 return true;
292 }
293 return false;
294 }
295
296 map<string, MetricsMapIPtr>::iterator q = _maps.find(mapName);
297 if(q != _maps.end() && q->second->getProperties() == mapProps)
298 {
299 return false; // The map configuration didn't change, no need to re-create.
300 }
301
302 if(q != _maps.end())
303 {
304 // Destroy the previous map
305 q->second->destroy();
306 _maps.erase(q);
307 }
308
309 try
310 {
311 _maps.insert(make_pair(mapName, factory->create(mapPrefix, properties)));
312 }
313 catch(const std::exception& ex)
314 {
315 ::Ice::Warning warn(logger);
316 warn << "unexpected exception while creating metrics map:\n" << ex;
317 }
318 return true;
319 }
320
321 bool
removeMap(const string & mapName)322 MetricsViewI::removeMap(const string& mapName)
323 {
324 map<string, MetricsMapIPtr>::iterator q = _maps.find(mapName);
325 if(q != _maps.end())
326 {
327 q->second->destroy();
328 _maps.erase(q);
329 return true;
330 }
331 return false;
332 }
333
334 MetricsView
getMetrics()335 MetricsViewI::getMetrics()
336 {
337 MetricsView metrics;
338 for(map<string, MetricsMapIPtr>::const_iterator p = _maps.begin(); p != _maps.end(); ++p)
339 {
340 metrics.insert(make_pair(p->first, p->second->getMetrics()));
341 }
342 return metrics;
343 }
344
345 MetricsFailuresSeq
getFailures(const string & mapName)346 MetricsViewI::getFailures(const string& mapName)
347 {
348 map<string, MetricsMapIPtr>::const_iterator p = _maps.find(mapName);
349 if(p != _maps.end())
350 {
351 return p->second->getFailures();
352 }
353 return MetricsFailuresSeq();
354 }
355
356 MetricsFailures
getFailures(const string & mapName,const string & id)357 MetricsViewI::getFailures(const string& mapName, const string& id)
358 {
359 map<string, MetricsMapIPtr>::const_iterator p = _maps.find(mapName);
360 if(p != _maps.end())
361 {
362 return p->second->getFailures(id);
363 }
364 return MetricsFailures();
365 }
366
367 vector<string>
getMaps() const368 MetricsViewI::getMaps() const
369 {
370 vector<string> maps;
371 for(map<string, MetricsMapIPtr>::const_iterator p = _maps.begin(); p != _maps.end(); ++p)
372 {
373 maps.push_back(p->first);
374 }
375 return maps;
376 }
377
378 MetricsMapIPtr
getMap(const string & mapName) const379 MetricsViewI::getMap(const string& mapName) const
380 {
381 map<string, MetricsMapIPtr>::const_iterator p = _maps.find(mapName);
382 if(p != _maps.end())
383 {
384 return p->second;
385 }
386 return ICE_NULLPTR;
387 }
388
MetricsAdminI(const PropertiesPtr & properties,const LoggerPtr & logger)389 MetricsAdminI::MetricsAdminI(const PropertiesPtr& properties, const LoggerPtr& logger) :
390 _logger(logger), _properties(properties)
391 {
392 updateViews();
393 }
394
~MetricsAdminI()395 MetricsAdminI::~MetricsAdminI()
396 {
397 }
398
399 void
destroy()400 MetricsAdminI::destroy()
401 {
402 Lock sync(*this);
403 for(map<string, MetricsViewIPtr>::const_iterator p = _views.begin(); p != _views.end(); ++p)
404 {
405 p->second->destroy();
406 }
407 }
408
409 void
updateViews()410 MetricsAdminI::updateViews()
411 {
412 set<MetricsMapFactoryPtr> updatedMaps;
413 {
414 Lock sync(*this);
415 const string viewsPrefix = "IceMX.Metrics.";
416 PropertyDict viewsProps = _properties->getPropertiesForPrefix(viewsPrefix);
417 map<string, MetricsViewIPtr> views;
418 _disabledViews.clear();
419 for(PropertyDict::const_iterator p = viewsProps.begin(); p != viewsProps.end(); ++p)
420 {
421 string viewName = p->first.substr(viewsPrefix.size());
422 string::size_type dotPos = viewName.find('.');
423 if(dotPos != string::npos)
424 {
425 viewName = viewName.substr(0, dotPos);
426 }
427
428 if(views.find(viewName) != views.end() || _disabledViews.find(viewName) != _disabledViews.end())
429 {
430 continue; // View already configured.
431 }
432
433 validateProperties(viewsPrefix + viewName + ".", _properties);
434
435 if(_properties->getPropertyAsIntWithDefault(viewsPrefix + viewName + ".Disabled", 0) > 0)
436 {
437 _disabledViews.insert(viewName);
438 continue; // The view is disabled
439 }
440
441 //
442 // Create the view or update it.
443 //
444 map<string, MetricsViewIPtr>::const_iterator q = _views.find(viewName);
445 if(q == _views.end())
446 {
447 q = views.insert(map<string, MetricsViewIPtr>::value_type(viewName, ICE_MAKE_SHARED(MetricsViewI, viewName))).first;
448 }
449 else
450 {
451 q = views.insert(make_pair(viewName, q->second)).first;
452 }
453
454 for(map<string, MetricsMapFactoryPtr>::const_iterator r = _factories.begin(); r != _factories.end(); ++r)
455 {
456 if(q->second->addOrUpdateMap(_properties, r->first, r->second, _logger))
457 {
458 updatedMaps.insert(r->second);
459 }
460 }
461 }
462 _views.swap(views);
463
464 //
465 // Go through removed views to collect maps to update.
466 //
467 for(map<string, MetricsViewIPtr>::const_iterator p = views.begin(); p != views.end(); ++p)
468 {
469 if(_views.find(p->first) == _views.end())
470 {
471 vector<string> maps = p->second->getMaps();
472 for(vector<string>::const_iterator q = maps.begin(); q != maps.end(); ++q)
473 {
474 updatedMaps.insert(_factories[*q]);
475 }
476 p->second->destroy();
477 }
478 }
479 }
480
481 //
482 // Call the updaters to update the maps.
483 //
484 for(set<MetricsMapFactoryPtr>::const_iterator p = updatedMaps.begin(); p != updatedMaps.end(); ++p)
485 {
486 (*p)->update();
487 }
488 }
489
490 void
unregisterMap(const std::string & mapName)491 MetricsAdminI::unregisterMap(const std::string& mapName)
492 {
493 bool updated;
494 MetricsMapFactoryPtr factory;
495 {
496 Lock sync(*this);
497 map<string, MetricsMapFactoryPtr>::iterator p = _factories.find(mapName);
498 if(p == _factories.end())
499 {
500 return;
501 }
502 factory = p->second;
503 _factories.erase(p);
504 updated = removeMap(mapName);
505 }
506 if(updated)
507 {
508 factory->update();
509 }
510 }
511
512 Ice::StringSeq
getMetricsViewNames(Ice::StringSeq & disabledViews,const Current &)513 MetricsAdminI::getMetricsViewNames(Ice::StringSeq& disabledViews, const Current&)
514 {
515 Ice::StringSeq enabledViews;
516
517 Lock sync(*this);
518 for(map<string, MetricsViewIPtr>::const_iterator p = _views.begin(); p != _views.end(); ++p)
519 {
520 enabledViews.push_back(p->first);
521 }
522
523 #if defined(__SUNPRO_CC) && defined(_RWSTD_NO_MEMBER_TEMPLATES)
524 for(set<string>::const_iterator p = _disabledViews.begin(); p != _disabledViews.end(); ++p)
525 {
526 disabledViews.push_back(*p);
527 }
528
529 #else
530 disabledViews.insert(disabledViews.end(), _disabledViews.begin(), _disabledViews.end());
531 #endif
532
533 return enabledViews;
534 }
535
536 void
537 #ifdef ICE_CPP11_MAPPING
enableMetricsView(string viewName,const Current &)538 MetricsAdminI::enableMetricsView(string viewName, const Current&)
539 #else
540 MetricsAdminI::enableMetricsView(const string& viewName, const Current&)
541 #endif
542 {
543 {
544 Lock sync(*this);
545 getMetricsView(viewName); // Throws if unkonwn metrics view.
546 _properties->setProperty("IceMX.Metrics." + viewName + ".Disabled", "0");
547 }
548 updateViews();
549 }
550
551 void
552 #ifdef ICE_CPP11_MAPPING
disableMetricsView(string viewName,const Current &)553 MetricsAdminI::disableMetricsView(string viewName, const Current&)
554 #else
555 MetricsAdminI::disableMetricsView(const string& viewName, const Current&)
556 #endif
557 {
558 {
559 Lock sync(*this);
560 getMetricsView(viewName); // Throws if unkonwn metrics view.
561 _properties->setProperty("IceMX.Metrics." + viewName + ".Disabled", "1");
562 }
563 updateViews();
564 }
565
566 MetricsView
567 #ifdef ICE_CPP11_MAPPING
getMetricsView(string viewName,::Ice::Long & timestamp,const Current &)568 MetricsAdminI::getMetricsView(string viewName, ::Ice::Long& timestamp, const Current&)
569 #else
570 MetricsAdminI::getMetricsView(const string& viewName, ::Ice::Long& timestamp, const Current&)
571 #endif
572 {
573 Lock sync(*this);
574 MetricsViewIPtr view = getMetricsView(viewName);
575 timestamp = IceUtil::Time::now().toMilliSeconds();
576 if(view)
577 {
578 return view->getMetrics();
579 }
580 return MetricsView();
581 }
582
583 MetricsFailuresSeq
584 #ifdef ICE_CPP11_MAPPING
getMapMetricsFailures(string viewName,string map,const Current &)585 MetricsAdminI::getMapMetricsFailures(string viewName, string map, const Current&)
586 #else
587 MetricsAdminI::getMapMetricsFailures(const string& viewName, const string& map, const Current&)
588 #endif
589 {
590 Lock sync(*this);
591 MetricsViewIPtr view = getMetricsView(viewName);
592 if(view)
593 {
594 return view->getFailures(map);
595 }
596 return MetricsFailuresSeq();
597 }
598
599 MetricsFailures
600 #ifdef ICE_CPP11_MAPPING
getMetricsFailures(string viewName,string map,string id,const Current &)601 MetricsAdminI::getMetricsFailures(string viewName, string map, string id, const Current&)
602 #else
603 MetricsAdminI::getMetricsFailures(const string& viewName, const string& map, const string& id, const Current&)
604 #endif
605 {
606 Lock sync(*this);
607 MetricsViewIPtr view = getMetricsView(viewName);
608 if(view)
609 {
610 return view->getFailures(map, id);
611 }
612 return MetricsFailures();
613 }
614
615 vector<MetricsMapIPtr>
getMaps(const string & mapName) const616 MetricsAdminI::getMaps(const string& mapName) const
617 {
618 Lock sync(*this);
619 vector<MetricsMapIPtr> maps;
620 for(std::map<string, MetricsViewIPtr>::const_iterator p = _views.begin(); p != _views.end(); ++p)
621 {
622 MetricsMapIPtr map = p->second->getMap(mapName);
623 if(map)
624 {
625 maps.push_back(map);
626 }
627 }
628 return maps;
629 }
630
631 const LoggerPtr&
getLogger() const632 MetricsAdminI::getLogger() const
633 {
634 return _logger;
635 }
636
637 MetricsViewIPtr
getMetricsView(const std::string & name)638 MetricsAdminI::getMetricsView(const std::string& name)
639 {
640 std::map<string, MetricsViewIPtr>::const_iterator p = _views.find(name);
641 if(p == _views.end())
642 {
643 if(_disabledViews.find(name) == _disabledViews.end())
644 {
645 throw UnknownMetricsView();
646 }
647 return ICE_NULLPTR;
648 }
649 return p->second;
650 }
651
652 void
updated(const PropertyDict & props)653 MetricsAdminI::updated(const PropertyDict& props)
654 {
655 for(PropertyDict::const_iterator p = props.begin(); p != props.end(); ++p)
656 {
657 if(p->first.find("IceMX.") == 0)
658 {
659 // Udpate the metrics views using the new configuration.
660 try
661 {
662 updateViews();
663 }
664 catch(const std::exception& ex)
665 {
666 ::Ice::Warning warn(_logger);
667 warn << "unexpected exception while updating metrics view configuration:\n" << ex.what();
668 }
669 return;
670 }
671 }
672 }
673
674 bool
addOrUpdateMap(const std::string & mapName,const MetricsMapFactoryPtr & factory)675 MetricsAdminI::addOrUpdateMap(const std::string& mapName, const MetricsMapFactoryPtr& factory)
676 {
677 bool updated = false;
678 for(std::map<string, MetricsViewIPtr>::const_iterator p = _views.begin(); p != _views.end(); ++p)
679 {
680 updated |= p->second->addOrUpdateMap(_properties, mapName, factory, _logger);
681 }
682 return updated;
683 }
684
685 bool
removeMap(const std::string & mapName)686 MetricsAdminI::removeMap(const std::string& mapName)
687 {
688 bool updated = false;
689 for(std::map<string, MetricsViewIPtr>::const_iterator p = _views.begin(); p != _views.end(); ++p)
690 {
691 updated |= p->second->removeMap(mapName);
692 }
693 return updated;
694 }
695