1 /******************************************************************************
3 * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
20 #include "compat/compatlogger.h"
21 #include "icinga/service.h"
22 #include "icinga/checkcommand.h"
23 #include "icinga/eventcommand.h"
24 #include "icinga/notification.h"
25 #include "icinga/macroprocessor.h"
26 #include "icinga/externalcommandprocessor.h"
27 #include "icinga/compatutility.h"
28 #include "config/configcompilercontext.h"
29 #include "base/dynamictype.h"
30 #include "base/objectlock.h"
31 #include "base/logger_fwd.h"
32 #include "base/exception.h"
33 #include "base/convert.h"
34 #include "base/application.h"
35 #include "base/utility.h"
36 #include "base/scriptfunction.h"
37 #include "base/statsfunction.h"
38 #include <boost/foreach.hpp>
39 #include <boost/algorithm/string.hpp>
41 using namespace icinga;
43 REGISTER_TYPE(CompatLogger);
44 REGISTER_SCRIPTFUNCTION(ValidateRotationMethod, &CompatLogger::ValidateRotationMethod);
46 REGISTER_STATSFUNCTION(CompatLoggerStats, &CompatLogger::StatsFunc);
48 Value CompatLogger::StatsFunc(Dictionary::Ptr& status, Dictionary::Ptr& perfdata)
50 Dictionary::Ptr nodes = make_shared<Dictionary>();
52 BOOST_FOREACH(const CompatLogger::Ptr& compat_logger, DynamicType::GetObjects<CompatLogger>()) {
53 nodes->Set(compat_logger->GetName(), 1); //add more stats
56 status->Set("compatlogger", nodes);
62 * @threadsafety Always.
64 void CompatLogger::Start(void)
66 DynamicObject::Start();
68 Checkable::OnNewCheckResult.connect(bind(&CompatLogger::CheckResultHandler, this, _1, _2));
69 Checkable::OnNotificationSentToUser.connect(bind(&CompatLogger::NotificationSentHandler, this, _1, _2, _3, _4, _5, _6, _7, _8));
70 Checkable::OnFlappingChanged.connect(bind(&CompatLogger::FlappingHandler, this, _1, _2));
71 Checkable::OnDowntimeTriggered.connect(boost::bind(&CompatLogger::TriggerDowntimeHandler, this, _1, _2));
72 Checkable::OnDowntimeRemoved.connect(boost::bind(&CompatLogger::RemoveDowntimeHandler, this, _1, _2));
73 Checkable::OnEventCommandExecuted.connect(bind(&CompatLogger::EventCommandHandler, this, _1));
74 ExternalCommandProcessor::OnNewExternalCommand.connect(boost::bind(&CompatLogger::ExternalCommandHandler, this, _2, _3));
76 m_RotationTimer = make_shared<Timer>();
77 m_RotationTimer->OnTimerExpired.connect(boost::bind(&CompatLogger::RotationTimerHandler, this));
78 m_RotationTimer->Start();
81 ScheduleNextRotation();
85 * @threadsafety Always.
87 void CompatLogger::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr)
91 tie(host, service) = GetHostService(checkable);
93 Dictionary::Ptr vars_after = cr->GetVarsAfter();
95 long state_after = vars_after->Get("state");
96 long stateType_after = vars_after->Get("state_type");
97 long attempt_after = vars_after->Get("attempt");
98 bool reachable_after = vars_after->Get("reachable");
99 bool host_reachable_after = vars_after->Get("host_reachable");
101 Dictionary::Ptr vars_before = cr->GetVarsBefore();
104 long state_before = vars_before->Get("state");
105 long stateType_before = vars_before->Get("state_type");
106 long attempt_before = vars_before->Get("attempt");
107 bool reachable_before = vars_before->Get("reachable");
109 if (state_before == state_after && stateType_before == stateType_after &&
110 attempt_before == attempt_after && reachable_before == reachable_after)
111 return; /* Nothing changed, ignore this checkresult. */
116 output = CompatUtility::GetCheckResultOutput(cr);
118 std::ostringstream msgbuf;
121 msgbuf << "SERVICE ALERT: "
122 << host->GetName() << ";"
123 << service->GetShortName() << ";"
124 << Service::StateToString(static_cast<ServiceState>(state_after)) << ";"
125 << Service::StateTypeToString(static_cast<StateType>(stateType_after)) << ";"
126 << attempt_after << ";"
130 String state = Host::StateToString(Host::CalculateState(static_cast<ServiceState>(state_after)));
132 if (!reachable_after)
133 state = "UNREACHABLE";
135 msgbuf << "HOST ALERT: "
136 << host->GetName() << ";"
138 << Host::StateTypeToString(static_cast<StateType>(stateType_after)) << ";"
139 << attempt_after << ";"
146 ObjectLock olock(this);
147 WriteLine(msgbuf.str());
153 * @threadsafety Always.
155 void CompatLogger::TriggerDowntimeHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
158 Service::Ptr service;
159 tie(host, service) = GetHostService(checkable);
164 std::ostringstream msgbuf;
167 msgbuf << "SERVICE DOWNTIME ALERT: "
168 << host->GetName() << ";"
169 << service->GetShortName() << ";"
171 << "Checkable has entered a period of scheduled downtime."
174 msgbuf << "HOST DOWNTIME ALERT: "
175 << host->GetName() << ";"
177 << "Checkable has entered a period of scheduled downtime."
182 ObjectLock oLock(this);
183 WriteLine(msgbuf.str());
189 * @threadsafety Always.
191 void CompatLogger::RemoveDowntimeHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
194 Service::Ptr service;
195 tie(host, service) = GetHostService(checkable);
200 String downtime_output;
201 String downtime_state_str;
203 if (downtime->GetWasCancelled()) {
204 downtime_output = "Scheduled downtime for service has been cancelled.";
205 downtime_state_str = "CANCELLED";
207 downtime_output = "Checkable has exited from a period of scheduled downtime.";
208 downtime_state_str = "STOPPED";
211 std::ostringstream msgbuf;
214 msgbuf << "SERVICE DOWNTIME ALERT: "
215 << host->GetName() << ";"
216 << service->GetShortName() << ";"
217 << downtime_state_str << "; "
221 msgbuf << "HOST DOWNTIME ALERT: "
222 << host->GetName() << ";"
223 << downtime_state_str << "; "
229 ObjectLock oLock(this);
230 WriteLine(msgbuf.str());
236 * @threadsafety Always.
238 void CompatLogger::NotificationSentHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable,
239 const User::Ptr& user, NotificationType const& notification_type, CheckResult::Ptr const& cr,
240 const String& author, const String& comment_text, const String& command_name)
243 Service::Ptr service;
244 tie(host, service) = GetHostService(checkable);
246 String notification_type_str = Notification::NotificationTypeToString(notification_type);
248 /* override problem notifications with their current state string */
249 if (notification_type == NotificationProblem) {
251 notification_type_str = Service::StateToString(service->GetState());
253 notification_type_str = host->IsReachable() ? Host::StateToString(host->GetState()) : "UNREACHABLE";
256 String author_comment = "";
257 if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) {
258 author_comment = author + ";" + comment_text;
266 output = CompatUtility::GetCheckResultOutput(cr);
268 std::ostringstream msgbuf;
271 msgbuf << "SERVICE NOTIFICATION: "
272 << user->GetName() << ";"
273 << host->GetName() << ";"
274 << service->GetShortName() << ";"
275 << notification_type_str << ";"
276 << command_name << ";"
281 msgbuf << "HOST NOTIFICATION: "
282 << user->GetName() << ";"
283 << host->GetName() << ";"
284 << notification_type_str << " "
285 << "(" << (host->IsReachable() ? Host::StateToString(host->GetState()) : "UNREACHABLE") << ");"
286 << command_name << ";"
293 ObjectLock oLock(this);
294 WriteLine(msgbuf.str());
300 * @threadsafety Always.
302 void CompatLogger::FlappingHandler(const Checkable::Ptr& checkable, FlappingState flapping_state)
305 Service::Ptr service;
306 tie(host, service) = GetHostService(checkable);
308 String flapping_state_str;
309 String flapping_output;
311 switch (flapping_state) {
312 case FlappingStarted:
313 flapping_output = "Checkable appears to have started flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change >= " + Convert::ToString(checkable->GetFlappingThreshold()) + "% threshold)";
314 flapping_state_str = "STARTED";
316 case FlappingStopped:
317 flapping_output = "Checkable appears to have stopped flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change < " + Convert::ToString(checkable->GetFlappingThreshold()) + "% threshold)";
318 flapping_state_str = "STOPPED";
320 case FlappingDisabled:
321 flapping_output = "Flap detection has been disabled";
322 flapping_state_str = "DISABLED";
325 Log(LogCritical, "compat", "Unknown flapping state: " + Convert::ToString(flapping_state));
329 std::ostringstream msgbuf;
332 msgbuf << "SERVICE FLAPPING ALERT: "
333 << host->GetName() << ";"
334 << service->GetShortName() << ";"
335 << flapping_state_str << "; "
339 msgbuf << "HOST FLAPPING ALERT: "
340 << host->GetName() << ";"
341 << flapping_state_str << "; "
347 ObjectLock oLock(this);
348 WriteLine(msgbuf.str());
353 void CompatLogger::ExternalCommandHandler(const String& command, const std::vector<String>& arguments)
355 std::ostringstream msgbuf;
356 msgbuf << "EXTERNAL COMMAND: "
358 << boost::algorithm::join(arguments, ";")
362 ObjectLock oLock(this);
363 WriteLine(msgbuf.str());
367 void CompatLogger::EventCommandHandler(const Checkable::Ptr& checkable)
370 Service::Ptr service;
371 tie(host, service) = GetHostService(checkable);
373 EventCommand::Ptr event_command = checkable->GetEventCommand();
374 String event_command_name = event_command->GetName();
375 long current_attempt = checkable->GetCheckAttempt();
377 std::ostringstream msgbuf;
380 msgbuf << "SERVICE EVENT HANDLER: "
381 << host->GetName() << ";"
382 << service->GetShortName() << ";"
383 << Service::StateToString(service->GetState()) << ";"
384 << Service::StateTypeToString(service->GetStateType()) << ";"
385 << current_attempt << ";"
386 << event_command_name;
388 msgbuf << "HOST EVENT HANDLER: "
389 << host->GetName() << ";"
390 << (host->IsReachable() ? Host::StateToString(host->GetState()) : "UNREACHABLE") << ";"
391 << Host::StateTypeToString(host->GetStateType()) << ";"
392 << current_attempt << ";"
393 << event_command_name;
397 ObjectLock oLock(this);
398 WriteLine(msgbuf.str());
403 void CompatLogger::WriteLine(const String& line)
407 if (!m_OutputFile.good())
410 m_OutputFile << "[" << (long)Utility::GetTime() << "] " << line << "\n";
413 void CompatLogger::Flush(void)
417 if (!m_OutputFile.good())
420 m_OutputFile << std::flush;
424 * @threadsafety Always.
426 void CompatLogger::ReopenFile(bool rotate)
428 ObjectLock olock(this);
430 String tempFile = GetLogDir() + "/icinga.log";
433 m_OutputFile.close();
436 String archiveFile = GetLogDir() + "/archives/icinga-" + Utility::FormatDateTime("%m-%d-%Y-%H", Utility::GetTime()) + ".log";
438 Log(LogInformation, "compat", "Rotating compat log file '" + tempFile + "' -> '" + archiveFile + "'");
439 (void) rename(tempFile.CStr(), archiveFile.CStr());
443 m_OutputFile.open(tempFile.CStr(), std::ofstream::app);
445 if (!m_OutputFile.good()) {
446 Log(LogWarning, "icinga", "Could not open compat log file '" + tempFile + "' for writing. Log output will be lost.");
451 WriteLine("LOG ROTATION: " + GetRotationMethod());
452 WriteLine("LOG VERSION: 2.0");
454 BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
456 CheckResult::Ptr cr = host->GetLastCheckResult();
459 output = CompatUtility::GetCheckResultOutput(cr);
461 std::ostringstream msgbuf;
462 msgbuf << "CURRENT HOST STATE: "
463 << host->GetName() << ";"
464 << (host->IsReachable() ? Host::StateToString(host->GetState()) : "UNREACHABLE") << ";"
465 << Host::StateTypeToString(host->GetStateType()) << ";"
466 << host->GetCheckAttempt() << ";"
469 WriteLine(msgbuf.str());
472 BOOST_FOREACH(const Service::Ptr& service, DynamicType::GetObjects<Service>()) {
473 Host::Ptr host = service->GetHost();
476 CheckResult::Ptr cr = service->GetLastCheckResult();
479 output = CompatUtility::GetCheckResultOutput(cr);
481 std::ostringstream msgbuf;
482 msgbuf << "CURRENT SERVICE STATE: "
483 << host->GetName() << ";"
484 << service->GetShortName() << ";"
485 << Service::StateToString(service->GetState()) << ";"
486 << Service::StateTypeToString(service->GetStateType()) << ";"
487 << service->GetCheckAttempt() << ";"
490 WriteLine(msgbuf.str());
496 void CompatLogger::ScheduleNextRotation(void)
498 time_t now = (time_t)Utility::GetTime();
499 String method = GetRotationMethod();
504 tm *temp = localtime(&now);
507 BOOST_THROW_EXCEPTION(posix_error()
508 << boost::errinfo_api_function("localtime")
509 << boost::errinfo_errno(errno));
514 if (localtime_r(&now, &tmthen) == NULL) {
515 BOOST_THROW_EXCEPTION(posix_error()
516 << boost::errinfo_api_function("localtime_r")
517 << boost::errinfo_errno(errno));
519 #endif /* _MSC_VER */
524 if (method == "HOURLY") {
526 } else if (method == "DAILY") {
529 } else if (method == "WEEKLY") {
530 tmthen.tm_mday += 7 - tmthen.tm_wday;
532 } else if (method == "MONTHLY") {
538 time_t ts = mktime(&tmthen);
540 Log(LogInformation, "compat", "Rescheduling rotation timer for compat log '"
541 + GetName() + "' to '" + Utility::FormatDateTime("%Y/%m/%d %H:%M:%S %z", ts) + "'");
542 m_RotationTimer->Reschedule(ts);
546 * @threadsafety Always.
548 void CompatLogger::RotationTimerHandler(void)
553 ScheduleNextRotation();
558 ScheduleNextRotation();
561 void CompatLogger::ValidateRotationMethod(const String& location, const Dictionary::Ptr& attrs)
563 Value rotation_method = attrs->Get("rotation_method");
565 if (!rotation_method.IsEmpty() && rotation_method != "HOURLY" && rotation_method != "DAILY" &&
566 rotation_method != "WEEKLY" && rotation_method != "MONTHLY" && rotation_method != "NONE") {
567 ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
568 location + ": Rotation method '" + rotation_method + "' is invalid.");