2013-03-19 13:04:30 +01:00
/******************************************************************************
* Icinga 2 *
* Copyright ( C ) 2012 Icinga Development Team ( http : //www.icinga.org/) *
* *
* 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 . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "compat/compatlog.h"
# include "icinga/service.h"
2013-07-01 10:03:00 +02:00
# include "icinga/checkcommand.h"
2013-06-28 21:31:38 +02:00
# include "icinga/notification.h"
2013-03-19 13:04:30 +01:00
# include "icinga/macroprocessor.h"
2013-03-20 11:11:46 +01:00
# include "config/configcompilercontext.h"
2013-03-19 13:04:30 +01:00
# include "base/dynamictype.h"
# include "base/objectlock.h"
# include "base/logger_fwd.h"
2013-03-20 11:11:46 +01:00
# include "base/exception.h"
2013-03-19 13:04:30 +01:00
# include "base/convert.h"
# include "base/application.h"
2013-03-25 18:36:15 +01:00
# include "base/utility.h"
# include "base/scriptfunction.h"
2013-03-19 13:04:30 +01:00
# include <boost/smart_ptr/make_shared.hpp>
# include <boost/foreach.hpp>
2013-07-02 13:38:06 +02:00
# include <boost/algorithm/string.hpp>
2013-03-19 13:04:30 +01:00
using namespace icinga ;
REGISTER_TYPE ( CompatLog ) ;
2013-03-20 11:11:46 +01:00
REGISTER_SCRIPTFUNCTION ( ValidateRotationMethod , & CompatLog : : ValidateRotationMethod ) ;
2013-03-19 13:04:30 +01:00
2013-08-20 11:06:04 +02:00
CompatLog : : CompatLog ( void )
: m_LastRotation ( 0 )
{ }
2013-03-19 13:04:30 +01:00
/**
* @ threadsafety Always .
*/
void CompatLog : : Start ( void )
{
2013-09-09 10:06:49 +02:00
DynamicObject : : Start ( ) ;
2013-08-20 11:06:04 +02:00
Service : : OnNewCheckResult . connect ( bind ( & CompatLog : : CheckResultHandler , this , _1 , _2 ) ) ;
2013-07-01 17:56:21 +02:00
Service : : OnNotificationSentChanged . connect ( bind ( & CompatLog : : NotificationSentHandler , this , _1 , _2 , _3 , _4 , _5 , _6 ) ) ;
2013-07-01 14:29:07 +02:00
Service : : OnFlappingChanged . connect ( bind ( & CompatLog : : FlappingHandler , this , _1 , _2 ) ) ;
2013-09-17 19:40:23 +02:00
Service : : OnDowntimeTriggered . connect ( boost : : bind ( & CompatLog : : TriggerDowntimeHandler , this , _1 , _2 ) ) ;
Service : : OnDowntimeRemoved . connect ( boost : : bind ( & CompatLog : : RemoveDowntimeHandler , this , _1 , _2 ) ) ;
2013-06-28 16:08:43 +02:00
2013-03-19 13:04:30 +01:00
m_RotationTimer = boost : : make_shared < Timer > ( ) ;
m_RotationTimer - > OnTimerExpired . connect ( boost : : bind ( & CompatLog : : RotationTimerHandler , this ) ) ;
m_RotationTimer - > Start ( ) ;
2013-03-20 11:11:46 +01:00
ReopenFile ( false ) ;
ScheduleNextRotation ( ) ;
2013-03-19 13:04:30 +01:00
}
/**
* @ threadsafety Always .
*/
String CompatLog : : GetLogDir ( void ) const
{
if ( ! m_LogDir . IsEmpty ( ) )
return m_LogDir ;
else
2013-03-20 11:11:46 +01:00
return Application : : GetLocalStateDir ( ) + " /log/icinga2/compat " ;
2013-03-19 13:04:30 +01:00
}
/**
* @ threadsafety Always .
*/
2013-03-20 11:11:46 +01:00
String CompatLog : : GetRotationMethod ( void ) const
2013-03-19 13:04:30 +01:00
{
2013-03-20 11:11:46 +01:00
if ( ! m_RotationMethod . IsEmpty ( ) )
return m_RotationMethod ;
2013-03-19 13:04:30 +01:00
else
2013-03-20 11:11:46 +01:00
return " HOURLY " ;
2013-03-19 13:04:30 +01:00
}
/**
* @ threadsafety Always .
*/
2013-08-20 11:06:04 +02:00
void CompatLog : : CheckResultHandler ( const Service : : Ptr & service , const Dictionary : : Ptr & cr )
2013-03-19 13:04:30 +01:00
{
Host : : Ptr host = service - > GetHost ( ) ;
if ( ! host )
return ;
Dictionary : : Ptr vars_after = cr - > Get ( " vars_after " ) ;
long state_after = vars_after - > Get ( " state " ) ;
long stateType_after = vars_after - > Get ( " state_type " ) ;
long attempt_after = vars_after - > Get ( " attempt " ) ;
bool reachable_after = vars_after - > Get ( " reachable " ) ;
bool host_reachable_after = vars_after - > Get ( " host_reachable " ) ;
Dictionary : : Ptr vars_before = cr - > Get ( " vars_before " ) ;
if ( vars_before ) {
long state_before = vars_before - > Get ( " state " ) ;
long stateType_before = vars_before - > Get ( " state_type " ) ;
long attempt_before = vars_before - > Get ( " attempt " ) ;
bool reachable_before = vars_before - > Get ( " reachable " ) ;
if ( state_before = = state_after & & stateType_before = = stateType_after & &
attempt_before = = attempt_after & & reachable_before = = reachable_after )
return ; /* Nothing changed, ignore this checkresult. */
}
2013-06-26 18:23:59 +02:00
String raw_output ;
String output ;
if ( cr ) {
raw_output = cr - > Get ( " output " ) ;
size_t line_end = raw_output . Find ( " \n " ) ;
output = raw_output . SubStr ( 0 , line_end ) ;
boost : : algorithm : : replace_all ( output , " \n " , " \\ n " ) ;
}
2013-03-19 13:04:30 +01:00
std : : ostringstream msgbuf ;
msgbuf < < " SERVICE ALERT: "
< < host - > GetName ( ) < < " ; "
< < service - > GetShortName ( ) < < " ; "
< < Service : : StateToString ( static_cast < ServiceState > ( state_after ) ) < < " ; "
< < Service : : StateTypeToString ( static_cast < StateType > ( stateType_after ) ) < < " ; "
< < attempt_after < < " ; "
2013-06-26 18:23:59 +02:00
< < output < < " "
2013-03-19 13:04:30 +01:00
< < " " ;
2013-06-24 08:56:13 +02:00
{
ObjectLock olock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
2013-03-19 13:04:30 +01:00
if ( service = = host - > GetHostCheckService ( ) ) {
std : : ostringstream msgbuf ;
msgbuf < < " HOST ALERT: "
< < host - > GetName ( ) < < " ; "
< < Host : : StateToString ( Host : : CalculateState ( static_cast < ServiceState > ( state_after ) , host_reachable_after ) ) < < " ; "
< < Service : : StateTypeToString ( static_cast < StateType > ( stateType_after ) ) < < " ; "
< < attempt_after < < " ; "
2013-06-26 18:23:59 +02:00
< < output < < " "
2013-03-19 13:04:30 +01:00
< < " " ;
2013-06-24 08:56:13 +02:00
{
ObjectLock olock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
2013-03-19 13:04:30 +01:00
}
2013-06-24 08:56:13 +02:00
{
ObjectLock olock ( this ) ;
Flush ( ) ;
}
2013-03-19 13:04:30 +01:00
}
2013-09-17 19:40:23 +02:00
/**
* @ threadsafety Always .
*/
void CompatLog : : TriggerDowntimeHandler ( const Service : : Ptr & service , const Dictionary : : Ptr & downtime )
{
Host : : Ptr host = service - > GetHost ( ) ;
if ( ! host )
return ;
if ( ! downtime )
return ;
std : : ostringstream msgbuf ;
msgbuf < < " SERVICE DOWNTIME ALERT: "
< < host - > GetName ( ) < < " ; "
< < service - > GetShortName ( ) < < " ; "
2013-09-18 10:08:28 +02:00
< < " STARTED " < < " ; "
< < " Service has entered a period of scheduled downtime. "
2013-09-17 19:40:23 +02:00
< < " " ;
{
ObjectLock oLock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
if ( service = = host - > GetHostCheckService ( ) ) {
std : : ostringstream msgbuf ;
msgbuf < < " HOST DOWNTIME ALERT: "
< < host - > GetName ( ) < < " ; "
2013-09-18 10:08:28 +02:00
< < " STARTED " < < " ; "
< < " Service has entered a period of scheduled downtime. "
2013-09-17 19:40:23 +02:00
< < " " ;
{
ObjectLock oLock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
}
{
ObjectLock oLock ( this ) ;
Flush ( ) ;
}
}
/**
* @ threadsafety Always .
*/
void CompatLog : : RemoveDowntimeHandler ( const Service : : Ptr & service , const Dictionary : : Ptr & downtime )
{
Host : : Ptr host = service - > GetHost ( ) ;
if ( ! host )
return ;
if ( ! downtime )
return ;
String downtime_output ;
String downtime_state_str ;
if ( downtime - > Get ( " was_cancelled " ) = = true ) {
downtime_output = " Scheduled downtime for service has been cancelled. " ;
downtime_state_str = " CANCELLED " ;
} else {
downtime_output = " Service has exited from a period of scheduled downtime. " ;
downtime_state_str = " STOPPED " ;
}
std : : ostringstream msgbuf ;
msgbuf < < " SERVICE DOWNTIME ALERT: "
< < host - > GetName ( ) < < " ; "
< < service - > GetShortName ( ) < < " ; "
< < downtime_state_str < < " ; "
< < downtime_output
< < " " ;
{
ObjectLock oLock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
if ( service = = host - > GetHostCheckService ( ) ) {
std : : ostringstream msgbuf ;
msgbuf < < " HOST DOWNTIME ALERT: "
< < host - > GetName ( ) < < " ; "
< < downtime_state_str < < " ; "
< < downtime_output
< < " " ;
{
ObjectLock oLock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
}
{
ObjectLock oLock ( this ) ;
Flush ( ) ;
}
}
2013-06-28 13:40:01 +02:00
2013-06-28 21:31:38 +02:00
/**
* @ threadsafety Always .
*/
2013-08-20 11:06:04 +02:00
void CompatLog : : NotificationSentHandler ( const Service : : Ptr & service , const User : : Ptr & user ,
2013-07-01 17:56:21 +02:00
NotificationType const & notification_type , Dictionary : : Ptr const & cr ,
const String & author , const String & comment_text )
2013-06-28 21:31:38 +02:00
{
Host : : Ptr host = service - > GetHost ( ) ;
if ( ! host )
return ;
2013-07-01 10:03:00 +02:00
CheckCommand : : Ptr commandObj = service - > GetCheckCommand ( ) ;
String check_command = " " ;
if ( commandObj )
check_command = commandObj - > GetName ( ) ;
2013-06-28 21:31:38 +02:00
String notification_type_str = Notification : : NotificationTypeToString ( notification_type ) ;
String author_comment = " " ;
if ( notification_type = = NotificationCustom | | notification_type = = NotificationAcknowledgement ) {
author_comment = " ; " + author + " ; " + comment_text ;
}
if ( ! cr )
return ;
String output ;
String raw_output ;
if ( cr ) {
raw_output = cr - > Get ( " output " ) ;
size_t line_end = raw_output . Find ( " \n " ) ;
output = raw_output . SubStr ( 0 , line_end ) ;
}
std : : ostringstream msgbuf ;
msgbuf < < " SERVICE NOTIFICATION: "
2013-08-20 11:06:04 +02:00
< < user - > GetName ( ) < < " ; "
2013-06-28 21:31:38 +02:00
< < host - > GetName ( ) < < " ; "
< < service - > GetShortName ( ) < < " ; "
< < notification_type_str < < " "
< < " ( " < < Service : : StateToString ( service - > GetState ( ) ) < < " ); "
2013-07-01 10:03:00 +02:00
< < check_command < < " ; "
2013-06-28 21:31:38 +02:00
< < raw_output < < author_comment
< < " " ;
{
ObjectLock oLock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
if ( service = = host - > GetHostCheckService ( ) ) {
std : : ostringstream msgbuf ;
msgbuf < < " HOST NOTIFICATION: "
2013-08-20 11:06:04 +02:00
< < user - > GetName ( ) < < " ; "
2013-06-28 21:31:38 +02:00
< < host - > GetName ( ) < < " ; "
< < notification_type_str < < " "
< < " ( " < < Service : : StateToString ( service - > GetState ( ) ) < < " ); "
2013-07-01 10:03:00 +02:00
< < check_command < < " ; "
2013-06-28 21:31:38 +02:00
< < raw_output < < author_comment
< < " " ;
{
ObjectLock oLock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
}
{
ObjectLock oLock ( this ) ;
Flush ( ) ;
}
}
2013-07-01 14:29:07 +02:00
/**
* @ threadsafety Always .
*/
void CompatLog : : FlappingHandler ( const Service : : Ptr & service , FlappingState flapping_state )
{
Host : : Ptr host = service - > GetHost ( ) ;
if ( ! host )
return ;
String flapping_state_str ;
String flapping_output ;
switch ( flapping_state ) {
case FlappingStarted :
2013-07-01 14:49:04 +02:00
flapping_output = " Service appears to have started flapping ( " + Convert : : ToString ( service - > GetFlappingCurrent ( ) ) + " % change >= " + Convert : : ToString ( service - > GetFlappingThreshold ( ) ) + " % threshold) " ;
2013-07-01 14:29:07 +02:00
flapping_state_str = " STARTED " ;
break ;
case FlappingStopped :
2013-07-01 14:49:04 +02:00
flapping_output = " Service appears to have stopped flapping ( " + Convert : : ToString ( service - > GetFlappingCurrent ( ) ) + " % change < " + Convert : : ToString ( service - > GetFlappingThreshold ( ) ) + " % threshold) " ;
2013-07-01 14:29:07 +02:00
flapping_state_str = " STOPPED " ;
break ;
case FlappingDisabled :
flapping_output = " Flap detection has been disabled " ;
flapping_state_str = " DISABLED " ;
break ;
default :
Log ( LogCritical , " compat " , " Unknown flapping state: " + Convert : : ToString ( flapping_state ) ) ;
return ;
}
std : : ostringstream msgbuf ;
msgbuf < < " SERVICE FLAPPING ALERT: "
< < host - > GetName ( ) < < " ; "
< < service - > GetShortName ( ) < < " ; "
< < flapping_state_str < < " ; "
< < flapping_output
< < " " ;
{
ObjectLock oLock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
if ( service = = host - > GetHostCheckService ( ) ) {
std : : ostringstream msgbuf ;
msgbuf < < " HOST FLAPPING ALERT: "
< < host - > GetName ( ) < < " ; "
< < flapping_state_str < < " ; "
< < flapping_output
< < " " ;
{
ObjectLock oLock ( this ) ;
WriteLine ( msgbuf . str ( ) ) ;
}
}
{
ObjectLock oLock ( this ) ;
Flush ( ) ;
}
}
2013-06-28 21:31:38 +02:00
2013-03-19 13:04:30 +01:00
void CompatLog : : WriteLine ( const String & line )
{
ASSERT ( OwnsLock ( ) ) ;
if ( ! m_OutputFile . good ( ) )
return ;
m_OutputFile < < " [ " < < ( long ) Utility : : GetTime ( ) < < " ] " < < line < < " \n " ;
}
void CompatLog : : Flush ( void )
{
ASSERT ( OwnsLock ( ) ) ;
if ( ! m_OutputFile . good ( ) )
return ;
m_OutputFile < < std : : flush ;
}
/**
* @ threadsafety Always .
*/
2013-03-20 11:11:46 +01:00
void CompatLog : : ReopenFile ( bool rotate )
2013-03-19 13:04:30 +01:00
{
ObjectLock olock ( this ) ;
String tempFile = GetLogDir ( ) + " /icinga.log " ;
2013-03-20 11:11:46 +01:00
if ( m_OutputFile ) {
2013-03-19 13:04:30 +01:00
m_OutputFile . close ( ) ;
2013-03-20 11:11:46 +01:00
if ( rotate ) {
String archiveFile = GetLogDir ( ) + " /archives/icinga- " + Utility : : FormatDateTime ( " %m-%d-%Y-%H " , Utility : : GetTime ( ) ) + " .log " ;
Log ( LogInformation , " compat " , " Rotating compat log file ' " + tempFile + " ' -> ' " + archiveFile + " ' " ) ;
( void ) rename ( tempFile . CStr ( ) , archiveFile . CStr ( ) ) ;
}
2013-03-19 13:04:30 +01:00
}
2013-03-20 11:11:46 +01:00
m_OutputFile . open ( tempFile . CStr ( ) , std : : ofstream : : app ) ;
2013-03-19 13:04:30 +01:00
if ( ! m_OutputFile . good ( ) ) {
Log ( LogWarning , " icinga " , " Could not open compat log file ' " + tempFile + " ' for writing. Log output will be lost. " ) ;
return ;
}
2013-03-20 11:11:46 +01:00
WriteLine ( " LOG ROTATION: " + GetRotationMethod ( ) ) ;
2013-03-19 13:04:30 +01:00
WriteLine ( " LOG VERSION: 2.0 " ) ;
2013-08-20 11:06:04 +02:00
BOOST_FOREACH ( const Host : : Ptr & host , DynamicType : : GetObjects < Host > ( ) ) {
2013-03-19 13:04:30 +01:00
Service : : Ptr hc = host - > GetHostCheckService ( ) ;
if ( ! hc )
continue ;
bool reachable = host - > IsReachable ( ) ;
ObjectLock olock ( hc ) ;
std : : ostringstream msgbuf ;
msgbuf < < " HOST STATE: CURRENT; "
< < host - > GetName ( ) < < " ; "
< < Host : : StateToString ( Host : : CalculateState ( hc - > GetState ( ) , reachable ) ) < < " ; "
< < Service : : StateTypeToString ( hc - > GetStateType ( ) ) < < " ; "
< < hc - > GetCurrentCheckAttempt ( ) < < " ; "
< < " " ;
WriteLine ( msgbuf . str ( ) ) ;
}
2013-08-20 11:06:04 +02:00
BOOST_FOREACH ( const Service : : Ptr & service , DynamicType : : GetObjects < Service > ( ) ) {
2013-03-19 13:04:30 +01:00
Host : : Ptr host = service - > GetHost ( ) ;
if ( ! host )
continue ;
std : : ostringstream msgbuf ;
msgbuf < < " SERVICE STATE: CURRENT; "
< < host - > GetName ( ) < < " ; "
< < service - > GetShortName ( ) < < " ; "
< < Service : : StateToString ( service - > GetState ( ) ) < < " ; "
< < Service : : StateTypeToString ( service - > GetStateType ( ) ) < < " ; "
< < service - > GetCurrentCheckAttempt ( ) < < " ; "
< < " " ;
WriteLine ( msgbuf . str ( ) ) ;
}
Flush ( ) ;
}
2013-03-20 11:11:46 +01:00
void CompatLog : : ScheduleNextRotation ( void )
{
time_t now = ( time_t ) Utility : : GetTime ( ) ;
String method = GetRotationMethod ( ) ;
tm tmthen ;
# ifdef _MSC_VER
tm * temp = localtime ( & now ) ;
if ( temp = = NULL ) {
BOOST_THROW_EXCEPTION ( posix_error ( )
< < boost : : errinfo_api_function ( " localtime " )
< < boost : : errinfo_errno ( errno ) ) ;
}
tmthen = * temp ;
# else /* _MSC_VER */
if ( localtime_r ( & now , & tmthen ) = = NULL ) {
BOOST_THROW_EXCEPTION ( posix_error ( )
< < boost : : errinfo_api_function ( " localtime_r " )
< < boost : : errinfo_errno ( errno ) ) ;
}
# endif /* _MSC_VER */
tmthen . tm_min = 0 ;
tmthen . tm_sec = 0 ;
if ( method = = " HOURLY " ) {
tmthen . tm_hour + + ;
} else if ( method = = " DAILY " ) {
tmthen . tm_mday + + ;
tmthen . tm_hour = 0 ;
} else if ( method = = " WEEKLY " ) {
tmthen . tm_mday + = 7 - tmthen . tm_wday ;
tmthen . tm_hour = 0 ;
} else if ( method = = " MONTHLY " ) {
tmthen . tm_mon + + ;
tmthen . tm_mday = 1 ;
tmthen . tm_hour = 0 ;
}
time_t ts = mktime ( & tmthen ) ;
Log ( LogInformation , " compat " , " Rescheduling rotation timer for compat log ' "
+ GetName ( ) + " ' to ' " + Utility : : FormatDateTime ( " %Y/%m/%d %H:%M:%S %z " , ts ) + " ' " ) ;
m_RotationTimer - > Reschedule ( ts ) ;
}
2013-03-19 13:04:30 +01:00
/**
* @ threadsafety Always .
*/
void CompatLog : : RotationTimerHandler ( void )
{
2013-03-20 11:11:46 +01:00
try {
ReopenFile ( true ) ;
} catch ( . . . ) {
ScheduleNextRotation ( ) ;
throw ;
}
ScheduleNextRotation ( ) ;
}
2013-03-25 20:47:02 +01:00
void CompatLog : : ValidateRotationMethod ( const String & location , const Dictionary : : Ptr & attrs )
2013-03-20 11:11:46 +01:00
{
Value rotation_method = attrs - > Get ( " rotation_method " ) ;
if ( ! rotation_method . IsEmpty ( ) & & rotation_method ! = " HOURLY " & & rotation_method ! = " DAILY " & &
rotation_method ! = " WEEKLY " & & rotation_method ! = " MONTHLY " & & rotation_method ! = " NONE " ) {
2013-08-20 11:06:04 +02:00
ConfigCompilerContext : : GetInstance ( ) - > AddError ( false , " Validation failed for " +
2013-03-20 11:11:46 +01:00
location + " : Rotation method ' " + rotation_method + " ' is invalid. " ) ;
}
2013-03-19 13:04:30 +01:00
}
2013-08-20 11:06:04 +02:00
void CompatLog : : InternalSerialize ( const Dictionary : : Ptr & bag , int attributeTypes ) const
{
DynamicObject : : InternalSerialize ( bag , attributeTypes ) ;
if ( attributeTypes & Attribute_Config ) {
bag - > Set ( " log_dir " , m_LogDir ) ;
bag - > Set ( " rotation_method " , m_RotationMethod ) ;
}
}
void CompatLog : : InternalDeserialize ( const Dictionary : : Ptr & bag , int attributeTypes )
{
DynamicObject : : InternalDeserialize ( bag , attributeTypes ) ;
if ( attributeTypes & Attribute_Config ) {
m_LogDir = bag - > Get ( " log_dir " ) ;
m_RotationMethod = bag - > Get ( " rotation_method " ) ;
}
}