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