2013-10-14 20:12:42 +02:00
/******************************************************************************
* Icinga 2 *
2015-01-22 12:00:23 +01:00
* Copyright ( C ) 2012 - 2015 Icinga Development Team ( http : //www.icinga.org) *
2013-10-14 20:12:42 +02:00
* *
* This program is free software ; you can redistribute it and / or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation ; either version 2 *
* of the License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the Free Software Foundation *
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-05-25 16:23:35 +02:00
# include "perfdata/graphitewriter.hpp"
2015-03-28 11:04:42 +01:00
# include "perfdata/graphitewriter.tcpp"
2014-05-25 16:23:35 +02:00
# include "icinga/service.hpp"
# include "icinga/macroprocessor.hpp"
# include "icinga/icingaapplication.hpp"
# include "icinga/compatutility.hpp"
# include "icinga/perfdatavalue.hpp"
# include "base/tcpsocket.hpp"
2015-08-15 20:28:05 +02:00
# include "base/configtype.hpp"
2014-05-25 16:23:35 +02:00
# include "base/objectlock.hpp"
2014-10-19 14:21:12 +02:00
# include "base/logger.hpp"
2014-05-25 16:23:35 +02:00
# include "base/convert.hpp"
# include "base/utility.hpp"
# include "base/application.hpp"
# include "base/stream.hpp"
# include "base/networkstream.hpp"
# include "base/exception.hpp"
# include "base/statsfunction.hpp"
2013-10-18 16:05:56 +02:00
# include <boost/algorithm/string.hpp>
2013-10-14 20:12:42 +02:00
# include <boost/algorithm/string/classification.hpp>
# include <boost/foreach.hpp>
# include <boost/algorithm/string/split.hpp>
2013-10-15 20:37:58 +02:00
# include <boost/algorithm/string/replace.hpp>
2013-10-14 20:12:42 +02:00
using namespace icinga ;
REGISTER_TYPE ( GraphiteWriter ) ;
2015-09-21 11:44:58 +02:00
REGISTER_STATSFUNCTION ( GraphiteWriter , & GraphiteWriter : : StatsFunc ) ;
2014-02-17 16:34:18 +01:00
2015-02-07 22:36:17 +01:00
void GraphiteWriter : : StatsFunc ( const Dictionary : : Ptr & status , const Array : : Ptr & )
2014-02-17 16:34:18 +01:00
{
2014-11-08 21:17:16 +01:00
Dictionary : : Ptr nodes = new Dictionary ( ) ;
2014-02-18 10:53:44 +01:00
2015-08-15 20:28:05 +02:00
BOOST_FOREACH ( const GraphiteWriter : : Ptr & graphitewriter , ConfigType : : GetObjectsByType < GraphiteWriter > ( ) ) {
2014-02-18 10:53:44 +01:00
nodes - > Set ( graphitewriter - > GetName ( ) , 1 ) ; //add more stats
}
status - > Set ( " graphitewriter " , nodes ) ;
2014-02-17 16:34:18 +01:00
}
2013-10-14 20:12:42 +02:00
void GraphiteWriter : : Start ( void )
{
2015-08-25 13:53:43 +02:00
ObjectImpl < GraphiteWriter > : : Start ( ) ;
2013-10-14 20:12:42 +02:00
2014-11-08 21:17:16 +01:00
m_ReconnectTimer = new Timer ( ) ;
2013-10-14 20:12:42 +02:00
m_ReconnectTimer - > SetInterval ( 10 ) ;
m_ReconnectTimer - > OnTimerExpired . connect ( boost : : bind ( & GraphiteWriter : : ReconnectTimerHandler , this ) ) ;
m_ReconnectTimer - > Start ( ) ;
m_ReconnectTimer - > Reschedule ( 0 ) ;
Service : : OnNewCheckResult . connect ( boost : : bind ( & GraphiteWriter : : CheckResultHandler , this , _1 , _2 ) ) ;
}
void GraphiteWriter : : ReconnectTimerHandler ( void )
{
2014-08-06 09:59:38 +02:00
if ( m_Stream )
return ;
2013-10-14 20:12:42 +02:00
2014-11-08 21:17:16 +01:00
TcpSocket : : Ptr socket = new TcpSocket ( ) ;
2013-10-14 20:12:42 +02:00
2014-10-20 10:09:57 +02:00
Log ( LogNotice , " GraphiteWriter " )
< < " Reconnecting to Graphite on host ' " < < GetHost ( ) < < " ' port ' " < < GetPort ( ) < < " '. " ;
2014-06-05 16:17:53 +02:00
try {
socket - > Connect ( GetHost ( ) , GetPort ( ) ) ;
} catch ( std : : exception & ) {
2014-10-20 10:09:57 +02:00
Log ( LogCritical , " GraphiteWriter " )
< < " Can't connect to Graphite on host ' " < < GetHost ( ) < < " ' port ' " < < GetPort ( ) < < " '. " ;
2014-06-05 16:17:53 +02:00
return ;
}
2013-10-14 20:12:42 +02:00
2014-11-08 21:17:16 +01:00
m_Stream = new NetworkStream ( socket ) ;
2013-10-14 20:12:42 +02:00
}
2014-04-03 15:36:13 +02:00
void GraphiteWriter : : CheckResultHandler ( const Checkable : : Ptr & checkable , const CheckResult : : Ptr & cr )
2013-10-14 20:12:42 +02:00
{
2014-08-14 17:08:11 +02:00
CONTEXT ( " Processing check result for ' " + checkable - > GetName ( ) + " ' " ) ;
2014-04-03 15:36:13 +02:00
if ( ! IcingaApplication : : GetInstance ( ) - > GetEnablePerfdata ( ) | | ! checkable - > GetEnablePerfdata ( ) )
2013-10-14 20:12:42 +02:00
return ;
2014-04-03 15:36:13 +02:00
Service : : Ptr service = dynamic_pointer_cast < Service > ( checkable ) ;
Host : : Ptr host ;
2014-04-01 20:30:44 +02:00
2014-04-03 15:36:13 +02:00
if ( service )
host = service - > GetHost ( ) ;
else
host = static_pointer_cast < Host > ( checkable ) ;
2013-11-08 15:42:46 +01:00
2014-10-11 20:38:24 +02:00
MacroProcessor : : ResolverList resolvers ;
if ( service )
resolvers . push_back ( std : : make_pair ( " service " , service ) ) ;
resolvers . push_back ( std : : make_pair ( " host " , host ) ) ;
resolvers . push_back ( std : : make_pair ( " icinga " , IcingaApplication : : GetInstance ( ) ) ) ;
2014-10-12 13:30:39 +02:00
String prefix ;
2014-10-11 20:38:24 +02:00
2015-02-06 08:51:33 +01:00
double ts = cr - > GetExecutionEnd ( ) ;
2015-06-19 18:11:55 +02:00
/* new mode below. old mode in else tree with 2.4, deprecate it in 2.6 */
if ( ! GetEnableLegacyMode ( ) ) {
if ( service ) {
prefix = MacroProcessor : : ResolveMacros ( GetServiceNameTemplate ( ) , resolvers , cr , NULL , boost : : bind ( & GraphiteWriter : : EscapeMacroMetric , _1 , false ) ) ;
} else {
prefix = MacroProcessor : : ResolveMacros ( GetHostNameTemplate ( ) , resolvers , cr , NULL , boost : : bind ( & GraphiteWriter : : EscapeMacroMetric , _1 , false ) ) ;
}
String prefix_perfdata = prefix + " .perfdata " ;
String prefix_metadata = prefix + " .metadata " ;
if ( GetEnableSendMetadata ( ) ) {
if ( service ) {
SendMetric ( prefix_metadata , " state " , service - > GetState ( ) , ts ) ;
} else {
SendMetric ( prefix_metadata , " state " , host - > GetState ( ) , ts ) ;
}
SendMetric ( prefix_metadata , " current_attempt " , checkable - > GetCheckAttempt ( ) , ts ) ;
SendMetric ( prefix_metadata , " max_check_attempts " , checkable - > GetMaxCheckAttempts ( ) , ts ) ;
SendMetric ( prefix_metadata , " state_type " , checkable - > GetStateType ( ) , ts ) ;
SendMetric ( prefix_metadata , " reachable " , checkable - > IsReachable ( ) , ts ) ;
SendMetric ( prefix_metadata , " downtime_depth " , checkable - > GetDowntimeDepth ( ) , ts ) ;
SendMetric ( prefix_metadata , " latency " , Service : : CalculateLatency ( cr ) , ts ) ;
SendMetric ( prefix_metadata , " execution_time " , Service : : CalculateExecutionTime ( cr ) , ts ) ;
}
2014-04-01 20:30:44 +02:00
2015-06-19 18:11:55 +02:00
SendPerfdata ( prefix_perfdata , cr , ts ) ;
2014-04-03 15:36:13 +02:00
} else {
2015-06-19 18:11:55 +02:00
if ( service ) {
prefix = MacroProcessor : : ResolveMacros ( GetServiceNameTemplate ( ) , resolvers , cr , NULL , boost : : bind ( & GraphiteWriter : : EscapeMacroMetric , _1 , true ) ) ;
SendMetric ( prefix , " state " , service - > GetState ( ) , ts ) ;
} else {
prefix = MacroProcessor : : ResolveMacros ( GetHostNameTemplate ( ) , resolvers , cr , NULL , boost : : bind ( & GraphiteWriter : : EscapeMacroMetric , _1 , true ) ) ;
SendMetric ( prefix , " state " , host - > GetState ( ) , ts ) ;
}
2014-04-01 20:30:44 +02:00
2015-06-19 18:11:55 +02:00
SendMetric ( prefix , " current_attempt " , checkable - > GetCheckAttempt ( ) , ts ) ;
SendMetric ( prefix , " max_check_attempts " , checkable - > GetMaxCheckAttempts ( ) , ts ) ;
SendMetric ( prefix , " state_type " , checkable - > GetStateType ( ) , ts ) ;
SendMetric ( prefix , " reachable " , checkable - > IsReachable ( ) , ts ) ;
SendMetric ( prefix , " downtime_depth " , checkable - > GetDowntimeDepth ( ) , ts ) ;
SendMetric ( prefix , " latency " , Service : : CalculateLatency ( cr ) , ts ) ;
SendMetric ( prefix , " execution_time " , Service : : CalculateExecutionTime ( cr ) , ts ) ;
SendPerfdata ( prefix , cr , ts ) ;
2014-04-01 20:30:44 +02:00
}
}
2015-02-06 08:51:33 +01:00
void GraphiteWriter : : SendPerfdata ( const String & prefix , const CheckResult : : Ptr & cr , double ts )
2014-04-01 20:30:44 +02:00
{
2014-09-17 15:38:39 +02:00
Array : : Ptr perfdata = cr - > GetPerformanceData ( ) ;
2013-10-14 20:12:42 +02:00
2014-09-17 16:35:42 +02:00
if ( ! perfdata )
return ;
2013-11-30 17:42:50 +01:00
ObjectLock olock ( perfdata ) ;
2014-09-17 15:38:39 +02:00
BOOST_FOREACH ( const Value & val , perfdata ) {
PerfdataValue : : Ptr pdv ;
2015-02-11 13:12:08 +01:00
2014-09-17 15:38:39 +02:00
if ( val . IsObjectType < PerfdataValue > ( ) )
pdv = val ;
else {
try {
pdv = PerfdataValue : : Parse ( val ) ;
} catch ( const std : : exception & ) {
2014-10-20 10:09:57 +02:00
Log ( LogWarning , " GraphiteWriter " )
< < " Ignoring invalid perfdata value: " < < val ;
2014-09-17 15:38:39 +02:00
continue ;
}
}
2015-02-11 13:12:08 +01:00
2015-06-19 18:11:55 +02:00
/* new mode below. old mode in else tree with 2.4, deprecate it in 2.6 */
if ( ! GetEnableLegacyMode ( ) ) {
String escaped_key = EscapeMetricLabel ( pdv - > GetLabel ( ) ) ;
SendMetric ( prefix , escaped_key + " .value " , pdv - > GetValue ( ) , ts ) ;
if ( GetEnableSendThresholds ( ) ) {
if ( pdv - > GetCrit ( ) )
SendMetric ( prefix , escaped_key + " .crit " , pdv - > GetCrit ( ) , ts ) ;
if ( pdv - > GetWarn ( ) )
SendMetric ( prefix , escaped_key + " .warn " , pdv - > GetWarn ( ) , ts ) ;
if ( pdv - > GetMin ( ) )
SendMetric ( prefix , escaped_key + " .min " , pdv - > GetMin ( ) , ts ) ;
if ( pdv - > GetMax ( ) )
SendMetric ( prefix , escaped_key + " .max " , pdv - > GetMax ( ) , ts ) ;
}
} else {
String escaped_key = EscapeMetric ( pdv - > GetLabel ( ) ) ;
boost : : algorithm : : replace_all ( escaped_key , " :: " , " . " ) ;
SendMetric ( prefix , escaped_key , pdv - > GetValue ( ) , ts ) ;
if ( pdv - > GetCrit ( ) )
SendMetric ( prefix , escaped_key + " _crit " , pdv - > GetCrit ( ) , ts ) ;
if ( pdv - > GetWarn ( ) )
SendMetric ( prefix , escaped_key + " _warn " , pdv - > GetWarn ( ) , ts ) ;
if ( pdv - > GetMin ( ) )
SendMetric ( prefix , escaped_key + " _min " , pdv - > GetMin ( ) , ts ) ;
if ( pdv - > GetMax ( ) )
SendMetric ( prefix , escaped_key + " _max " , pdv - > GetMax ( ) , ts ) ;
}
2013-11-07 14:38:37 +01:00
}
2013-10-14 20:12:42 +02:00
}
2015-02-06 08:51:33 +01:00
void GraphiteWriter : : SendMetric ( const String & prefix , const String & name , double value , double ts )
2013-10-14 20:12:42 +02:00
{
2013-11-08 15:42:46 +01:00
std : : ostringstream msgbuf ;
2015-02-06 08:51:33 +01:00
msgbuf < < prefix < < " . " < < name < < " " < < Convert : : ToString ( value ) < < " " < < static_cast < long > ( ts ) ;
2013-10-14 20:12:42 +02:00
2014-10-19 17:52:17 +02:00
Log ( LogDebug , " GraphiteWriter " )
< < " Add to metric list:' " < < msgbuf . str ( ) < < " '. " ;
2014-08-05 00:53:18 +02:00
// do not send \n to debug log
msgbuf < < " \n " ;
2013-11-08 15:42:46 +01:00
String metric = msgbuf . str ( ) ;
2013-10-14 20:12:42 +02:00
2013-11-07 14:38:37 +01:00
ObjectLock olock ( this ) ;
2013-10-14 20:12:42 +02:00
2013-11-07 14:38:37 +01:00
if ( ! m_Stream )
return ;
2013-10-18 09:11:21 +02:00
2013-11-07 14:38:37 +01:00
try {
m_Stream - > Write ( metric . CStr ( ) , metric . GetLength ( ) ) ;
} catch ( const std : : exception & ex ) {
2014-10-19 17:52:17 +02:00
Log ( LogCritical , " GraphiteWriter " )
< < " Cannot write to TCP socket on host ' " < < GetHost ( ) < < " ' port ' " < < GetPort ( ) < < " '. " ;
2013-10-18 09:11:21 +02:00
2013-11-07 14:38:37 +01:00
m_Stream . reset ( ) ;
2013-10-14 20:12:42 +02:00
}
}
2015-06-19 18:11:55 +02:00
String GraphiteWriter : : EscapeMetric ( const String & str , bool legacyMode )
2013-10-18 16:05:56 +02:00
{
2014-10-12 13:30:39 +02:00
String result = str ;
2015-06-19 18:11:55 +02:00
//don't allow '.' in metric prefixes
2014-10-12 13:30:39 +02:00
boost : : replace_all ( result , " " , " _ " ) ;
boost : : replace_all ( result , " . " , " _ " ) ;
boost : : replace_all ( result , " \\ " , " _ " ) ;
boost : : replace_all ( result , " / " , " _ " ) ;
2015-06-19 18:11:55 +02:00
if ( legacyMode )
boost : : replace_all ( result , " - " , " _ " ) ;
return result ;
}
String GraphiteWriter : : EscapeMetricLabel ( const String & str )
{
String result = str ;
//allow to pass '.' in perfdata labels
boost : : replace_all ( result , " " , " _ " ) ;
boost : : replace_all ( result , " \\ " , " _ " ) ;
boost : : replace_all ( result , " / " , " _ " ) ;
boost : : replace_all ( result , " :: " , " . " ) ;
2014-10-12 13:30:39 +02:00
return result ;
2013-10-18 16:05:56 +02:00
}
2014-11-26 20:43:42 +01:00
2015-06-19 18:11:55 +02:00
Value GraphiteWriter : : EscapeMacroMetric ( const Value & value , bool legacyMode )
2014-11-26 20:43:42 +01:00
{
if ( value . IsObjectType < Array > ( ) ) {
Array : : Ptr arr = value ;
Array : : Ptr result = new Array ( ) ;
ObjectLock olock ( arr ) ;
BOOST_FOREACH ( const Value & arg , arr ) {
2015-06-19 18:11:55 +02:00
result - > Add ( EscapeMetric ( arg , legacyMode ) ) ;
2014-11-26 20:43:42 +01:00
}
return Utility : : Join ( result , ' . ' ) ;
} else
2015-06-19 18:11:55 +02:00
return EscapeMetric ( value , legacyMode ) ;
2014-11-26 20:43:42 +01:00
}
2015-02-11 13:12:08 +01:00
2014-11-30 23:32:13 +01:00
void GraphiteWriter : : ValidateHostNameTemplate ( const String & value , const ValidationUtils & utils )
2015-02-11 13:12:08 +01:00
{
2014-11-30 23:32:13 +01:00
ObjectImpl < GraphiteWriter > : : ValidateHostNameTemplate ( value , utils ) ;
2015-02-11 15:47:45 +01:00
2014-11-30 23:32:13 +01:00
if ( ! MacroProcessor : : ValidateMacroString ( value ) )
BOOST_THROW_EXCEPTION ( ValidationError ( this , boost : : assign : : list_of ( " host_name_template " ) , " Closing $ not found in macro format string ' " + value + " '. " ) ) ;
}
void GraphiteWriter : : ValidateServiceNameTemplate ( const String & value , const ValidationUtils & utils )
{
ObjectImpl < GraphiteWriter > : : ValidateServiceNameTemplate ( value , utils ) ;
if ( ! MacroProcessor : : ValidateMacroString ( value ) )
BOOST_THROW_EXCEPTION ( ValidationError ( this , boost : : assign : : list_of ( " service_name_template " ) , " Closing $ not found in macro format string ' " + value + " '. " ) ) ;
2015-02-11 13:12:08 +01:00
}